diff --git a/apps/action.c b/apps/action.c index aa21a61222..c7dd74f60a 100644 --- a/apps/action.c +++ b/apps/action.c @@ -28,6 +28,7 @@ #if !defined(BOOTLOADER) #include "language.h" +#include "skin_engine/skin_engine.h" #endif #include "appevents.h" @@ -937,19 +938,20 @@ static inline void do_softlock(action_last_t *last, action_cur_t *cur) if (notify_user) { +#ifndef BOOTLOADER + skin_request_update_locked(last->keys_locked); +#endif action_handle_backlight(true, false); -#ifdef HAVE_BACKLIGHT - /* If we don't wait for a moment for the backlight queue to process, - * the user will never see the message - */ - if (!is_backlight_on(false)) - { - sleep(HZ/2); - } -#endif if (!has_flag(last->softlock_mask, SEL_ACTION_ALLNONOTIFY)) { +#ifdef HAVE_BACKLIGHT + /* If we don't wait for a moment for the backlight queue to process, + * the user will never see the message + */ + if (!is_backlight_on(false)) + sleep(HZ/2); +#endif if (last->keys_locked) { splash(HZ/2, ID2P(LANG_KEYLOCK_ON)); @@ -1440,6 +1442,9 @@ bool is_keys_locked(void) void set_selective_softlock_actions(bool selective, unsigned int mask) { action_last.keys_locked = false; +#if defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN) + button_enable_touch(true); +#endif if (selective) { action_last.softlock_mask = mask | SEL_ACTION_ENABLED; diff --git a/apps/bitmaps/bitmaps.make b/apps/bitmaps/bitmaps.make index d39531a8b8..cf166f3f8c 100644 --- a/apps/bitmaps/bitmaps.make +++ b/apps/bitmaps/bitmaps.make @@ -50,3 +50,11 @@ $(BUILDDIR)/apps/bitmaps/remote_mono/%.c: $(ROOTDIR)/apps/bitmaps/remote_mono/%. $(BUILDDIR)/apps/bitmaps/remote_native/%.c: $(ROOTDIR)/apps/bitmaps/remote_native/%.bmp $(TOOLSDIR)/bmp2rb $(SILENT)mkdir -p $(dir $@) $(BMPINCDIR) $(call PRINTS,BMP2RB $( $@ + +ifdef APP_TYPE +# Bitmaps must be explicitly Position independent to avoid linker warnings +$(BUILDDIR)/apps/bitmaps/native/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/bitmaps/mono/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/bitmaps/remote_mono/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/bitmaps/remote_native/%.o: CFLAGS += -fPIC +endif diff --git a/apps/bookmark.c b/apps/bookmark.c index f00ace7c41..d7fe0b29b1 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -43,6 +43,7 @@ #include "file.h" #include "pathfuncs.h" #include "playlist_menu.h" +#include "iap-usb.h" /*#define LOGF_ENABLE*/ #include "logf.h" @@ -1125,7 +1126,9 @@ static bool play_bookmark(const char* bookmark) if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true)) { global_settings.repeat_mode = resume_info.repeat_mode; + iap_on_repeat_state(global_settings.repeat_mode); global_settings.playlist_shuffle = resume_info.shuffle; + iap_on_shuffle_state(global_settings.playlist_shuffle); #if defined(HAVE_PITCHCONTROL) sound_set_pitch(resume_info.pitch); dsp_set_timestretch(resume_info.speed); diff --git a/apps/codecs.c b/apps/codecs.c index 7bd2de367e..c304b6eaf2 100644 --- a/apps/codecs.c +++ b/apps/codecs.c @@ -50,6 +50,7 @@ #include "splash.h" #include "general.h" #include "rbpaths.h" +#include "panic.h" #define LOGF_ENABLE #include "logf.h" @@ -150,6 +151,7 @@ struct codec_api ci = { /* new stuff at the end, sort into place next time the API gets incompatible */ + panicf, }; diff --git a/apps/cuesheet.c b/apps/cuesheet.c index c5a1aacad9..9779f40c5d 100644 --- a/apps/cuesheet.c +++ b/apps/cuesheet.c @@ -46,7 +46,8 @@ static bool search_for_cuesheet(const char *path, struct cuesheet_file *cue_file { size_t len; char cuepath[MAX_PATH]; - char *dot, *slash, *slash_cuepath; + char *dot, *slash_cuepath; + const char *slash; cue_file->pos = 0; cue_file->size = 0; @@ -281,7 +282,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) } s = skip_whitespace(line); -/* RECOGNIZED TAGS *********************** +/* RECOGNIZED TAGS *********************** * eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE, * eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE, */ @@ -306,7 +307,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) cue->tracks[cue->track_count-1].offset = parse_cue_index(s); #endif } - else if (option != eCS_NOTFOUND) + else if (option != eCS_NOTFOUND) { char *dest = NULL; char *string = get_string(s); diff --git a/apps/features.txt b/apps/features.txt index c9dea50abd..2d3ad26a73 100644 --- a/apps/features.txt +++ b/apps/features.txt @@ -316,3 +316,7 @@ lto #if defined(USB_ENABLE_AUDIO) usbdac #endif + +#if defined(UTF8PROC_EXPORTS) +utf8proc +#endif diff --git a/apps/filetypes.c b/apps/filetypes.c index 88e6a17666..0e16b2c24b 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -525,7 +525,7 @@ static void read_config_init(int fd) static int file_find_extension(const char* file) { - char *extension = strrchr(file, '.'); + const char *extension = strrchr(file, '.'); if (extension) extension++; return find_extension(extension); @@ -704,7 +704,7 @@ int filetype_load_plugin(const char* plugin, const char* file) { int i; char plugin_name[MAX_PATH]; - char *s; + const char *s; for (i=1;iset_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); current_drawing_line = list->selected_item; return true; } diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index 1d4942839a..34ae79e316 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -446,8 +446,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) callback_draw_item(&list_info); } - display->set_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); display->set_viewport(last_vp); } diff --git a/apps/gui/list.c b/apps/gui/list.c index 572a7757cc..f5f02f2cb8 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -49,6 +49,8 @@ void list_draw(struct screen *display, struct gui_synclist *list); static long last_dirty_tick; +static bool sb_title_is_dirty; +static bool theme_enabled; static struct viewport parent[NB_SCREENS]; static struct gui_synclist *current_lists; @@ -57,17 +59,17 @@ static bool list_is_dirty(struct gui_synclist *list) return TIME_BEFORE(list->dirty_tick, last_dirty_tick); } -static void list_force_reinit(unsigned short id, void *param, void *last_dirty_tick) +static void list_force_reinit(unsigned short id, void *param) { (void)id; (void)param; - *(int *)last_dirty_tick = current_tick; + last_dirty_tick = current_tick; } void list_init(void) { last_dirty_tick = current_tick; - add_event_ex(GUI_EVENT_THEME_CHANGED, false, list_force_reinit, &last_dirty_tick); + add_event(GUI_EVENT_THEME_CHANGED, list_force_reinit); } static void list_init_viewports(struct gui_synclist *list) @@ -81,7 +83,7 @@ static void list_init_viewports(struct gui_synclist *list) gui_synclist_set_viewport_defaults(list->parent[i], i); } } - list->dirty_tick = current_tick; + list->dirty_tick = last_dirty_tick; } static int list_nb_lines(struct gui_synclist *list, enum screen_type screen) @@ -185,7 +187,6 @@ void gui_synclist_init(struct gui_synclist * gui_list, gui_list->title_icon = Icon_NOICON; gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0; - gui_list->dirty_tick = current_tick; #ifdef HAVE_LCD_COLOR gui_list->title_color = -1; @@ -220,11 +221,32 @@ int gui_list_get_item_offset(struct gui_synclist * gui_list, return item_offset; } +static void sb_title_cb(unsigned short id, void *data, void *userdata) +{ + (void)id; + (void)data; + theme_enabled = true; + gui_synclist_draw((struct gui_synclist *) userdata); +} + /* * Force a full screen update. */ void gui_synclist_draw(struct gui_synclist *gui_list) { + if (sb_title_is_dirty) + { + sb_title_is_dirty = theme_enabled = false; + + /* tell skin engine to refresh, then call us back */ + add_event_ex(GUI_EVENT_NEED_UI_UPDATE, true, sb_title_cb, gui_list); + send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); + remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, sb_title_cb, gui_list); + + /* sb_title_cb was only called if theme is enabled */ + if (theme_enabled) + return; + } if (list_is_dirty(gui_list)) { list_init_viewports(gui_list); @@ -439,7 +461,7 @@ void gui_synclist_set_title(struct gui_synclist * gui_list, gui_list->title_icon = icon; FOR_NB_SCREENS(i) sb_set_title_text(title, icon, i); - send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); + sb_title_is_dirty = true; } void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items) diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c index b76a4fd386..b0d74d43a5 100644 --- a/apps/gui/quickscreen.c +++ b/apps/gui/quickscreen.c @@ -50,33 +50,25 @@ #define MARGIN 10 #define CENTER_ICONAREA_SIZE (MARGIN+8*2) -struct gui_quickscreen +struct quickscreen { const struct settings_list *items[QUICKSCREEN_ITEM_COUNT]; + struct viewport parent[NB_SCREENS]; + struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT]; + struct viewport vp_icons[NB_SCREENS]; + int button_enter; + enum quickscreen_return result; }; -static bool redraw; - -static void quickscreen_update_callback(unsigned short id, - void *data, void *userdata) -{ - (void)id; - (void)data; - (void)userdata; - - redraw = true; -} - -static void quickscreen_fix_viewports(struct gui_quickscreen *qs, - struct screen *display, - struct viewport *parent, - struct viewport - vps[QUICKSCREEN_ITEM_COUNT], - struct viewport *vp_icons) +static void quickscreen_fix_viewports(struct quickscreen *qs, enum screen_type screen) { int char_height, width, pad = 0; int left_width = 0, right_width = 0, vert_lines; unsigned char *s; + struct screen *display = &screens[screen]; + struct viewport *parent = &qs->parent[screen]; + struct viewport *vps = qs->vps[screen]; + struct viewport *vp_icons = &qs->vp_icons[screen]; int nb_lines = viewport_get_nb_lines(parent); /* nb_lines only returns the number of fully visible lines, small screens @@ -181,16 +173,15 @@ static void quickscreen_fix_viewports(struct gui_quickscreen *qs, vps[QUICKSCREEN_RIGHT].flags |= VP_FLAG_ALIGN_RIGHT; } -static void gui_quickscreen_draw(const struct gui_quickscreen *qs, - struct screen *display, - struct viewport *parent, - struct viewport vps[QUICKSCREEN_ITEM_COUNT], - struct viewport *vp_icons) +static void quickscreen_draw(struct quickscreen *qs, enum screen_type screen) { - int i; + int temp, i; char buf[MAX_PATH]; unsigned const char *title, *value; - int temp; + struct screen *display = &screens[screen]; + struct viewport *parent = &qs->parent[screen]; + struct viewport *vps = qs->vps[screen]; + struct viewport *vp_icons = &qs->vp_icons[screen]; struct viewport *last_vp = display->set_viewport(parent); display->clear_viewport(); @@ -204,12 +195,12 @@ static void gui_quickscreen_draw(const struct gui_quickscreen *qs, title = P2STR(ID2P(qs->items[i]->lang_id)); temp = option_value_as_int(qs->items[i]); value = option_get_valuestring(qs->items[i], - buf, MAX_PATH, temp); + buf, sizeof buf, temp); if (viewport_get_nb_lines(vp) < 2) { char text[MAX_PATH]; - snprintf(text, MAX_PATH, "%s: %s", title, value); + snprintf(text, sizeof text, "%s: %s", title, value); display->puts_scroll(0, 0, text); } else @@ -242,11 +233,19 @@ static void gui_quickscreen_draw(const struct gui_quickscreen *qs, (vp_icons->width/2) - 4, vp_icons->height - 8, 7, 8); } - display->set_viewport(parent); - display->update_viewport(); + skin_render_deferred(display, parent); display->set_viewport(last_vp); } +static void quickscreen_draw_cb(unsigned short id, void *data, void *userdata) +{ + (void)id; + (void)data; + + FOR_NB_SCREENS(i) + quickscreen_draw((struct quickscreen *) userdata, i); +} + static void talk_qs_option(const struct settings_list *opt, bool enqueue) { if (!global_settings.talk_menu || !opt) @@ -263,7 +262,7 @@ static void talk_qs_option(const struct settings_list *opt, bool enqueue) * - button : the key we are going to analyse * returns : true if the button corresponded to an action, false otherwise */ -static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button) +static bool quickscreen_do_button(struct quickscreen * qs, int button) { int item; bool previous = false; @@ -343,13 +342,27 @@ static int quickscreen_touchscreen_button(void) } #endif -static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb) +static void cleanup(void *parameter) +{ + struct quickscreen *qs = (struct quickscreen *) parameter; + remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, quickscreen_draw_cb, qs); + + FOR_NB_SCREENS(i) + { + for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++) + screens[i].scroll_stop_viewport(&qs->vps[i][j]); + viewportmanager_theme_undo(i, !(qs->result & QUICKSCREEN_GOTO_SHORTCUTS_MENU)); + } + /* Eliminate flashing of parent during transition to Shortcuts */ + if (qs->result & QUICKSCREEN_GOTO_SHORTCUTS_MENU) + pop_current_activity_without_refresh(); + else + pop_current_activity(); +} + +static void quickscreen_run(struct quickscreen * qs) { int button; - struct viewport parent[NB_SCREENS]; - struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT]; - struct viewport vp_icons[NB_SCREENS]; - int ret = QUICKSCREEN_OK; /* To quit we need either : * - a second press on the button that made us enter * - an action taken while pressing the enter button, @@ -358,17 +371,14 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter push_current_activity(ACTIVITY_QUICKSCREEN); - add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, quickscreen_update_callback, NULL); - FOR_NB_SCREENS(i) { screens[i].set_viewport(NULL); screens[i].scroll_stop(); - viewportmanager_theme_enable(i, true, &parent[i]); - quickscreen_fix_viewports(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]); - gui_quickscreen_draw(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]); + viewportmanager_theme_enable(i, true, &qs->parent[i]); + quickscreen_fix_viewports(qs, i); + quickscreen_draw(qs, i); } - *usb = false; /* Announce current selection on entering this screen. This is all queued up, but can be interrupted as soon as a setting is changed. */ @@ -383,31 +393,28 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter #ifdef HAVE_TOUCHSCREEN action_gesture_reset(); #endif - while (true) { - if (redraw) - { - redraw = false; - FOR_NB_SCREENS(i) - gui_quickscreen_draw(qs, &screens[i], &parent[i], - vps[i], &vp_icons[i]); - } + add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, quickscreen_draw_cb, qs); + while (true) + { button = get_action(CONTEXT_QUICKSCREEN, HZ/5); #ifdef HAVE_TOUCHSCREEN if (button == ACTION_TOUCHSCREEN) button = quickscreen_touchscreen_button(); #endif - if (default_event_handler(button) == SYS_USB_CONNECTED) + if (default_event_handler_ex(button, cleanup, qs) + == SYS_USB_CONNECTED) { - *usb = true; - break; + qs->result |= QUICKSCREEN_IN_USB; + return; } - if (gui_quickscreen_do_button(qs, button)) + if (quickscreen_do_button(qs, button)) { - ret |= QUICKSCREEN_CHANGED; + qs->result |= QUICKSCREEN_CHANGED; can_quit = true; - redraw = true; + FOR_NB_SCREENS(i) + quickscreen_draw(qs, i); } - else if (button == button_enter) + else if (button == qs->button_enter) can_quit = true; else if (button == ACTION_QS_VOLUP) { adjust_volume(1); @@ -421,10 +428,10 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter } else if (button == ACTION_STD_CONTEXT) { - ret |= QUICKSCREEN_GOTO_SHORTCUTS_MENU; + qs->result |= QUICKSCREEN_GOTO_SHORTCUTS_MENU; break; } - if ((button == button_enter) && can_quit) + if ((button == qs->button_enter) && can_quit) break; if (button == ACTION_STD_CANCEL) @@ -432,27 +439,14 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter } /* Notify that we're exiting this screen */ cond_talk_ids_fq(VOICE_OK); - FOR_NB_SCREENS(i) - { /* stop scrolling before exiting */ - for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++) - screens[i].scroll_stop_viewport(&vps[i][j]); - viewportmanager_theme_undo(i, !(ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU)); - } - - if (ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* Eliminate flashing of parent during */ - pop_current_activity_without_refresh(); /* transition to Shortcuts */ - else - pop_current_activity(); - - remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, quickscreen_update_callback, NULL); - - return ret; + cleanup(qs); } int quick_screen_quick(int button_enter) { - struct gui_quickscreen qs; - bool usb = false; + struct quickscreen qs; + qs.button_enter = button_enter; + qs.result = QUICKSCREEN_OK; for (int i = 0; i < 4; ++i) { @@ -462,13 +456,12 @@ int quick_screen_quick(int button_enter) qs.items[i] = NULL; } - int ret = gui_syncquickscreen_run(&qs, button_enter, &usb); - if (ret & QUICKSCREEN_CHANGED) + quickscreen_run(&qs); + + if (qs.result & QUICKSCREEN_CHANGED) settings_save(); - if (usb) - return QUICKSCREEN_IN_USB; - return ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU ? QUICKSCREEN_GOTO_SHORTCUTS_MENU : - QUICKSCREEN_OK; + + return qs.result & ~QUICKSCREEN_CHANGED; } /* stuff to make the quickscreen configurable */ diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c index bc2f599b4e..5528385f80 100644 --- a/apps/gui/skin_engine/skin_engine.c +++ b/apps/gui/skin_engine/skin_engine.c @@ -180,7 +180,7 @@ void settings_apply_skins(void) audio_stop(); bool first_run = skin_backdrop_init(); - + if (!first_run) { /* Make sure all skins unloaded */ @@ -342,6 +342,23 @@ void skin_request_full_update(enum skinnable_screens skin) skins[skin][i].needs_full_update = true; } + +/* Request skin update for lock state change */ +void skin_request_update_locked(bool locked) +{ + if (get_current_activity() == ACTIVITY_WPS) + return; + + sb_skin_force_next_update(); + + /* fix themes that draw on top of the UI viewport when locked */ + if (!locked) + skin_request_full_update(CUSTOM_STATUSBAR); +#ifdef HAS_BUTTON_HOLD + button_queue_post(BUTTON_NONE, 0); +#endif +} + bool dbg_skin_engine(void) { struct simplelist_info info; diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h index ce9527ab12..f753dc8087 100644 --- a/apps/gui/skin_engine/skin_engine.h +++ b/apps/gui/skin_engine/skin_engine.h @@ -51,6 +51,11 @@ void skin_disarm_touchregions(struct gui_wps *gwps); void skin_update(enum skinnable_screens skin, enum screen_type screen, unsigned int update_type); +/* Defer updates in skin_render */ +void skin_defer_rendering(bool deferred); +/* Render viewport together with deferred updates */ +void skin_render_deferred(struct screen *display, struct viewport *vp); + bool skin_has_sbs(struct gui_wps *gwps); @@ -82,6 +87,7 @@ void skin_unload_all(void); bool skin_do_full_update(enum skinnable_screens skin, enum screen_type screen); void skin_request_full_update(enum skinnable_screens skin); +void skin_request_update_locked(bool locked); bool dbg_skin_engine(void); diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c index c3294c53bf..1030d3926c 100644 --- a/apps/gui/skin_engine/skin_parser.c +++ b/apps/gui/skin_engine/skin_parser.c @@ -2676,7 +2676,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, if (isfile) { /* get the bitmap dir */ - char *dot = strrchr(buf, '.'); + const char *dot = strrchr(buf, '.'); strmemccpy(bmpdir, buf, dot - buf + 1); } else diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c index 77443eb8eb..eefbc80ee7 100644 --- a/apps/gui/skin_engine/skin_render.c +++ b/apps/gui/skin_engine/skin_render.c @@ -84,6 +84,8 @@ static void skin_render_playlistviewer(struct playlistviewer* viewer, unsigned long refresh_type); static char* skin_buffer; +static bool defer_rendering; +static bool dirty[NB_SCREENS]; static inline struct skin_element* get_child(OFFSETTYPE(struct skin_element**) children, int child) @@ -295,7 +297,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, { struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, data->albumart); if (aa) - { + { int handle = playback_current_aa_hid(data->playback_aa_slot); #if CONFIG_TUNER if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF)) @@ -841,6 +843,29 @@ void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps, wps_display_images(gwps, &skin_viewport->vp); } +void skin_defer_rendering(bool deferred) +{ + defer_rendering = deferred; +} + +void skin_render_deferred(struct screen *display, struct viewport *vp) +{ + if (defer_rendering) + return; + + if (dirty[display->screen_type]) + { + dirty[display->screen_type] = false; + display->set_viewport(NULL); + display->update(); + } + else + { + display->set_viewport(vp); + display->update_viewport(); + } +} + void skin_render(struct gui_wps *gwps, unsigned refresh_mode) { const int vp_is_appearing = (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE); @@ -932,15 +957,19 @@ void skin_render(struct gui_wps *gwps, unsigned refresh_mode) skin_backdrop_show(data->backdrop_id); #endif + dirty[display->screen_type] = defer_rendering; if (((refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)) { + defer_rendering = true; /* If this is the UI viewport then let the UI know * to redraw itself */ send_event(GUI_EVENT_NEED_UI_UPDATE, NULL); + defer_rendering = dirty[display->screen_type]; } /* Restore the default viewport */ display->set_viewport_ex(NULL, VP_FLAG_VP_SET_CLEAN); - display->update(); + if (!defer_rendering) + display->update(); } static __attribute__((noinline)) diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c index eaa71c9b70..7098d8aa05 100644 --- a/apps/gui/skin_engine/skin_touchsupport.c +++ b/apps/gui/skin_engine/skin_touchsupport.c @@ -34,6 +34,7 @@ #include "playlist.h" #include "dsp_misc.h" #include "playback.h" +#include "iap-usb.h" /** Disarms all touchregions. */ void skin_disarm_touchregions(struct gui_wps *gwps) @@ -344,6 +345,7 @@ int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset) case ACTION_TOUCH_SHUFFLE: global_settings.playlist_shuffle = !global_settings.playlist_shuffle; + iap_on_shuffle_state(global_settings.playlist_shuffle); replaygain_update(); if (global_settings.playlist_shuffle) playlist_randomise(NULL, current_tick, true); @@ -358,6 +360,7 @@ int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset) const struct settings_list *rep_setting = find_setting(&global_settings.repeat_mode); option_select_next_val(rep_setting, false, true); + iap_on_repeat_state(global_settings.repeat_mode); audio_flush_and_reload_tracks(); action = ACTION_REDRAW; } break; diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c index 0242016e1e..715c3d6377 100644 --- a/apps/gui/statusbar-skinned.c +++ b/apps/gui/statusbar-skinned.c @@ -182,17 +182,18 @@ int sb_get_backdrop(enum screen_type screen) return -1; } #endif -static bool force_waiting = false; +static bool force_waiting[NB_SCREENS]; void sb_skin_update(enum screen_type screen, bool force) { struct wps_data *data = skin_get_gwps(CUSTOM_STATUSBAR, screen)->data; - static long next_update[NB_SCREENS] = {0}; + static long next_update[NB_SCREENS]; int i = screen; if (!data->wps_loaded) return; - if (TIME_AFTER(current_tick, next_update[i]) || force || force_waiting) + if (TIME_AFTER(current_tick, next_update[i]) || force || + force_waiting[i]) { - force_waiting = false; + force_waiting[i] = false; #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) /* currently, all remotes are readable without backlight * so still update those */ @@ -214,7 +215,8 @@ void do_sbs_update_callback(unsigned short id, void *param) /* the WPS handles changing the actual id3 data in the id3 pointers * we imported, we just want a full update */ skin_request_full_update(CUSTOM_STATUSBAR); - force_waiting = true; + FOR_NB_SCREENS(i) + force_waiting[i] = true; /* force timeout in wps main loop, so that the update is instantly */ button_queue_post(BUTTON_NONE, 0); } @@ -224,6 +226,12 @@ void sb_skin_set_update_delay(int delay) update_delay = delay; } +void sb_skin_force_next_update(void) +{ + FOR_NB_SCREENS(i) + force_waiting[i] = true; +} + /* This creates and loads a ".sbs" based on the user settings for: * - regular statusbar * - colours diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h index 905a15b369..323ce7f0d2 100644 --- a/apps/gui/statusbar-skinned.h +++ b/apps/gui/statusbar-skinned.h @@ -38,6 +38,7 @@ struct viewport *sb_skin_get_info_vp(enum screen_type screen); void sb_skin_update(enum screen_type screen, bool force); void sb_skin_set_update_delay(int delay); +void sb_skin_force_next_update(void); bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen); bool sb_set_persistent_title(const char* title, enum themable_icons icon, enum screen_type screen); diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c index 9eda934d2e..d4dd6d0590 100644 --- a/apps/gui/statusbar.c +++ b/apps/gui/statusbar.c @@ -331,7 +331,6 @@ void gui_statusbar_draw(struct gui_statusbar * bar, bool force_redraw, struct vi } #endif display->setfont(FONT_UI); - display->update_viewport(); display->set_viewport(last_vp); bar->lastinfo = bar->info; } diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c index 1d89467682..6bc51401f5 100644 --- a/apps/gui/viewport.c +++ b/apps/gui/viewport.c @@ -91,13 +91,12 @@ static void toggle_events(bool enable) #endif } -static void set_clear_update_valid_vp(enum screen_type screen, struct viewport *vp) +static void set_clear_valid_vp(enum screen_type screen, struct viewport *vp) { if (vp->width && vp->height) { screens[screen].set_viewport(vp); screens[screen].clear_viewport(); - screens[screen].update_viewport(); } } @@ -135,27 +134,31 @@ static void toggle_theme(enum screen_type screen, bool force) deadspace.y = 0; deadspace.width = screens[screen].lcdwidth; deadspace.height = user.y; - set_clear_update_valid_vp(screen, &deadspace); + set_clear_valid_vp(screen, &deadspace); /* below */ deadspace.y = user.y + user.height; deadspace.height = screens[screen].lcdheight - deadspace.y; - set_clear_update_valid_vp(screen, &deadspace); + set_clear_valid_vp(screen, &deadspace); /* left */ deadspace.x = 0; deadspace.y = 0; deadspace.width = user.x; deadspace.height = screens[screen].lcdheight; - set_clear_update_valid_vp(screen, &deadspace); + set_clear_valid_vp(screen, &deadspace); /* below */ deadspace.x = user.x + user.width; deadspace.width = screens[screen].lcdwidth - deadspace.x; - set_clear_update_valid_vp(screen, &deadspace); + set_clear_valid_vp(screen, &deadspace); screens[screen].set_viewport(last_vp); } intptr_t force = first_boot?0:1; + skin_defer_rendering(true); send_event(GUI_EVENT_ACTIONUPDATE, (void*)force); + skin_defer_rendering(false); + if (!first_boot) + sb_skin_force_next_update(); } else { diff --git a/apps/gui/wps.c b/apps/gui/wps.c index 0e52323dc2..99a1f6917e 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c @@ -533,7 +533,11 @@ static void gwps_leave_wps(bool theme_enabled) viewports drawn by the WPS. May need further thought... */ struct wps_data *sbs = skin_get_gwps(CUSTOM_STATUSBAR, i)->data; if (gwps->data->use_extra_framebuffer && sbs->use_extra_framebuffer) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } #endif viewportmanager_theme_undo(i, skin_has_sbs(gwps)); } @@ -595,7 +599,10 @@ static void gwps_enter_wps(bool theme_enabled) skin_backdrop_show(gwps->data->backdrop_id); #endif display->clear_display(); + if (skin_has_sbs(gwps)) + skin_defer_rendering(true); skin_update(WPS, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); } #ifdef HAVE_TOUCHSCREEN diff --git a/apps/keymaps/keymap-erosq.c b/apps/keymaps/keymap-erosq.c index b1e4d246a8..50d91f8ce9 100644 --- a/apps/keymaps/keymap-erosq.c +++ b/apps/keymaps/keymap-erosq.c @@ -129,6 +129,10 @@ static const struct button_mapping button_context_quickscreen[] = { { ACTION_QS_LEFT, BUTTON_SCROLL_BACK, BUTTON_NONE }, { ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REL, BUTTON_NONE }, { ACTION_QS_DOWN, BUTTON_NEXT|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_CANCEL, BUTTON_BACK, BUTTON_NONE }, { ACTION_STD_CONTEXT,BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU }, diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang index 1120999871..e714f0c713 100644 --- a/apps/lang/chinese-simp.lang +++ b/apps/lang/chinese-simp.lang @@ -14676,7 +14676,7 @@ user: core *: "Press LEFT to cancel." - android,hifietma*,zenvision: "Press BACK to cancel." + android,hifietma*: "Press BACK to cancel." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel." ihifi760,ihifi960: "Double tap RETURN to cancel." ihifi770,ihifi770c,ihifi800: "Press HOME to cancel." @@ -14691,7 +14691,7 @@ *: "请按LEFT键取消。" - android,hifietma*,zenvision: "请按BACK键取消。" + android,hifietma*: "请按BACK键取消。" cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWER键取消。" ihifi760,ihifi960: "双击RETURN键取消。" ihifi770,ihifi770c,ihifi800: "请按HOME键取消。" @@ -14707,7 +14707,7 @@ *: "请按LEFT键取消" - android,hifietma*,zenvision: "请按BACK键取消" + android,hifietma*: "请按BACK键取消" cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWER键取消" ihifi760,ihifi960: "双击RETURN键取消" ihifi770,ihifi770c,ihifi800: "请按HOME键取消" @@ -15202,7 +15202,7 @@ id: LANG_VOICED_DATE_FORMAT - desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021" + desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one user: core *: "dAY" @@ -15811,16 +15811,16 @@ id: LANG_DEFAULT_BROWSER - desc: in Settings + desc: deprecated user: core - *: "Default Browser" + *: "" - *: "默认浏览器" + *: "" - *: "默认浏览器" + *: "" @@ -16967,3 +16967,143 @@ *: "~U S B" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "播报状态" + + + *: "播报状态" + + + + id: LANG_KEEP_DIRECTORY + desc: file browser setting + user: core + + *: "Always remember last folder" + + + *: "总是记住上次的文件夹" + + + *: "总是记住上次的文件夹" + + + + id: LANG_FILE_NOT_FOUND + desc: When file does not exist + user: core + + *: "File not found" + + + *: "找不到文件" + + + *: "找不到文件" + + + + id: LANG_SHOW_IN_FILES + desc: Reveal item in File Browser + user: core + + *: "Show in Files" + + + *: "按文件展示" + + + *: "按文件展示" + + + + id: LANG_CHANNEL_SWAP + desc: in sound_settings + user: core + + *: "Swap Left & Right" + + + *: "切换左&右" + + + *: "切换左右" + + + + id: LANG_COUNTDOWN_TIMER_SET + desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration + user: core + + *: "SET TIMER" + + + *: "设定计时器" + + + *: "设定计时器" + + + + id: LANG_COUNTDOWN_TIMER_RUNNING + desc: countdown_timer plugin - status label shown while the countdown is active + user: core + + *: "RUNNING" + + + *: "运行中" + + + *: "运行中" + + + + id: LANG_COUNTDOWN_TIMER_PAUSED + desc: countdown_timer plugin - status label shown while the countdown is paused + user: core + + *: "PAUSED" + + + *: "暂停" + + + *: "暂停" + + + + id: LANG_COUNTDOWN_TIMER_OVERTIME + desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up + user: core + + *: "OVERTIME" + + + *: "超时" + + + *: "超时" + + + + id: LANG_COUNTDOWN_TIMER_FINISHED + desc: countdown_timer plugin - status label shown at the moment the countdown expires + user: core + + *: "FINISHED" + + + *: "完成" + + + *: "完成" + + diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang index 806ee79348..28c3529a3b 100644 --- a/apps/lang/deutsch.lang +++ b/apps/lang/deutsch.lang @@ -15822,16 +15822,16 @@ id: LANG_DEFAULT_BROWSER - desc: in Settings + desc: deprecated user: core - *: "Default Browser" + *: "" - *: "Standardbrowser" + *: "" - *: "Standardbrowser" + *: "" @@ -17006,3 +17006,115 @@ *: "~U S B" + + id: LANG_FILE_NOT_FOUND + desc: When file does not exist + user: core + + *: "File not found" + + + *: "Datei nicht gefunden" + + + *: "Datei nicht gefunden" + + + + id: LANG_SHOW_IN_FILES + desc: Reveal item in File Browser + user: core + + *: "Show in Files" + + + *: "Zeige in Dateien" + + + *: "Zeige in Dateien" + + + + id: LANG_CHANNEL_SWAP + desc: in sound_settings + user: core + + *: "Swap Left & Right" + + + *: "Rechts & Links tauschen" + + + *: "Rechts und Links tauschen" + + + + id: LANG_COUNTDOWN_TIMER_SET + desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration + user: core + + *: "SET TIMER" + + + *: "Timer stellen" + + + *: "Timer stellen" + + + + id: LANG_COUNTDOWN_TIMER_RUNNING + desc: countdown_timer plugin - status label shown while the countdown is active + user: core + + *: "RUNNING" + + + *: "LÄUFT" + + + *: "Läuft" + + + + id: LANG_COUNTDOWN_TIMER_PAUSED + desc: countdown_timer plugin - status label shown while the countdown is paused + user: core + + *: "PAUSED" + + + *: "ANGEHALTEN" + + + *: "Angehalten" + + + + id: LANG_COUNTDOWN_TIMER_OVERTIME + desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up + user: core + + *: "OVERTIME" + + + *: "ÜBERZEIT" + + + *: "Überzeit" + + + + id: LANG_COUNTDOWN_TIMER_FINISHED + desc: countdown_timer plugin - status label shown at the moment the countdown expires + user: core + + *: "FINISHED" + + + *: "BEENDET" + + + *: "Beendet" + + diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 45ed8938fc..8e0523a42b 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -78,7 +78,7 @@ # tools/voice-corrections.txt for further details. # # To validate the contents of a translation, you can use the Rockbox -# translation site at https://translate.rockbox.org. Alternatively, you +# translation site at https://www.rockbox.org/translate/ Alternatively, you # can use the command-line 'updatelang' tool as follows: # # tools/updatelang apps/lang/english.lang path/to/translation.lang path/to/updated.lang diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang index 5e837aa636..6d83d3f61e 100644 --- a/apps/lang/italiano.lang +++ b/apps/lang/italiano.lang @@ -15804,16 +15804,16 @@ id: LANG_DEFAULT_BROWSER - desc: in Settings + desc: deprecated user: core - *: "Default Browser" + *: "" - *: "Browser Predefinito" + *: "" - *: "Browser Predefinito" + *: "" @@ -16988,3 +16988,115 @@ *: "~U S B" + + id: LANG_FILE_NOT_FOUND + desc: When file does not exist + user: core + + *: "File not found" + + + *: "File non trovato" + + + *: "File non trovato" + + + + id: LANG_SHOW_IN_FILES + desc: Reveal item in File Browser + user: core + + *: "Show in Files" + + + *: "Mostra nei file" + + + *: "Mostra nei file" + + + + id: LANG_CHANNEL_SWAP + desc: in sound_settings + user: core + + *: "Swap Left & Right" + + + *: "Scambia sinistro & destro" + + + *: "Scambia sinistro e destro" + + + + id: LANG_COUNTDOWN_TIMER_SET + desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration + user: core + + *: "SET TIMER" + + + *: "IMPOSTA TIMER" + + + *: "Imposta timer" + + + + id: LANG_COUNTDOWN_TIMER_RUNNING + desc: countdown_timer plugin - status label shown while the countdown is active + user: core + + *: "RUNNING" + + + *: "IN CORSO" + + + *: "In corso" + + + + id: LANG_COUNTDOWN_TIMER_PAUSED + desc: countdown_timer plugin - status label shown while the countdown is paused + user: core + + *: "PAUSED" + + + *: "IN PAUSA" + + + *: "In pausa" + + + + id: LANG_COUNTDOWN_TIMER_OVERTIME + desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up + user: core + + *: "OVERTIME" + + + *: "TEMPO SUPPLEMENTARE" + + + *: "Tempo supplementare" + + + + id: LANG_COUNTDOWN_TIMER_FINISHED + desc: countdown_timer plugin - status label shown at the moment the countdown expires + user: core + + *: "FINISHED" + + + *: "TERMINATO" + + + *: "Terminato" + + diff --git a/apps/lang/latviesu.lang b/apps/lang/latviesu.lang index e0db4361b6..0d65d484fa 100644 --- a/apps/lang/latviesu.lang +++ b/apps/lang/latviesu.lang @@ -14781,7 +14781,7 @@ user: core *: "Press LEFT to cancel." - android,hifietma*,zenvision: "Press BACK to cancel." + android,hifietma*: "Press BACK to cancel." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel." ihifi760,ihifi960: "Double tap RETURN to cancel." ihifi770,ihifi770c,ihifi800: "Press HOME to cancel." @@ -14796,7 +14796,7 @@ *: "Nospiediet LEFT, lai atceltu." - android,hifietma*,zenvision: "Nospiediet BACK, lai atceltu." + android,hifietma*: "Nospiediet BACK, lai atceltu." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Nospiediet POWER, lai atceltu." ihifi760,ihifi960: "Divreiz pieskarieties RETURN, lai atceltu." ihifi770,ihifi770c,ihifi800: "Nospiediet HOME, lai atceltu." @@ -14811,7 +14811,7 @@ *: "Nospiediet LEFT, lai atceltu." - android,hifietma*,zenvision: "Nospiediet BACK, lai atceltu." + android,hifietma*: "Nospiediet BACK, lai atceltu." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Nospiediet POWER, lai atceltu." ihifi760,ihifi960: "Divreiz pieskarieties RETURN, lai atceltu." ihifi770,ihifi770c,ihifi800: "Nospiediet HOME, lai atceltu." @@ -15306,13 +15306,13 @@ id: LANG_VOICED_DATE_FORMAT - desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021" + desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one user: core *: "dAY" - *: "dAY" + *: "dAy" *: "" @@ -15915,16 +15915,16 @@ id: LANG_DEFAULT_BROWSER - desc: in Settings + desc: deprecated user: core - *: "Default Browser" + *: "" - *: "Noklusējuma pārlūks" + *: "" - *: "Noklusējuma pārlūks" + *: "" @@ -16973,3 +16973,129 @@ *: "Ju es bī" + + id: LANG_KEEP_DIRECTORY + desc: file browser setting + user: core + + *: "Always remember last folder" + + + *: "Vienmēr atcerēties pēdējo mapi" + + + *: "Vienmēr atcerēties pēdējo mapi" + + + + id: LANG_FILE_NOT_FOUND + desc: When file does not exist + user: core + + *: "File not found" + + + *: "Fails nav atrasts" + + + *: "Fails nav atrasts" + + + + id: LANG_SHOW_IN_FILES + desc: Reveal item in File Browser + user: core + + *: "Show in Files" + + + *: "Rādīt failu pārlūkā" + + + *: "Rādīt failu pārlūkā" + + + + id: LANG_CHANNEL_SWAP + desc: in sound_settings + user: core + + *: "Swap Left & Right" + + + *: "Apmainīt kanālus vietām" + + + *: "Apmainīt kanālus vietām" + + + + id: LANG_COUNTDOWN_TIMER_SET + desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration + user: core + + *: "SET TIMER" + + + *: "IESTATĪT TAIMERI" + + + *: "Iestatīt taimeri" + + + + id: LANG_COUNTDOWN_TIMER_RUNNING + desc: countdown_timer plugin - status label shown while the countdown is active + user: core + + *: "RUNNING" + + + *: "DARBOJAS" + + + *: "Darbojas" + + + + id: LANG_COUNTDOWN_TIMER_PAUSED + desc: countdown_timer plugin - status label shown while the countdown is paused + user: core + + *: "PAUSED" + + + *: "APTURĒTS" + + + *: "Apturēts" + + + + id: LANG_COUNTDOWN_TIMER_OVERTIME + desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up + user: core + + *: "OVERTIME" + + + *: "PAPILDLAIKS" + + + *: "Papildlaiks" + + + + id: LANG_COUNTDOWN_TIMER_FINISHED + desc: countdown_timer plugin - status label shown at the moment the countdown expires + user: core + + *: "FINISHED" + + + *: "BEIDZIES" + + + *: "Beidzies" + + diff --git a/apps/lang/srpski.lang b/apps/lang/srpski.lang index 1a238f8207..1044e3f32b 100644 --- a/apps/lang/srpski.lang +++ b/apps/lang/srpski.lang @@ -14780,7 +14780,7 @@ user: core *: "Press LEFT to cancel." - android,hifietma*,zenvision: "Press BACK to cancel." + android,hifietma*: "Press BACK to cancel." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel." ihifi760,ihifi960: "Double tap RETURN to cancel." ihifi770,ihifi770c,ihifi800: "Press HOME to cancel." @@ -14795,7 +14795,7 @@ *: "Притисните LEFT за прекид." - android,hifietma*,zenvision: "Притисните BACK за прекид." + android,hifietma*: "Притисните BACK за прекид." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Притисните POWER за прекид." ihifi760,ihifi960: "Дупли тап RETURN за прекид." ihifi770,ihifi770c,ihifi800: "Притисните HOME за прекид." @@ -14810,7 +14810,7 @@ *: "Притисните LEFT за прекид." - android,hifietma*,zenvision: "Притисните BACK за прекид." + android,hifietma*: "Притисните BACK за прекид." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Притисните POWER за прекид." ihifi760,ihifi960: "Дупли тап RETURN за прекид." ihifi770,ihifi770c,ihifi800: "Притисните HOME за прекид." @@ -15305,7 +15305,7 @@ id: LANG_VOICED_DATE_FORMAT - desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021" + desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one user: core *: "dAY" @@ -15914,16 +15914,16 @@ id: LANG_DEFAULT_BROWSER - desc: in Settings + desc: deprecated user: core - *: "Default Browser" + *: "" - *: "Подраз. прегледач" + *: "" - *: "Подраз. прегледач" + *: "" @@ -16972,3 +16972,129 @@ *: "У Ес Бе" + + id: LANG_KEEP_DIRECTORY + desc: file browser setting + user: core + + *: "Always remember last folder" + + + *: "Увек памти последњи фолдер" + + + *: "Увек памти последњи фолдер" + + + + id: LANG_FILE_NOT_FOUND + desc: When file does not exist + user: core + + *: "File not found" + + + *: "Фајл није пронађен" + + + *: "Фајл није пронађен" + + + + id: LANG_SHOW_IN_FILES + desc: Reveal item in File Browser + user: core + + *: "Show in Files" + + + *: "Прикажи у фајловима" + + + *: "Прикажи у фајловима" + + + + id: LANG_CHANNEL_SWAP + desc: in sound_settings + user: core + + *: "Swap Left & Right" + + + *: "Замени леви и десни" + + + *: "Замени леви и десни" + + + + id: LANG_COUNTDOWN_TIMER_SET + desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration + user: core + + *: "SET TIMER" + + + *: "ПОСТАВИ ТАЈМЕР" + + + *: "Постави тајмер" + + + + id: LANG_COUNTDOWN_TIMER_RUNNING + desc: countdown_timer plugin - status label shown while the countdown is active + user: core + + *: "RUNNING" + + + *: "ОДБРОЈАВАЊЕ" + + + *: "Одбројавање" + + + + id: LANG_COUNTDOWN_TIMER_PAUSED + desc: countdown_timer plugin - status label shown while the countdown is paused + user: core + + *: "PAUSED" + + + *: "ПАУЗИРАНО" + + + *: "Паузирано" + + + + id: LANG_COUNTDOWN_TIMER_OVERTIME + desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up + user: core + + *: "OVERTIME" + + + *: "ИСТЕКЛО ВРЕМЕ" + + + *: "Истекло време" + + + + id: LANG_COUNTDOWN_TIMER_FINISHED + desc: countdown_timer plugin - status label shown at the moment the countdown expires + user: core + + *: "FINISHED" + + + *: "ГОТОВО" + + + *: "Готово" + + diff --git a/apps/menus/eq_menu.c b/apps/menus/eq_menu.c index 5ffc78c7c5..831cd06b6f 100644 --- a/apps/menus/eq_menu.c +++ b/apps/menus/eq_menu.c @@ -442,13 +442,10 @@ enum eq_type { HIGH_SHELF }; -/* Size of just the slider/srollbar */ -#define SCROLLBAR_SIZE 6 - /* Draw the UI for a whole EQ band */ static int draw_eq_slider(struct screen * screen, int x, int y, int width, int cutoff, int q, int gain, bool selected, - enum eq_slider_mode mode, int band) + enum eq_slider_mode mode, int band, int scrollbar_size) { char buf[26]; int steps, min_item, max_item; @@ -534,14 +531,14 @@ static int draw_eq_slider(struct screen * screen, int x, int y, screen->putsxy(x1, y1, buf); /* Draw selection box */ - total_height = 3 + h + 1 + SCROLLBAR_SIZE + 3; + total_height = 3 + h + 1 + scrollbar_size + 3; screen->set_drawmode(DRMODE_SOLID); if (selected) { screen->drawrect(x, y, width, total_height); } /* Draw horizontal slider. Reuse scrollbar for this */ - gui_scrollbar_draw(screen, x + 3, y1 + h + 1, width - 6, SCROLLBAR_SIZE, + gui_scrollbar_draw(screen, x + 3, y1 + h + 1, width - 6, scrollbar_size, steps, min_item, max_item, HORIZONTAL); return total_height; @@ -550,7 +547,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y, /* Draw's all the EQ sliders. Returns the total height of the sliders drawn */ static void draw_eq_sliders(struct screen * screen, int x, int y, int nb_eq_sliders, int start_item, - int current_band, enum eq_slider_mode mode) + int current_band, enum eq_slider_mode mode, int scrollbar_size) { int height = y; @@ -568,14 +565,14 @@ static void draw_eq_sliders(struct screen * screen, int x, int y, if (i >= start_item) { height += draw_eq_slider(screen, x, height, screen->lcdwidth - x - 1, cutoff, q, gain, i == current_band, mode, - i); + i, scrollbar_size); /* add a margin */ height++; } } if (nb_eq_sliders != EQ_NUM_BANDS) - gui_scrollbar_draw(screen, 0, y, SCROLLBAR_SIZE - 1, + gui_scrollbar_draw(screen, 0, y, scrollbar_size - 1, screen->lcdheight - y, EQ_NUM_BANDS, start_item, start_item + nb_eq_sliders, VERTICAL); @@ -593,20 +590,23 @@ int eq_menu_graphical(void) int current_band, x, y, step, fast_step, min, max; enum eq_slider_mode mode; int h, height, start_item, nb_eq_sliders[NB_SCREENS]; + int scrollbar_size[NB_SCREENS]; + FOR_NB_SCREENS(i) viewportmanager_theme_enable(i, false, NULL); - FOR_NB_SCREENS(i) { screens[i].set_viewport(NULL); - screens[i].setfont(FONT_SYSFIXED); screens[i].clear_display(); /* Figure out how many sliders can be drawn on the screen */ h = screens[i].getcharheight(); + scrollbar_size[i] = h * 3 / 4; + if (scrollbar_size[i] < 6) + scrollbar_size[i] = 6; /* Total height includes margins (1), text, slider, and line selector (1) */ - height = 3 + h + 1 + SCROLLBAR_SIZE + 3; + height = 3 + h + 1 + scrollbar_size[i] + 3; nb_eq_sliders[i] = screens[i].lcdheight / height; /* Make sure the "Edit Mode" text fits too */ @@ -675,14 +675,14 @@ int eq_menu_graphical(void) } else { start_item = current_band - 1; } - x = SCROLLBAR_SIZE; + x = scrollbar_size[i]; } else { x = 1; start_item = 0; } /* Draw equalizer band details */ draw_eq_sliders(&screens[i], x, y, nb_eq_sliders[i], start_item, - current_band, mode); + current_band, mode, scrollbar_size[i]); screens[i].update(); } @@ -761,7 +761,6 @@ int eq_menu_graphical(void) /* Reset screen settings */ FOR_NB_SCREENS(i) { - screens[i].setfont(FONT_UI); screens[i].clear_display(); screens[i].set_viewport(NULL); viewportmanager_theme_undo(i, false); diff --git a/apps/misc.c b/apps/misc.c index a44f106fb9..0c5d73d2ee 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -29,6 +29,7 @@ #include "system.h" #include "lcd.h" #include "language.h" /* is_lang_rtl() */ +#include "iap-usb.h" #ifdef HAVE_DIRCACHE #include "dircache.h" @@ -64,6 +65,7 @@ #include "list.h" #include "fixedpoint.h" #include "open_plugin.h" +#include "statusbar-skinned.h" #include "debug.h" @@ -870,6 +872,7 @@ void check_bootfile(bool do_rolo) void setvol(void) { sound_set_volume(global_status.volume); + iap_on_volume(global_status.volume); global_status.last_volume_change = current_tick; status_save(false); } @@ -1793,8 +1796,14 @@ static void push_current_activity_refresh(enum current_activity screen, bool ref { skinlist_set_cfg(i, NULL); if (refresh) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } } + if (refresh) + sb_skin_force_next_update(); } static void pop_current_activity_refresh(bool refresh) @@ -1804,8 +1813,14 @@ static void pop_current_activity_refresh(bool refresh) { skinlist_set_cfg(i, NULL); if (refresh) + { + skin_defer_rendering(true); skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + } } + if (refresh) + sb_skin_force_next_update(); } void push_current_activity(enum current_activity screen) diff --git a/apps/onplay.c b/apps/onplay.c index 82b5ab8931..824b2f38cc 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -1287,6 +1287,7 @@ static int hotkey_tree_run_plugin(void *param) return ONPLAY_RELOAD_DIR; } +static int hotkey_run_menu(void); /* display hotkey items as a menu */ static int hotkey_wps_run_plugin(void) { open_plugin_run(ID2P(LANG_HOTKEY_WPS)); @@ -1297,6 +1298,7 @@ static int hotkey_wps_run_plugin(void) /* Any desired hotkey functions go here, in the enum in onplay.h, and in the settings menu in settings_list.c. The order here is not important. */ + static const struct hotkey_assignment hotkey_items[] = { [0]{ .action = HOTKEY_OFF, .lang_id = LANG_OFF, @@ -1360,6 +1362,18 @@ static const struct hotkey_assignment hotkey_items[] = { .func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"properties"), .return_code = ONPLAY_FUNC_RETURN, .flags = HOTKEY_FLAG_TREE }, + { .action = HOTKEY_CONTEXT_MENU, + .lang_id = LANG_ONPLAY_MENU_TITLE, + .func = HOTKEY_FUNC(hotkey_run_menu, NULL), + .return_code = ONPLAY_FUNC_RETURN, + .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE }, +#ifdef HAVE_ALBUMART + { .action = HOTKEY_ALBUMART, + .lang_id = LANG_VIEW_ALBUMART, + .func = HOTKEY_FUNC(view_album_art, NULL), + .return_code = ONPLAY_OK, + .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS }, +#endif #ifdef HAVE_TAGCACHE { .action = HOTKEY_PICTUREFLOW, .lang_id = LANG_ONPLAY_PICTUREFLOW, @@ -1380,11 +1394,8 @@ const struct hotkey_assignment *get_hotkey(int action) } /* Execute the hotkey function, if listed */ -static int execute_hotkey(bool is_wps) +static int execute_hotkey(int action) { - const int action = (is_wps ? global_settings.hotkey_wps : - global_settings.hotkey_tree); - /* search assignment struct for a match for the hotkey setting */ const struct hotkey_assignment *this_item = get_hotkey(action); @@ -1405,6 +1416,60 @@ static int execute_hotkey(bool is_wps) return func_return; /* Use value returned by function */ return return_code; /* or return the associated value */ } + +static const char* hotkey_get_name(int selected_item, void * data, + char * buffer, size_t buffer_len) +{ + (void)buffer; (void)buffer_len; + const struct hotkey_assignment **hk_menu = (const struct hotkey_assignment **)data; + return ID2P(hk_menu[selected_item + 1]->lang_id); /* +1 to skip HOTKEY_OFF */ +} + +static int hotkey_get_talk(int selected_item, void * data) +{ + if (global_settings.talk_menu) + { + const struct hotkey_assignment **hk_menu = (const struct hotkey_assignment **)data; + talk_id(hk_menu[selected_item + 1]->lang_id, false); /* +1 to skip HOTKEY_OFF */ + } + return 0; +} + +static int hotkey_run_menu(void) +{ + intptr_t flag = HOTKEY_FLAG_WPS; + if (selected_file.context != CONTEXT_WPS) + flag = HOTKEY_FLAG_TREE; + + const struct hotkey_assignment *hk_menu[ARRAYLEN(hotkey_items)]; + + struct simplelist_info info; + int count = 0; + for (size_t i = 0; i < ARRAYLEN(hotkey_items); i++) + { + hk_menu[i] = NULL; /*clear all the hk_menu entries prior to setting them */ + if ((hotkey_items[i].flags & flag) == flag) + { + if (hotkey_items[i].action != HOTKEY_CONTEXT_MENU) + { + hk_menu[count++] = &hotkey_items[i]; + } + } + } + + /* count -1 don't display HOTKEY_OFF item */ + simplelist_info_init(&info, str(LANG_ONPLAY_MENU_TITLE), count - 1, (void*)&hk_menu); + info.get_name = hotkey_get_name; + info.get_icon = NULL; + info.get_talk = hotkey_get_talk; + + simplelist_show_list(&info); + if (info.selection >= 0) /* run user selected hotkey item */ + { + return execute_hotkey(hk_menu[info.selection + 1]->action); + } + return ONPLAY_RELOAD_DIR; +} #endif /* HOTKEY */ int onplay(char* file, int attr, int from_context, bool hotkey, int customaction) @@ -1441,7 +1506,8 @@ int onplay(char* file, int attr, int from_context, bool hotkey, int customaction #ifdef HAVE_HOTKEY if (hotkey) - return execute_hotkey(from_context == CONTEXT_WPS); + return execute_hotkey((from_context == CONTEXT_WPS ? + global_settings.hotkey_wps : global_settings.hotkey_tree)); #else (void)hotkey; #endif diff --git a/apps/onplay.h b/apps/onplay.h index 4e5e6eeb69..e811b9b983 100644 --- a/apps/onplay.h +++ b/apps/onplay.h @@ -63,6 +63,8 @@ enum hotkey_action { HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED, HOTKEY_BOOKMARK_LIST, + HOTKEY_ALBUMART, + HOTKEY_CONTEXT_MENU, /* shows / executes above actions in a menu */ }; enum hotkey_flags { HOTKEY_FLAG_NONE = 0x0, diff --git a/apps/open_plugin.c b/apps/open_plugin.c index c892a94233..7950d96101 100644 --- a/apps/open_plugin.c +++ b/apps/open_plugin.c @@ -35,7 +35,7 @@ static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM; -static const int op_entry_sz = sizeof(struct open_plugin_entry_t); +#define OP_ENTRY_SZ (sizeof(struct open_plugin_entry_t)) static const char* strip_rockbox_root(const char *path) { @@ -49,7 +49,7 @@ static inline void op_clear_entry(struct open_plugin_entry_t *entry) { if (entry == NULL) return; - memset(entry, 0, op_entry_sz); + memset(entry, 0, OP_ENTRY_SZ); entry->lang_id = OPEN_PLUGIN_LANG_INVALID; } @@ -78,7 +78,7 @@ static int op_find_entry(int fd, struct open_plugin_entry_t *entry, { logf("OP find_entry *Searching* hash: %x lang_id: %d", hash, lang_id); - while (read(fd, entry, op_entry_sz) == op_entry_sz) + while (read(fd, entry, OP_ENTRY_SZ) == OP_ENTRY_SZ) { if (entry->lang_id == lang_id || entry->hash == hash || (lang_id == OPEN_PLUGIN_LANG_IGNOREALL))/* return first entry found */ @@ -167,9 +167,9 @@ static int op_update_dat(struct open_plugin_entry_t *entry, bool clear) lseek(fd, 0-hlc_sz, SEEK_CUR);/* back to the start of record */ break; } - lseek(fd, op_entry_sz - hlc_sz, SEEK_CUR); /* finish record */ + lseek(fd, OP_ENTRY_SZ - hlc_sz, SEEK_CUR); /* finish record */ } - write(fd, entry, op_entry_sz); + write(fd, entry, OP_ENTRY_SZ); close(fd); #else /* Everyone else make a temp file */ logf("OP update *Copying entries* %s", OPEN_PLUGIN_DAT ".tmp"); @@ -177,18 +177,18 @@ static int op_update_dat(struct open_plugin_entry_t *entry, bool clear) if (fd < 0) return OPEN_PLUGIN_NOT_FOUND; - write(fd, entry, op_entry_sz); + write(fd, entry, OP_ENTRY_SZ); int fd1 = open(OPEN_PLUGIN_DAT, O_RDONLY); if (fd1 >= 0) { /* copy non-duplicate entries back from original */ - while (read(fd1, entry, op_entry_sz) == op_entry_sz) + while (read(fd1, entry, OP_ENTRY_SZ) == OP_ENTRY_SZ) { if (entry->hash != hash && entry->lang_id != lang_id && op_entry_checksum(entry) > 0) { - write(fd, entry, op_entry_sz); + write(fd, entry, OP_ENTRY_SZ); } } close(fd1); @@ -529,41 +529,13 @@ static bool op_entry_read(int fd, int selected_item, off_t data_sz, struct open_ { op_clear_entry(op_entry); return ((selected_item >= 0) && (fd >= 0) && - (lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) && + (lseek(fd, selected_item * OP_ENTRY_SZ, SEEK_SET) >= 0) && (read(fd, op_entry, data_sz) == data_sz) && op_entry_checksum(op_entry) > 0); } -void open_plugin_import(char *strdat) +void open_plugin_import(const char *cfg_file) { - /* Note: Destroys strdat */ - char *vect[5]; - /* expected openplugin strdat: "englishid", "name", "path", "param" */ - - if (split_string(strdat, ',', vect, 5) == 4) - { - /* Space for 5 values so we will fail if excess arguments or too few */ - for(int i = 0; i < 4; i++) - { - vect[i] = skip_whitespace(vect[i]); - int eos = ((int)strlen(vect[i]))-2; - /* Failure if string is not quoted */ - if (vect[i][0] != '"' || eos < 0 || vect[i][eos + 1] != '"') - { - logf("%s[%d] error: quoted string expected\n", __func__, i); - return; - } - vect[i]++; /* skip first " */ - vect[i][eos] = '\0'; /* skip other " */ - } - char *key = vect[1]; - int lang_id = lang_english_to_id(vect[0]); - if (lang_id >= 0) - key = ID2P(lang_id); - /* if key exists it will be overwritten */ - open_plugin_add_path(key, vect[2], vect[3]); - return; - } - logf("%s error: importing entries", __func__); + plugin_load(VIEWERS_DIR"/open_plugins.rock", cfg_file); } void open_plugin_export(int cfg_fd) @@ -584,7 +556,7 @@ void open_plugin_export(int cfg_fd) logf("%s error: opening %s", __func__, OPEN_PLUGIN_DAT); return; /* OPEN_PLUGIN_NOT_FOUND */ } - while (op_entry_read(fd, index++, op_entry_sz, op_entry)) + while (op_entry_read(fd, index++, OP_ENTRY_SZ, op_entry)) { /* don't save the LANG_OPEN_PLUGIN entry -- it is for internal use */ if (op_entry->lang_id == LANG_OPEN_PLUGIN) @@ -596,8 +568,16 @@ void open_plugin_export(int cfg_fd) else lang_name = "[USER]"; /* needs to be an invalid lang string */ - fdprintf(cfg_fd,"%s: \"%s\", \"%s\", \"%s\", \"%s\"\n", - "openplugin", lang_name, op_entry->name, op_entry->path, op_entry->param); + bool dblquote = strchr(op_entry->name, '"') != NULL || + strchr(op_entry->param, '"') != NULL; + const char* fmtstr = "%s: \"%s\", \"%s\", \"%s\", \"%s\"\n"; + + if (dblquote) /* if using double quotes export with single quotes */ + { + fmtstr = "%s: '%s', '%s', '%s', '%s'\n"; + } + fdprintf(cfg_fd, fmtstr, OPEN_PLUGIN_CFGNAME, + lang_name, op_entry->name, op_entry->path, op_entry->param); logf("openplugin[%d]: \"%s\", \"%s\", \"%s\", \"%s\"\n lang id: %d hash: %x, csum: %x\n", index, lang_name, op_entry->name, op_entry->path, op_entry->param, diff --git a/apps/open_plugin.h b/apps/open_plugin.h index 7038f419f3..3e00827baf 100644 --- a/apps/open_plugin.h +++ b/apps/open_plugin.h @@ -35,6 +35,7 @@ #define OPEN_RBPLUGIN_DAT PLUGIN_DIR "/rb_plugins.dat" #define OPEN_PLUGIN_BUFSZ MAX_PATH #define OPEN_PLUGIN_NAMESZ 32 +#define OPEN_PLUGIN_CFGNAME "openplugin" enum { OPEN_PLUGIN_LANG_INVALID = (-1), @@ -84,7 +85,7 @@ int open_plugin_load_entry(const char *key); void open_plugin_browse(const char *key); int open_plugin_run(const char *key); void open_plugin_cache_flush(void); /* flush to disk */ -void open_plugin_import(char *strdat); +void open_plugin_import(const char *cfg_file); void open_plugin_export(int cfg_fd); #endif diff --git a/apps/playback.c b/apps/playback.c index eeb4aca86e..8102f8d917 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -48,6 +48,7 @@ #include "settings.h" #include "audiohw.h" #include "general.h" +#include "iap-usb.h" #include #ifdef HAVE_TAGCACHE @@ -178,7 +179,11 @@ struct audio_resume_info static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ /** For album art support **/ +#if defined(USB_ENABLE_IAP) +#define MAX_MULTIPLE_AA (SKINNABLE_SCREENS_COUNT + 1) +#else #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT +#endif #ifdef HAVE_ALBUMART static int albumart_mode = -1; @@ -1407,6 +1412,7 @@ static void audio_playlist_track_change(void) { send_track_event(PLAYBACK_EVENT_TRACK_CHANGE, track_event_flags, id3); + iap_on_track_playback_index(playlist_get_display_index() - 1, false); } position_key = pcmbuf_get_position_key(); @@ -2315,6 +2321,8 @@ static int audio_finish_load_track(struct track_info *infop) by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */ send_track_event(PLAYBACK_EVENT_CUR_TRACK_READY, 0, id3_get(PLAYING_ID3)); + /* Also send pending notification */ + iap_on_track_playback_index(playlist_get_display_index() - 1, true); } #ifdef HAVE_CODEC_BUFFERING @@ -2742,6 +2750,7 @@ static void audio_finalise_track_change(void) if (pause_on_track_change || single_mode_do_pause(info.id3_hid)) { play_status = PLAY_PAUSED; + iap_on_play_status(play_status); pcmbuf_pause(true); pause_on_track_change = false; } @@ -3075,6 +3084,7 @@ static void audio_start_playback(const struct audio_resume_info *resume_info, /* Update our state */ play_status = PLAY_PLAYING; + iap_on_play_status(play_status); } /* Codec's position should be available as soon as it knows it */ @@ -3162,6 +3172,7 @@ static void audio_stop_playback(void) /* Update our state */ ff_rw_mode = false; play_status = PLAY_STOPPED; + iap_on_play_status(play_status); wipe_track_metadata(true); #ifdef HAVE_ALBUMART @@ -3183,6 +3194,7 @@ static void audio_on_pause(bool pause) return; play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; + iap_on_play_status(play_status); if (!pause && codec_skip_pending) { @@ -3824,6 +3836,7 @@ void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset, struct mp3entry *id3 = id3_get(PLAYING_ID3); id3->elapsed = elapsed; id3->offset = offset; + iap_on_track_time_position(elapsed); } } @@ -4234,12 +4247,22 @@ void audio_set_crossfade(int enable) static unsigned long audio_guess_frequency(struct mp3entry *id3) { const struct pcm_sink_caps* caps = pcm_sink_caps(pcm_current_sink()); + bool have_44 = false; + bool have_48 = false; for (size_t i = 0; i < caps->num_samprs; i += 1) { + if (caps->samprs[i] == SAMPR_44) + have_44 = true; + if (caps->samprs[i] == SAMPR_48) + have_48 = true; if (id3->frequency == caps->samprs[i]) return id3->frequency; } - return (id3->frequency % 4000) ? SAMPR_44 : SAMPR_48; + unsigned long fallback = (id3->frequency % 4000) ? SAMPR_44 : SAMPR_48; + if ((fallback == SAMPR_44 && have_44) || (fallback == SAMPR_48 && have_48)) + return fallback; + else + return caps->samprs[caps->default_freq]; } static bool audio_auto_change_frequency(struct mp3entry *id3, bool play) diff --git a/apps/playlist.c b/apps/playlist.c index 056bab957f..198e2baa01 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -107,6 +107,7 @@ #include "rbunicode.h" #include "root_menu.h" #include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */ +#include "iap-usb.h" #include "logdiskf.h" #ifdef HAVE_DIRCACHE #include "dircache.h" @@ -315,7 +316,7 @@ static void pl_close_control(struct playlist_info *playlist) /* Check if the filename suggests M3U or M3U8 format. */ static bool is_m3u8_name(const char* filename) { - char *dot = strrchr(filename, '.'); + const char *dot = strrchr(filename, '.'); /* Default to M3U8 unless explicitly told otherwise. */ return (!dot || strcasecmp(dot, ".m3u") != 0); @@ -1217,6 +1218,8 @@ static int remove_all_tracks_unlocked(struct playlist_info *playlist) playlist->amount = 1; playlist->indices[0] |= PLAYLIST_QUEUED; playlist->flags = 0; /* Reset dirplay and modified flags */ + if (playlist == ¤t_playlist) + iap_on_tracks_count(playlist->amount); if (playlist->last_insert_pos == 0) playlist->last_insert_pos = -1; @@ -1424,6 +1427,8 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist, dc_init_filerefs(playlist, insert_position, 1); playlist->amount++; + if (playlist == ¤t_playlist) + iap_on_tracks_count(playlist->amount); return insert_position; } @@ -1506,6 +1511,9 @@ static void find_and_set_playlist_index_unlocked(struct playlist_info* playlist, { playlist->index = playlist->first_index = i; + if (playlist == ¤t_playlist) + iap_on_track_playback_index(rotate_index(playlist, playlist->index), true); + break; } } @@ -2949,6 +2957,7 @@ int playlist_next(int steps) sort_playlist_unlocked(playlist, false, false); randomise_playlist_unlocked(playlist, current_tick, false, true); global_settings.playlist_shuffle = true; + iap_on_shuffle_state(global_settings.playlist_shuffle); playlist->started = true; playlist->index = 0; @@ -3537,8 +3546,9 @@ int playlist_resume(void) } } - if (global_status.resume_index != -1) + if (global_status.resume_index != -1) { playlist->index = global_status.resume_index; + } out: playlist_write_unlock(playlist); @@ -4047,8 +4057,10 @@ static int pl_save_update_control(struct playlist_info* playlist, /* Reset shuffle seed */ playlist->seed = 0; - if (playlist == ¤t_playlist) + if (playlist == ¤t_playlist) { global_settings.playlist_shuffle = false; + iap_on_shuffle_state(global_settings.playlist_shuffle); + } pl_close_control(playlist); close(old_fd); diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c index 392131a8a9..8e28606652 100644 --- a/apps/playlist_viewer.c +++ b/apps/playlist_viewer.c @@ -52,6 +52,10 @@ #include "yesno.h" #include "playback.h" +#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) +#include "tagcache.h" +#endif + /* Maximum number of tracks we can have loaded at one time */ #define MAX_PLAYLIST_ENTRIES 200 @@ -124,6 +128,8 @@ struct playlist_viewer { struct playlist_buffer buffer; struct mp3entry *id3; bool allow_view_text_plugin; + unsigned long loading_tick; + bool is_open; }; struct playlist_search_data @@ -214,6 +220,13 @@ static void playlist_buffer_load_entries(struct playlist_buffer *pb, int index, for (i = 0; i < num_entries; i++) { + /* provide UI feedback if opening Playlist Viewer is taking too long */ + if (!viewer.is_open && TIME_AFTER(current_tick, viewer.loading_tick)) + { + viewer.loading_tick += HZ*10; + splash(0, ID2P(LANG_WAIT)); + } + int len = playlist_entry_load(&(pb->tracks[i]), index, p, remaining); if (len < 0) { @@ -276,9 +289,15 @@ static bool retrieve_id3_tags(const int index, const char* name, struct mp3entry copy_mp3entry(id3, audio_current_track()); /* retrieve id3 from RAM */ id3_retrieval_successful = true; } - else +#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) + else if (flags & METADATA_EXCLUDE_ID3_PATH) + /* retrieve id3 from database */ + id3_retrieval_successful = tagcache_fill_tags(id3, name); +#endif + + if (!id3_retrieval_successful) { - /* Read from disk, the database, doesn't store frequency, file size or codec (g4470) ChrisS*/ + /* Read from disk: retrieves frequency, file size, and codec */ id3_retrieval_successful = get_metadata_ex(id3, -1, name, flags); } return id3_retrieval_successful; @@ -497,7 +516,7 @@ static void format_name(char* dest, const char* src, size_t bufsz) default: { /* Only display the filename */ - char* p = strrchr(src, '/'); + const char* p = strrchr(src, '/'); strlcpy(dest, p+1, bufsz); /* Remove the extension */ strrsplt(dest, '.'); @@ -663,6 +682,7 @@ static void close_playlist_viewer(void) } playlist_close(viewer.playlist); } + viewer.is_open = false; } #if defined(HAVE_HOTKEY) || defined(HAVE_TAGCACHE) @@ -960,9 +980,12 @@ static bool open_playlist_viewer(const char* filename, struct gui_synclist *playlist_lists, bool reload, int *most_recent_selection) { + viewer.loading_tick = current_tick + HZ/3; push_current_activity(ACTIVITY_PLAYLISTVIEWER); - if (!playlist_viewer_init(&viewer, filename, reload, most_recent_selection)) + if (playlist_viewer_init(&viewer, filename, reload, most_recent_selection)) + viewer.is_open = true; + else return false; update_gui(playlist_lists, true); diff --git a/apps/plugin.c b/apps/plugin.c index 6dfb9ce765..6c689b21e6 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -49,6 +49,7 @@ #include "core_keymap.h" #include "language.h" #include "statusbar-skinned.h" +#include "panic.h" #if CONFIG_CHARGING #include "power.h" @@ -870,6 +871,10 @@ static const struct plugin_api rockbox_api = { /* new stuff at the end, sort into place next time the API gets incompatible */ + panicf, + gui_synclist_scroll_stop, + add_event_ex, + remove_event_ex, }; static int plugin_buffer_handle; @@ -890,7 +895,9 @@ int plugin_load(const char* plugin, const void* parameter) !strcmp("playing_time.rock", sepch + 1) || !strcmp("main_menu_config.rock", sepch + 1) || !strcmp("text_viewer.rock", sepch + 1) || - !strcmp("disktidy.rock", sepch + 1)); + !strcmp("view_text.rock", sepch + 1) || + !strcmp("disktidy.rock", sepch + 1) || + !strcmp("open_plugins.rock", sepch + 1)); if (current_plugin_handle) { @@ -1015,8 +1022,11 @@ int plugin_load(const char* plugin, const void* parameter) pop_current_activity_without_refresh(); if (get_current_activity() != ACTIVITY_WPS) { + skin_defer_rendering(true); FOR_NB_SCREENS(i) skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + skin_defer_rendering(false); + sb_skin_force_next_update(); } if (!pfn_tsr_exit) diff --git a/apps/plugin.h b/apps/plugin.h index 5dcbdcaee7..50fe1508ab 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -45,6 +45,8 @@ #undef strcmp #undef strncmp #undef strchr +#undef strstr +#undef strrchr #undef strtok_r #ifdef __APPLE__ #undef strncpy @@ -1024,6 +1026,18 @@ struct plugin_api { /* new stuff at the end, sort into place next time the API gets incompatible */ + void (*panicf)(const char *msg, ...); + void (*gui_synclist_scroll_stop)(struct gui_synclist *lists); + bool (*add_event_ex)(unsigned short id, bool oneshot, + void (*handler)(unsigned short id, + void *event_data, + void *user_data), + void *user_data); + void (*remove_event_ex)(unsigned short id, + void (*handler)(unsigned short id, + void *event_data, + void *user_data), + void *user_data); }; /* plugin header */ diff --git a/apps/plugins/bitmaps/pluginbitmaps.make b/apps/plugins/bitmaps/pluginbitmaps.make index 78294a5e8c..caf7d23535 100644 --- a/apps/plugins/bitmaps/pluginbitmaps.make +++ b/apps/plugins/bitmaps/pluginbitmaps.make @@ -58,4 +58,12 @@ $(BUILDDIR)/apps/plugins/bitmaps/remote_native/%.c: $(ROOTDIR)/apps/plugins/bitm $(SILENT)mkdir -p $(dir $@) $(PBMPINCDIR) $(call PRINTS,BMP2RB $( $@ +ifdef APP_TYPE +# Bitmaps must be explicitly Position independent to avoid linker warnings +$(BUILDDIR)/apps/plugins/bitmaps/native/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/plugins/bitmaps/mono/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/plugins/bitmaps/remote_mono/%.o: CFLAGS += -fPIC +$(BUILDDIR)/apps/plugins/bitmaps/remote_native/%.o: CFLAGS += -fPIC +endif + endif diff --git a/apps/plugins/calculator.c b/apps/plugins/calculator.c index 5e69e1f12a..27bb4bb438 100644 --- a/apps/plugins/calculator.c +++ b/apps/plugins/calculator.c @@ -519,6 +519,7 @@ F3: equal to "=" #define CALCULATOR_DOWN BUTTON_DOWN #define CALCULATOR_QUIT BUTTON_POWER #define CALCULATOR_INPUT BUTTON_PLAY +#define CALCULATOR_INPUT2 (BUTTON_SELECT|BUTTON_REL) #define CALCULATOR_CALC BUTTON_MENU #define CALCULATOR_CLEAR BUTTON_BACK @@ -1938,6 +1939,9 @@ Handle buttons on basic screen ----------------------------------------------------------------------- */ static void basicButtonsProcess(void){ switch (btn) { +#ifdef CALCULATOR_INPUT2 + case CALCULATOR_INPUT2: /*fallthrough*/ +#endif case CALCULATOR_INPUT: if (calStatus == cal_error && (CAL_BUTTON != btn_C) ) break; flashButton(); @@ -2041,6 +2045,9 @@ Handle buttons on scientific screen ----------------------------------------------------------------------- */ static void sciButtonsProcess(void){ switch (btn) { +#ifdef CALCULATOR_INPUT2 + case CALCULATOR_INPUT2: /*fallthrough*/ +#endif case CALCULATOR_INPUT: if (calStatus == cal_error && (CAL_BUTTON != sci_sci) ) break; flashButton(); @@ -2133,6 +2140,9 @@ static int handleButton(int button){ break; /* no unconditional break; here! */ #endif +#ifdef CALCULATOR_INPUT2 /* bypass pre button */ + case CALCULATOR_INPUT2: /*fallthrough*/ +#endif #ifdef CALCULATOR_OPERATORS case CALCULATOR_OPERATORS: #endif diff --git a/apps/plugins/chessbox/chessbox.make b/apps/plugins/chessbox/chessbox.make index c4b1b495e1..7fa0598eb9 100644 --- a/apps/plugins/chessbox/chessbox.make +++ b/apps/plugins/chessbox/chessbox.make @@ -19,7 +19,7 @@ ifeq ($(findstring YES, $(call preprocess, $(APPSDIR)/plugins/BUILD_OVERLAY)), Y ### lowmem targets ROCKS += $(CHESSBOX_OBJDIR)/chessbox.ovl CHESSBOX_OUTLDS = $(CHESSBOX_OBJDIR)/chessbox.link - CHESSBOX_OVLFLAGS = -T$(CHESSBOX_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map + CHESSBOX_OVLFLAGS = -T$(CHESSBOX_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map $(GLOBAL_LDOPTS) else ROCKS += $(CHESSBOX_OBJDIR)/chessbox.rock endif diff --git a/apps/plugins/crypt_firmware.c b/apps/plugins/crypt_firmware.c index 0d632befdb..3ccab090f5 100644 --- a/apps/plugins/crypt_firmware.c +++ b/apps/plugins/crypt_firmware.c @@ -103,18 +103,18 @@ static void aes_decrypt(void* data, uint32_t size) static void calc_hash(uint32_t* data, uint32_t size, uint32_t* result) { uint32_t ptr, i; - uint32_t config = 2; + uint32_t config = SHA1_CONFIG_GO; PWRCONEXT &= ~0x4; for (ptr = 0; ptr < (size >> 2); ptr += 0x10) { - for (i = 0; i < 0x10; i++) SHA1DATAIN[i] = data[ptr + i]; - SHA1CONFIG = config; - config = 0xA; - while ((SHA1CONFIG & 1) != 0); + for (i = 0; i < 0x10; i++) SHA1_DATA[i] = data[ptr + i]; + SHA1_CONFIG = config; + config = SHA1_CONFIG_CONT | SHA1_CONFIG_GO; + while ((SHA1_CONFIG & SHA1_CONFIG_BUSY) != 0); } - for (i = 0; i < 5; i ++) result[i] = SHA1RESULT[i]; + for (i = 0; i < 5; i ++) result[i] = SHA1_RESULT[i]; PWRCONEXT |= 0x4; } diff --git a/apps/plugins/dart_scorer.c b/apps/plugins/dart_scorer.c index dbf63be598..6e06d4d737 100644 --- a/apps/plugins/dart_scorer.c +++ b/apps/plugins/dart_scorer.c @@ -9,7 +9,7 @@ #define BUTTON_COLS 5 #define REC_HEIGHT (int)(LCD_HEIGHT / (BUTTON_ROWS + 1)) -#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS) +#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS) #define Y_7_POS (LCD_HEIGHT) /* Leave room for the border */ #define Y_6_POS (Y_7_POS - REC_HEIGHT) /* y6 = 63 */ @@ -48,6 +48,7 @@ #define NUM_PLAYERS 2 #define MAX_UNDO 100 +#define CENTRE_X (LCD_WIDTH/2) static const struct button_mapping *plugin_contexts[] = {pla_main_ctx}; @@ -72,12 +73,12 @@ static bool loaded = false; /* has a save been loaded? */ int btn_row, btn_col; /* current position index for button */ int prev_btn_row, prev_btn_col; /* previous cursor position */ unsigned char *buttonChar[6][5] = { - {"", "Single", "Double", "Triple", ""}, + {"", "x1", "x2", "x3", ""}, {"1", "2", "3", "4", "5"}, {"6", "7", "8", "9", "10"}, {"11", "12", "13", "14", "15"}, {"16", "17", "18", "19", "20"}, - {"", "Missed", "Bull", "Undo", ""}}; + {"", "Miss", "Bull", "Undo", ""}}; int modifier; static int do_dart_scorer_pause_menu(void); @@ -146,22 +147,34 @@ static void draw(void) { rb->lcd_clear_display(); - char buf[32]; + unsigned int w,h; + char buf[16]; - int x = 5; - int y = 10; - for (int i = 0; i < NUM_PLAYERS; ++i, x = x + 95) - { - char *turn_marker = (i == settings.turn) ? "*" : ""; - rb->snprintf(buf, sizeof(buf), "%sPlayer %d: %d", turn_marker, i + 1, settings.scores[i]); - rb->lcd_putsxy(x, y, buf); - } - int throws_x = (LCD_WIDTH / 2) - 10; - char throws_buf[3]; + char *turn_marker; + + /* player 1 score */ + turn_marker = (0 == settings.turn) ? "*" : ""; +#if LCD_HEIGHT <= 64 || LCD_WIDTH <= 96 + rb->snprintf(buf, sizeof(buf), "%sP1 %d", turn_marker, settings.scores[0]); +#else + rb->snprintf(buf, sizeof(buf), "%sPlayer 1: %d", turn_marker, settings.scores[0]); +#endif + rb->lcd_putsxy(0, 10, buf); + + /* player 2 score */ + turn_marker = (1 == settings.turn) ? "*" : ""; +#if LCD_HEIGHT <= 64 || LCD_WIDTH <= 96 + rb->snprintf(buf, sizeof(buf), "%sP2 %d", turn_marker, settings.scores[1]); +#else + rb->snprintf(buf, sizeof(buf), "%sPlayer 2: %d", turn_marker, settings.scores[1]); +#endif + rb->lcd_getstringsize(buf, &w, &h); + rb->lcd_putsxy(LCD_WIDTH - w, 10, buf); + + int throws_x = CENTRE_X - 5; for (int i = 0; i < settings.throws; ++i, throws_x += 5) { - rb->strcat(throws_buf, "1"); - rb->lcd_putsxy(throws_x, y, "|"); + rb->lcd_putsxy(throws_x, 10, "|"); } drawButtons(); @@ -324,11 +337,11 @@ static enum plugin_status do_game(bool newgame) switch (button) { case DARTS_SELECT: - if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "Single"))) + if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "x1"))) modifier = 1; - else if (!rb->strcmp(selected, "Double")) + else if (!rb->strcmp(selected, "x2")) modifier = 2; - else if (!rb->strcmp(selected, "Triple")) + else if (!rb->strcmp(selected, "x3")) modifier = 3; else if (!rb->strcmp(selected, "Undo")) { diff --git a/apps/plugins/doom/i_sound.c b/apps/plugins/doom/i_sound.c index 068aa915c5..7dd07bd802 100644 --- a/apps/plugins/doom/i_sound.c +++ b/apps/plugins/doom/i_sound.c @@ -474,6 +474,7 @@ void I_SubmitSound(void) void I_ShutdownSound(void) { + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); rb->mixer_set_frequency(HW_SAMPR_DEFAULT); } @@ -508,6 +509,7 @@ void I_InitSound() rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif rb->mixer_set_frequency(samplerate); + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ vol_lookup=malloc(128*256*sizeof(int)); diff --git a/apps/plugins/imageviewer/bmp/bmp.c b/apps/plugins/imageviewer/bmp/bmp.c index 3911917fec..8ad822892c 100644 --- a/apps/plugins/imageviewer/bmp/bmp.c +++ b/apps/plugins/imageviewer/bmp/bmp.c @@ -276,7 +276,8 @@ static int get_image(struct image_info *info, int frame, int ds) buf_images += size; buf_images_size -= size; - if (!iv->running_slideshow) + if (!iv->settings->hide_info && + !iv->running_slideshow) { rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height); rb->lcd_update(); diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c index 01683c8ace..45836134fe 100644 --- a/apps/plugins/imageviewer/imageviewer.c +++ b/apps/plugins/imageviewer/imageviewer.c @@ -246,28 +246,32 @@ static bool set_option_dithering(void) return false; } +MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE), + set_option_grayscale, NULL, Icon_NOICON); +MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING), + set_option_dithering, NULL, Icon_NOICON); + +#endif /* HAVE_LCD_COLOR */ + static bool set_option_hide_info(void) { rb->set_bool(rb->str(LANG_HIDE_INFO), &settings.hide_info); return false; } -MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE), - set_option_grayscale, NULL, Icon_NOICON); -MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING), - set_option_dithering, NULL, Icon_NOICON); MENUITEM_FUNCTION(hide_info_item, 0, ID2P(LANG_HIDE_INFO), set_option_hide_info, NULL, Icon_NOICON); MAKE_MENU(display_menu, ID2P(LANG_MENU_DISPLAY_OPTIONS), NULL, Icon_NOICON, +#ifdef HAVE_LCD_COLOR &grayscale_item, &dithering_item, +#endif /* HAVE_LCD_COLOR */ &hide_info_item); static void display_options(void) { rb->do_menu(&display_menu, NULL, NULL, false); } -#endif /* HAVE_LCD_COLOR */ static int show_menu(void) /* return 1 to quit */ { @@ -281,9 +285,7 @@ static int show_menu(void) /* return 1 to quit */ #ifdef USE_PLUG_BUF MIID_SHOW_PLAYBACK_MENU, #endif -#ifdef HAVE_LCD_COLOR MIID_DISPLAY_OPTIONS, -#endif MIID_QUIT, }; @@ -294,9 +296,7 @@ static int show_menu(void) /* return 1 to quit */ #ifdef USE_PLUG_BUF ID2P(LANG_PLAYBACK_CONTROL), #endif -#ifdef HAVE_LCD_COLOR ID2P(LANG_MENU_DISPLAY_OPTIONS), -#endif ID2P(LANG_MENU_QUIT)); static const struct opt_items slideshow[2] = { @@ -332,11 +332,9 @@ static int show_menu(void) /* return 1 to quit */ } break; #endif -#ifdef HAVE_LCD_COLOR case MIID_DISPLAY_OPTIONS: display_options(); break; -#endif case MIID_QUIT: return 1; break; @@ -361,12 +359,6 @@ static int show_menu(void) /* return 1 to quit */ } } #endif -#if LCD_DEPTH > 1 - rb->lcd_set_backdrop(NULL); - rb->lcd_set_foreground(LCD_WHITE); - rb->lcd_set_background(LCD_BLACK); -#endif - rb->lcd_clear_display(); return 0; } @@ -776,9 +768,22 @@ static int scroll_bmp(struct image_info *info, bool initial_frame) #ifdef USEGSLIB grey_show(false); /* switch off greyscale overlay */ #endif - if (show_menu() == 1) + FOR_NB_SCREENS(i) + rb->viewportmanager_theme_enable(i, true, NULL); + int ret = show_menu(); + FOR_NB_SCREENS(i) + rb->viewportmanager_theme_undo(i, false); + + if (ret == 1) return PLUGIN_OK; +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(NULL); + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); +#endif + rb->lcd_clear_display(); + #ifdef USEGSLIB grey_show(true); /* switch on greyscale overlay */ #else diff --git a/apps/plugins/lib/gcc-support.c b/apps/plugins/lib/gcc-support.c index f1fbe4ec58..53b01b9f01 100644 --- a/apps/plugins/lib/gcc-support.c +++ b/apps/plugins/lib/gcc-support.c @@ -34,6 +34,15 @@ void __aeabi_ldiv0(void) __attribute__((alias("__div0"))); #endif #endif +#if defined(USE_STACK_PROTECTOR) +const uint32_t __stack_chk_guard = 0x3BADC0DE; + +void __stack_chk_fail(void) +{ + rb->panicf("plugin smashed stack"); +} +#endif + void *memcpy(void *dest, const void *src, size_t n) { return rb->memcpy(dest, src, n); diff --git a/apps/plugins/lib/simple_viewer.c b/apps/plugins/lib/simple_viewer.c index e71efce753..27a30ef0b5 100644 --- a/apps/plugins/lib/simple_viewer.c +++ b/apps/plugins/lib/simple_viewer.c @@ -29,13 +29,16 @@ struct view_info { struct font* pf; struct viewport scrollbar_vp; /* viewport for scrollbar */ + struct viewport title_vp; struct viewport vp; + bool sbs_has_title; /* SBS comes with custom title area */ const char *title; const char *text; /* displayed text */ int display_lines; /* number of lines can be displayed */ int line_count; /* number of lines */ int line; /* current first line */ int start; /* possition of first line in text */ + bool ui_update_cb; }; static bool isbrchr(const unsigned char *str, int len) @@ -93,17 +96,19 @@ static void calc_line_count(struct view_info *info) { ptr = get_next_line(ptr, info); i++; - if (!scrollbar && i > info->display_lines) + if (!scrollbar && i > info->display_lines && + rb->global_settings->scrollbar != SCROLLBAR_OFF) { ptr = info->text; i = 0; info->scrollbar_vp = info->vp; - info->scrollbar_vp.width = rb->global_settings->scrollbar_width; + info->scrollbar_vp.width = rb->global_settings->scrollbar_width + 1; + info->scrollbar_vp.height = info->display_lines * info->pf->height; info->vp.width -= info->scrollbar_vp.width; - if (rb->global_settings->scrollbar != SCROLLBAR_RIGHT) - info->vp.x = info->scrollbar_vp.width; + if (rb->global_settings->scrollbar == SCROLLBAR_RIGHT) + info->scrollbar_vp.x = info->vp.x + info->vp.width; else - info->scrollbar_vp.x = info->vp.width; + info->vp.x += info->scrollbar_vp.width; scrollbar = true; } } @@ -137,8 +142,14 @@ static void calc_first_line(struct view_info *info, int line) static int init_view(struct view_info *info, const char *title, const char *text) { - rb->viewport_set_defaults(&info->vp, SCREEN_MAIN); - info->pf = rb->font_get(rb->screens[SCREEN_MAIN]->getuifont()); + struct screen* display = rb->screens[SCREEN_MAIN]; + int ui_font = display->getuifont(); + + display->set_viewport(&info->vp); + display->clear_viewport(); + + info->pf = rb->font_get(ui_font); + info->vp.font = ui_font; info->display_lines = info->vp.height / info->pf->height; info->title = title; @@ -146,17 +157,23 @@ static int init_view(struct view_info *info, info->line_count = 0; info->line = 0; info->start = 0; + info->ui_update_cb = false; - /* no title for small screens. */ - if (info->display_lines < 4) + if (!info->sbs_has_title) { - info->title = NULL; - } - else - { - info->display_lines--; - info->vp.y += info->pf->height; - info->vp.height -= info->pf->height; + /* no title for small screens. */ + if (info->display_lines < 4) + info->title = NULL; + else + { + info->display_lines--; + + info->title_vp = info->vp; + info->title_vp.height = info->pf->height; + + info->vp.height -= info->title_vp.height; + info->vp.y += info->title_vp.height; + } } calc_line_count(info); @@ -171,20 +188,19 @@ static void draw_text(struct view_info *info) int max_show, line; struct screen* display = rb->screens[SCREEN_MAIN]; - /* clear screen */ - display->clear_display(); - /* display title. */ - if(info->title) + if (info->title && !info->sbs_has_title) { - display->set_viewport(NULL); + display->set_viewport(&info->title_vp); display->puts(0, 0, info->title); } + display->set_viewport(&info->vp); + display->clear_viewport(); + max_show = MIN(info->line_count - info->line, info->display_lines); text = info->text + info->start; - display->set_viewport(&info->vp); for (line = 0; line < max_show; line++) { int len; @@ -197,17 +213,21 @@ static void draw_text(struct view_info *info) display->puts(0, line, output); text = ptr; } - if (info->line_count > info->display_lines) + if (info->line_count > info->display_lines && + rb->global_settings->scrollbar != SCROLLBAR_OFF) { display->set_viewport(&info->scrollbar_vp); - rb->gui_scrollbar_draw(display, (info->scrollbar_vp.width? 0: 1), 0, - info->scrollbar_vp.width - 1, info->scrollbar_vp.height, - info->line_count, info->line, info->line + max_show, - VERTICAL); - } + int margin = info->scrollbar_vp.width > 2 ? 2 : 0; + int x = rb->global_settings->scrollbar == SCROLLBAR_RIGHT ? margin : 0; + rb->gui_scrollbar_draw(display, x, 0, + info->scrollbar_vp.width - margin, info->scrollbar_vp.height, + info->line_count, info->line, info->line + max_show, VERTICAL); + } display->set_viewport(NULL); - display->update(); + if (!info->ui_update_cb) + display->update(); + info->ui_update_cb = false; } static void scroll_up(struct view_info *info, int n) @@ -248,6 +268,22 @@ static void scroll_to_bottom(struct view_info *info) draw_text(info); } +static void ui_update_cb(unsigned short id, void* param, void* user_data) +{ + (void)id; + (void)param; + struct view_info *info = (struct view_info *) user_data; + info->ui_update_cb = true; + draw_text(info); +} + +static void cleanup(void *parameter) +{ + struct view_info *info = (struct view_info *) parameter; + rb->remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, ui_update_cb, info); + rb->viewportmanager_theme_undo(SCREEN_MAIN, false); +} + int view_text(const char *title, const char *text) { struct view_info info; @@ -256,13 +292,24 @@ int view_text(const char *title, const char *text) }; int button; + info.sbs_has_title = rb->sb_set_persistent_title(title, Icon_NOICON, SCREEN_MAIN); + rb->viewportmanager_theme_enable(SCREEN_MAIN, true, &info.vp); init_view(&info, title, text); - draw_text(&info); + + /* handle themes that draw over the UI viewport */ + rb->add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, ui_update_cb, &info); + + /* skin engine needs to render title and redraw screen */ + if (info.sbs_has_title) + rb->send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); + else + draw_text(&info); /* wait for keypress */ while(1) { - button = pluginlib_getaction(TIMEOUT_BLOCK, view_contexts, + /* don't block, so the skin engine can redraw */ + button = pluginlib_getaction(HZ/4, view_contexts, ARRAYLEN(view_contexts)); switch (button) { @@ -270,7 +317,7 @@ int view_text(const char *title, const char *text) #if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \ || (CONFIG_KEYPAD == IPOD_3G_PAD) \ || (CONFIG_KEYPAD == IPOD_4G_PAD) - return PLUGIN_OK; + goto out; #endif case PLA_UP_REPEAT: #ifdef HAVE_SCROLLWHEEL @@ -291,7 +338,7 @@ int view_text(const char *title, const char *text) #if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \ || (CONFIG_KEYPAD == IPOD_3G_PAD) \ || (CONFIG_KEYPAD == IPOD_4G_PAD) - return PLUGIN_OK; + goto out; #endif scroll_up(&info, info.display_lines); break; @@ -307,13 +354,14 @@ int view_text(const char *title, const char *text) case PLA_SELECT: case PLA_EXIT: case PLA_CANCEL: - return PLUGIN_OK; + goto out; default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + if ((rb->default_event_handler_ex(button, cleanup, + &info) == SYS_USB_CONNECTED)) return PLUGIN_USB_CONNECTED; - break; } } - +out: + cleanup(&info); return PLUGIN_OK; } diff --git a/apps/plugins/lua/rockconf.h b/apps/plugins/lua/rockconf.h index d55b3d434f..15d3f16106 100644 --- a/apps/plugins/lua/rockconf.h +++ b/apps/plugins/lua/rockconf.h @@ -51,6 +51,10 @@ int splash_scroller(int timeout, const char* str); #define floor lfloor #define pow lpow +/* Just in case */ +#undef memchr +#undef strstr + /* Simple substitutions */ #define malloc tlsf_malloc #define realloc tlsf_realloc diff --git a/apps/plugins/lua/rocklib_img.c b/apps/plugins/lua/rocklib_img.c index c58a5b6681..df5e028283 100644 --- a/apps/plugins/lua/rocklib_img.c +++ b/apps/plugins/lua/rocklib_img.c @@ -349,7 +349,7 @@ static struct rocklua_image* rli_checktype(lua_State *L, int arg) } /* rli_checktype */ static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data, - int width, int height) + int width, int height, size_t *allocd) { /* rliimage is pushed on the stack it is up to you to pop it */ struct rocklua_image *img; @@ -365,10 +365,24 @@ static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data, n_elems = (size_t)(w_native * h_native); - if(alloc_data) /* if this a new image we need space for image data */ - sz_data = n_elems * sizeof(fb_data); + if(alloc_data) + {/* if this a new image we need space for image data */ + /* need even rows (see lcd-16bit-common.c for details) */ + sz_data = BM_SIZE(width,height,FORMAT_NATIVE,false); +#if LCD_DEPTH > 24 /* account for possible 4bit alpha per pixel */ + sz_data += ALIGN_UP(width, 2) * height / 2; +#endif +#if (LCD_DEPTH > 1) && defined(HAVE_BMP_SCALING) + if (width > BM_MAX_WIDTH) + sz_data += width*4; +#endif + sz_data = MAX(n_elems * sizeof(fb_data), sz_data); + } + if (allocd) + *allocd = sz_data; /* newuserdata pushes the userdata onto the stack */ + /*DEBUGF("rli alloc %d\n", sz_header + sz_data);*/ img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data); luaL_getmetatable(L, ROCKLUA_IMAGE); @@ -386,15 +400,15 @@ static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data, static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height) { /* rliimage is pushed on the stack it is up to you to pop it */ - struct rocklua_image *a = alloc_rlimage(L, false, width, height); + struct rocklua_image *a = alloc_rlimage(L, false, width, height, NULL); a->data = src; } /* rli_wrap */ -static inline fb_data* rli_alloc(lua_State *L, int width, int height) +static inline fb_data* rli_alloc(lua_State *L, int width, int height, size_t *allocd) { /* rliimage is pushed on the stack it is up to you to pop it */ - struct rocklua_image *a = alloc_rlimage(L, true, width, height); + struct rocklua_image *a = alloc_rlimage(L, true, width, height, allocd); a->data = &a->dummy[0][0]; /* ref to beginning of alloc'd img data */ @@ -995,7 +1009,7 @@ RLI_LUA rli_new(lua_State *L) luaL_argcheck(L, width > 0 && height > 0, (width <= 0) ? 1 : 2, ERR_IDX_RANGE); - rli_alloc(L, width, height); + rli_alloc(L, width, height, NULL); return 1; } @@ -1904,10 +1918,13 @@ RB_WRAP(read_bmp_file) int result = rb->read_bmp_file(filename, &bm, 0, format | FORMAT_RETURN_SIZE, NULL); + /*DEBUGF("read_bmp wants %d %d\n", result, BM_SIZE(bm.width, bm.height, format, false));*/ + if(result > 0) { - bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height); - if(rb->read_bmp_file(filename, &bm, result, format, NULL) < 0) + size_t sz_data; + bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height, &sz_data); + if(rb->read_bmp_file(filename, &bm, sz_data, format, NULL) < 0) { /* Error occured, drop newly allocated image from stack */ lua_pop(L, 1); diff --git a/apps/plugins/lua_scripts/rlimg.lua b/apps/plugins/lua_scripts/rlimg.lua index b3fa72426c..7299f4e207 100755 --- a/apps/plugins/lua_scripts/rlimg.lua +++ b/apps/plugins/lua_scripts/rlimg.lua @@ -274,11 +274,11 @@ function bounce_image(img) if IS_COLOR_TARGET then if bit.band(loops, 128) == 128 then - _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BOR) + _lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, _blit.BOR) _lcd:copy(screen_img, x, y, x, y, imgn:width(), imgn:height(), false, _blit.BDEQC, imgn:get(1,1)) else - _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BSNEC, imgn:get(1,1)) + _lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, _blit.BSNEC, imgn:get(1,1)) end else local blitop @@ -289,7 +289,7 @@ function bounce_image(img) blitop = _blit.BXOR end - _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, blitop, WHITE) + _lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, blitop, WHITE) end if hold < 1 then @@ -674,6 +674,7 @@ end -- rotate_image function flip_image(img) local blitop = _blit.BOR + local i = 1 local d = 0 local x, y, w, h @@ -689,7 +690,7 @@ function flip_image(img) --[[--Profiling code local timer = _timer.start()]] - while d >= 0 do + while d >= 0 and img do -- copy our flipped image onto the background if d == 0 then _lcd:copy(img, x, y, 1, 1, w, h, false, blitop) @@ -757,7 +758,7 @@ end -- draw_x function random_img(img) local min = _clr.set(0, 0, 0, 0) local max = _clr.set(-1, 255, 255, 255) - math.randomseed(rb.current_tick()) + --math.randomseed(rb.current_tick()) for x = 1, img:width() do for y = 1, img:height() do img:set(x, y, math.random(min, max)) @@ -896,9 +897,13 @@ function main_menu() [10] = long_text, [11] = function(RAINB) rainbow_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) + if rb.file_exists == nil or not rb.file_exists("/rainbow.bmp") then + _img_save(_LCD, "/rainbow.bmp") + end end, [12] = function(RANDM) random_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) + _img_save(_LCD, "/random.bmp") end, [13] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end, [14] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end, @@ -927,6 +932,8 @@ _timer("main") -- keep track of how long the program ran -- Clear the screen _lcd:clear(BLACK) +math.randomseed(rb.current_tick()) + if LCD_DEPTH > 1 then --draw a gradient using available colors if IS_COLOR_TARGET == true then diff --git a/apps/plugins/metronome.c b/apps/plugins/metronome.c index f7baccf84f..0379bc0164 100644 --- a/apps/plugins/metronome.c +++ b/apps/plugins/metronome.c @@ -787,7 +787,6 @@ static bool beating = false; /* A beat is/was playing and count needs to increas static int display_state = 0; /* Current display state code. */ static bool display_trigger = false; /* Draw display on next occasion */ -static bool sound_active = false; static bool sound_paused = true; /* global static buffer for messages in any situation */ @@ -1168,7 +1167,7 @@ static void timer_callback(void) if(minitick >= period) { minitick = 0; - if(!sound_active && !sound_paused && !tap_count) + if(!sound_paused && !tap_count) { sound_trigger = true; rb->reset_poweroff_timer(); @@ -1213,6 +1212,7 @@ static void cleanup(void) if(fd >= 0) rb->close(fd); metronome_pause(); + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); tweak_volume(0); rb->led(0); @@ -1561,6 +1561,7 @@ enum plugin_status plugin_start(const void* file) rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif rb->mixer_set_frequency(SAMPR_44); + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ if(file) { diff --git a/apps/plugins/mpegplayer/SOURCES b/apps/plugins/mpegplayer/SOURCES index 3fc079dfbd..c1d0afe4c2 100644 --- a/apps/plugins/mpegplayer/SOURCES +++ b/apps/plugins/mpegplayer/SOURCES @@ -8,7 +8,7 @@ libmpeg2/slice.c libmpeg2/idct_coldfire.S libmpeg2/motion_comp_coldfire_c.c libmpeg2/motion_comp_coldfire_s.S -#elif defined CPU_ARM +#elif defined CPU_ARM_CLASSIC #if ARM_ARCH >= 6 libmpeg2/idct_armv6.S #else diff --git a/apps/plugins/mpegplayer/libmpeg2/idct.c b/apps/plugins/mpegplayer/libmpeg2/idct.c index 7f0b9a3c12..e202da31d2 100644 --- a/apps/plugins/mpegplayer/libmpeg2/idct.c +++ b/apps/plugins/mpegplayer/libmpeg2/idct.c @@ -33,7 +33,7 @@ #include "attributes.h" #include "mpeg2_internal.h" -#if defined(CPU_COLDFIRE) || defined (CPU_ARM) +#if defined(CPU_COLDFIRE) || defined (CPU_ARM_CLASSIC) #define IDCT_ASM #endif diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c index 9da5c1f3a2..a5f8b07dcd 100644 --- a/apps/plugins/open_plugins.c +++ b/apps/plugins/open_plugins.c @@ -31,6 +31,14 @@ * with the parameters previously supplied */ +#if defined(DEBUG) || defined(SIMULATOR) + #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n") +#elif defined(ROCKBOX_HAS_LOGF) + #define logf rb->logf +#else + #define logf(...) do { } while(0) +#endif + #include "plugin.h" #include "lang_enum.h" #include "../open_plugin.h" @@ -46,16 +54,77 @@ #define MENU_ID_MAIN "0" #define MENU_ID_EDIT "1" -static int fd_dat; +#define MAX_ENTRIES 128 + +static int fd_dat = -1; static struct gui_synclist lists; -struct open_plugin_entry_t op_entry; +static struct open_plugin_entry_t op_entry; static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM; static const off_t op_entry_sz = sizeof(struct open_plugin_entry_t); /* we only need the names for the first menu so don't bother reading paths yet */ const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry); +/*static char op_names[MAX_ENTRIES][OPEN_PLUGIN_NAMESZ + 1] = {0};*/ + +/* Forward declarations*/ static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key); +static int import_from_config(const char* cfgfile); +static int export_to_config(const char* cfgfile); + +static bool hash_exists(uint32_t hash, bool remove) +{ + /* keeps track of hash entries + * supply hash = 0, remove = true to erase all entries + * supply hash = 0, remove = false; returns true if empty entries are available + * supply a hash, remove = false; returns false if the hash does not exist & adds hash to the db + * supply a hash, remove = true; returns true and removes the hash from the db + * returns false if hash is unique + */ + static uint32_t seen_hashes[MAX_ENTRIES] = {0}; + int i; + if (hash == 0) + { + if (remove) + { + logf("removing all hashes"); + for(i = 0; i < MAX_ENTRIES; i++) + seen_hashes[i] = 0; + } + return seen_hashes[MAX_ENTRIES - 1] == 0; /* true if empty entries exist */ + } + for(i = 0; i < MAX_ENTRIES; i++) + { + uint32_t current_hash = seen_hashes[i]; + if (current_hash == 0 && !remove) + { + logf("(%d) added new hash: %x", i, hash); + seen_hashes[i] = hash; + break; + } + if (current_hash == hash) + { + if (remove) + { + logf("(%d) remove hash: %x", i, hash); + for (int j = i + 1; j < MAX_ENTRIES; j++, i++) + { + /* move everything above down 1*/ + uint32_t next_hash = seen_hashes[j]; + seen_hashes[i] = next_hash; + if (next_hash == 0) + break; + } + if (i + 1 == MAX_ENTRIES) /* handle last entry */ + { + seen_hashes[i] = 0; + } + } + return true; + } + } + return false; /* hash is unique */ +} static bool _yesno_pop(const char* text) { @@ -87,6 +156,7 @@ static size_t pathbasename(const char *name, const char **nameptr) *nameptr = q; return r - q; } + static int op_entry_checksum(void) { if (op_entry.checksum != open_plugin_csum + @@ -134,7 +204,36 @@ static int op_entry_read_opx(const char *path) return ret; } -static void op_entry_export(int selection) +static void op_entry_config_export(void) +{ + int i = 0; + int len; + char buf[MAX_PATH + 1]; + + do + { + i++; + rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", ROCKBOX_DIR, "open_plugins", i, ".cfg"); + } while (i < 100 && rb->file_exists(buf)); + + if( rb->kbd_input(buf, MAX_PATH, NULL ) == 0 ) + { + len = rb->strlen(buf); + if(len > 4 && buf[len] != PATH_SEPCH && + rb->strcasecmp(&((buf)[len-4]), ".cfg") != 0) + { + rb->strcat(buf, ".cfg"); + } + + if (export_to_config(buf) > 0) + return; + } + + rb->splashf( 2*HZ, "Save Failed (%s)", buf ); + +} + +static void op_entry_export_opx(int selection) { int len; int fd = -1; @@ -257,19 +356,43 @@ static void op_entry_set_param(void) rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ); } +static int op_et_exclude_invalid(struct open_plugin_entry_t *op_entry, int item, void *data) +{ + (void)item; + (void)data; + if (op_entry->hash == 0 || op_entry->name[0] == '\0') + { + logf("Exclude entry %d - Bad hash", item); + return 0; + } + if (op_entry->checksum != open_plugin_csum + + (op_entry->lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY)) + { + logf("Exclude entry %d - Bad csum", item); + return 0; + } + if (!rb->file_exists(op_entry->path)) + { + logf("Exclude entry %d - Bad path '%s'", item, op_entry->path); + return 0; + } + logf("%s Include entry %d %s - hash %x", __func__, item, + (op_entry->lang_id >= 0 ? "[Builtin]" : "[User]"), op_entry->hash); + return 1; +} + static int op_et_exclude_hash(struct open_plugin_entry_t *op_entry, int item, void *data) { (void)item; - if (op_entry->hash == 0 || op_entry->name[0] == '\0') - return 0; - if (data) { uint32_t *hash = data; + if (op_entry->hash != *hash) - return 1; + return op_et_exclude_invalid(op_entry, item, data); } + logf("Exclude entry %d - hash %x", item, op_entry->hash); return 0; } @@ -279,11 +402,12 @@ static int op_et_exclude_builtin(struct open_plugin_entry_t *op_entry, int item, (void)data; if (op_entry->lang_id >= 0) + { + logf("Exclude entry %d - Builtin", item); return 0; - else if(op_entry->hash == 0 || op_entry->name[0] == '\0') - return 0; + } - return 1; + return op_et_exclude_invalid(op_entry, item, data); } static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, void *data) @@ -292,11 +416,11 @@ static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, vo (void)data; if (op_entry->lang_id < 0) + { + logf("Exclude entry %d - User", item); return 0; - else if (op_entry->hash == 0 || op_entry->name[0] == '\0') - return 0; - - return 1; + } + return op_et_exclude_invalid(op_entry, item, data); } static int op_entry_transfer(int fd, int fd_tmp, @@ -304,21 +428,82 @@ static int op_entry_transfer(int fd, int fd_tmp, void *data) { int entries = -1; + int skipped = 0; if (fd_tmp >= 0 && fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0) { entries = 0; while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz) { - if (compfn && compfn(&op_entry, entries, data) > 0 && op_entry_checksum() > 0) + if (compfn && compfn(&op_entry, entries + skipped, data) > 0 && op_entry_checksum() > 0) { rb->write(fd_tmp, &op_entry, op_entry_sz); entries++; } + else + skipped++; } + logf("%s %d entries %d skipped", __func__, entries, skipped); + } + else + { + logf("%s Error: fd %d, fd_tmp %d, seekfd: %d", __func__, + fd, fd_tmp, (int) rb->lseek(fd, 0, SEEK_SET)); } return entries + 1; } +#if 0 +static int op_entry_exists(int fd) +{ + logf("%s? %s - %s", __func__, op_entry.name, op_entry.path); + /* returns index of the entry that already exists if not found returns -1 */ + static struct open_plugin_entry_t op_entry_rd; + int entries = 0; + if (fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0) + { + while (rb->read(fd, &op_entry_rd, op_entry_sz) == op_entry_sz) + { + logf("rd: %s - %s", op_entry_rd.name, op_entry_rd.path); + if (rb->memcmp(&op_entry_rd, &op_entry, op_entry_sz) == 0) + { + logf("%s entry exists @ %d", op_entry_rd.name, entries); + return entries; + } + entries++; + } + } + logf("%s is unique in %d entries", op_entry.name, entries); + return -1; +} +#endif + +static bool browse_configs(void) +{ + int entries = 0; + char tmp_buf[MAX_PATH+1]; + + struct browse_context browse = { + .dirfilter = SHOW_CFG, + .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER, + .title = rb->str(LANG_CUSTOM_CFG), + .icon = Icon_Plugin, + .root = ROCKBOX_DIR, + .buf = tmp_buf, + .bufsize = sizeof(tmp_buf), + }; + + if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS) + { + logf("import from %s\n", tmp_buf); + if(rb->filetype_get_attr(tmp_buf) == FILE_ATTR_CFG) + { + entries = import_from_config(tmp_buf); + rb->splashf(HZ *2, "Imported %d entries", entries); + } + } + return (entries > 0); +} + static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key) { char buf[MAX_PATH]; @@ -358,8 +543,6 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha if (op_entry.name[0] == '\0' || op_entry.lang_id >= 0) rb->strlcpy(op_entry.name, pos, OPEN_PLUGIN_NAMESZ); - - if ((!parameter || parameter[0] == '\0') && fattr != FILE_ATTR_ROCK && fattr != FILE_ATTR_OPX) { rb->strlcpy(op_entry.param, plugin, OPEN_PLUGIN_BUFSZ); @@ -396,16 +579,28 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha } else if (parameter != op_entry.param) rb->strlcpy(op_entry.param, parameter, OPEN_PLUGIN_BUFSZ); + } - /* hash on the parameter path if it is a file */ - if (op_entry.lang_id <0 && (key == op_entry.path || key == NULL) && - rb->file_exists(op_entry.param)) + op_entry_set_checksum(); + + if (!use_key) + { + if (op_entry.lang_id <0) { - open_plugin_get_hash(op_entry.param, &newhash); - op_entry.hash = newhash; + open_plugin_get_hash(op_entry.name, &hash); + op_entry.hash = hash; + } + if (hash_exists(op_entry.hash, false) && + _yesno_pop(ID2P(LANG_REALLY_OVERWRITE)) == false) + { + logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x", + __func__, key, op_entry.lang_id, op_entry.hash); + rb->close(fd_tmp); + rb->remove(OPEN_PLUGIN_DAT ".tmp"); + return op_entry.hash; } } - op_entry_set_checksum(); + rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */ } else if(op_entry_read_opx(plugin) == op_entry_sz) @@ -414,13 +609,23 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha if (fd_tmp < 0) return 0; - if (op_entry.lang_id <0 && rb->file_exists(op_entry.param)) - open_plugin_get_hash(op_entry.param, &hash); - else - open_plugin_get_hash(op_entry.path, &hash); + if (op_entry.lang_id <0) + { + open_plugin_get_hash(op_entry.name, &hash); + } op_entry.hash = hash; op_entry_set_checksum(); + + if (hash_exists(op_entry.hash, false)) + { + logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x", + __func__, key, op_entry.lang_id, op_entry.hash); + rb->close(fd_tmp); + rb->remove(OPEN_PLUGIN_DAT ".tmp"); + return op_entry.hash; + } + rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */ } else @@ -431,6 +636,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha } } + /*logf("OP add key: '%s' lang id: %d hash: %x", op_entry.name, op_entry.lang_id, op_entry.hash);*/ if (op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_hash, &hash) > 0) { rb->close(fd_tmp); @@ -451,6 +657,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha void op_entry_browse_add(int selection) { + logf("%s", __func__); char* key; op_entry_read(fd_dat, selection, op_entry_sz); if (op_entry_set_path() > 0) @@ -466,7 +673,6 @@ void op_entry_browse_add(int selection) static void op_entry_remove(int selection) { - int entries = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz; int32_t hash = 0; int lang_id = -1; @@ -486,6 +692,7 @@ static void op_entry_remove(int selection) rb->global_settings->start_in_screen = GO_TO_ROOT + 2; /* default */ } } + hash_exists(op_entry.hash, true); rb->memset(&op_entry, 0, op_entry_sz); op_entry.lang_id = lang_id; op_entry.hash = hash; @@ -685,9 +892,9 @@ static int context_menu_cb(int action, /*Run, Edit, Remove, Export, Blank, Import, Add, Back*/ switch(selection) { - case 0:case 1:case 2:case 3:case 5: + case 0:case 1:case 2:case 3:case 4:case 5:case 7: return ACTION_STD_OK; - case 4: /*blank*/ + case 6: /*blank*/ break; default: return ACTION_STD_CANCEL; @@ -709,6 +916,9 @@ static void edit_menu(int selection) if (!op_entry_read(fd_dat, selection, op_entry_sz)) return; + if (op_entry.hash != 0) /* remove old hash */ + hash_exists(op_entry.hash, true); + uint32_t crc = rb->crc_32(&op_entry, op_entry_sz, 0xffffffff); synclist_set(MENU_ID_EDIT, 2, 8, 2); @@ -762,6 +972,8 @@ static void edit_menu(int selection) op_entry_add_path(NULL, op_entry.path, param, false); fd_dat = rb->open(OPEN_PLUGIN_DAT, O_RDWR, 0666); } + else + hash_exists(op_entry.hash, false); } static int context_menu(int selection) @@ -771,9 +983,11 @@ static int context_menu(int selection) { MENUITEM_STRINGLIST(menu, op_entry.name, context_menu_cb, ID2P(LANG_RUN), ID2P(LANG_EDIT), ID2P(LANG_REMOVE), ID2P(LANG_EXPORT), - ID2P(VOICE_BLANK), ID2P(LANG_ADD), ID2P(LANG_BACK)); + ID2P(LANG_SAVE_SETTINGS), ID2P(LANG_CUSTOM_CFG), ID2P(VOICE_BLANK), + ID2P(LANG_ADD), ID2P(LANG_BACK)); selected_item = rb->do_menu(&menu, 0, NULL, false); + logf("%s %d", __func__, selected_item); switch (selected_item) { case 0: /*run*/ @@ -785,11 +999,20 @@ static int context_menu(int selection) op_entry_remove(selection); break; case 3: /*export*/ - op_entry_export(selection); + op_entry_export_opx(selection); break; - case 4: /*blank*/ + case 4: /* export to cfg */ + op_entry_config_export(); + case 5: /*import from cfg*/ + if (browse_configs()) + { + rb->plugin_open(rb->plugin_get_current_filename(), "\0"); + return OP_PLUGIN_RESTART; + } break; - case 5: /*add*/ + case 6: /*blank*/ + break; + case 7: /*add*/ op_entry_browse_add(-1); rb->plugin_open(rb->plugin_get_current_filename(), "\0"); return OP_PLUGIN_RESTART; @@ -802,6 +1025,296 @@ static int context_menu(int selection) return PLUGIN_ERROR; } +/* Read up to buffer_size chars from a quoted string + * within fd into buffer and return number of bytes read. + * A string starts with a quote character (single or double quote) + * and ends with a matching closing quote. Neither opening or closing quotes + * are stored in buffer. Too small buf or no opening and closing quote is an error. + * If an error occurs, -1 is returned (and buffer is cleared). + * If buffer too small file will still be advanced to the closing quote/LF/EOF + */ +static int read_quoted_string(int fd, char* buffer, int buffer_size) +{ + int pos = 0; + char ch; + char quote = '\0'; + /*logf("%s fd: %d bufsz: %d", __func__, fd, buffer_size);*/ + while (rb->read(fd, &ch, 1) == 1) + { + if (ch == '\n') /*LF marks end of line*/ + { + rb->lseek(fd, -1, SEEK_CUR);/*back up cursor to LF so calling fn sees it*/ + break; /* fail */ + } + if (quote == '\0' && + (ch == '\'' || ch == '"')) /*handle single or double quotes*/ + { + quote = ch; + } + else if (quote != '\0') + { + if (ch == quote) + { + if (pos < buffer_size) + { + buffer[pos] = '\0'; /*end quote*/ + return pos + 1; + } + break; /*fail*/ + } + if (pos < buffer_size) + { + buffer[pos] = ch; /*inside quote*/ + pos++; + } + } + } + + /*fail*/ + /*logf("Error %s", __func__);*/ + buffer[0] = '\0'; + return -1; +} + +static int lang_english_to_id(const char *english) +{ + int i; + unsigned char *ptr; + size_t ptrlen, len = rb->strlen(english); + for (i = 0; i < LANG_LAST_INDEX_IN_ARRAY; i++) { + ptr = rb->language_strings[i]; + ptrlen = rb->strlen((char *)ptr); + if ((ptrlen == len) && rb->memcmp(ptr, english, ptrlen) == 0) + return i; + } + return -1; +} + +static bool op_entry_config_import(int cfg_fd, int fd_tmp) +{ + /* NOTE: assumes cfg_fd is valid */ + /*"key", "name", "path", "param"*/ + /*"Start Screen", "logo.rock", "/.rockbox/rocks/demos/logo.rock", ""*/ + /*"[USER]", "text_viewer.rock", "/.rockbox/rocks/viewers/text_viewer.rock", "/text.txt"*/ + rb->memset(&op_entry, 0, op_entry_sz); + + static char errmsg[MAX_PATH]; + static char keybuf[MAX_PATH]; + int32_t lang_id; + uint32_t hash; + + if (read_quoted_string(cfg_fd, keybuf, sizeof(keybuf)) < 0) + { + logf("%s error: importing key entry @ %d", __func__, 0); + rb->snprintf(errmsg, sizeof(errmsg), "importing key entry @ %d", 1); + rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg); + return false; /* fail */ + } + + lang_id = lang_english_to_id(keybuf); + if (lang_id < 0) + { + int rd = read_quoted_string(cfg_fd, keybuf, sizeof(keybuf)); /* grab name field */ + if(rd < 0) + { + logf("%s error: importing key entry @ %d", __func__, 1); + rb->snprintf(errmsg, sizeof(errmsg), "importing key entry @ %d", 1); + rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg); + return false; /* fail */ + } + rb->lseek(cfg_fd, -(rd+2), SEEK_CUR); /* restore position to read name again */ + } + + int i, bufsz; + char *field[3] = {op_entry.name, op_entry.path, op_entry.param}; + for (i = 0, bufsz = OPEN_PLUGIN_NAMESZ; i < (int)ARRAYLEN(field); i++) + { + if (read_quoted_string(cfg_fd, field[i], bufsz) < 0) + { + logf("%s error: importing entry @ %d", __func__, i); + logf("OP import key: '%s' name: '%s' '%s' '%s'", keybuf, + op_entry.name, op_entry.path, op_entry.param); + rb->memset(&op_entry, 0, op_entry_sz); + rb->snprintf(errmsg, sizeof(errmsg), "importing entry %s @ %d", keybuf, i); + rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg); + return false; /* fail */ + } + bufsz = OPEN_PLUGIN_BUFSZ; + } + + if (!rb->file_exists(op_entry.path)) + { + logf("%s error: '%s' '%s' does not exist", __func__, keybuf, op_entry.path); + rb->splashf(HZ*2, ID2P(LANG_PLUGIN_CANT_OPEN), op_entry.path); + return false; /* fail */ + } + + if (lang_id <0) + { + open_plugin_get_hash(op_entry.name, &hash); + + } + else + { + open_plugin_get_hash(keybuf, &hash); + } + + op_entry.hash = hash; + op_entry.lang_id = lang_id; + + /*logf("OP import key: '%s' name: '%s' '%s' '%s'", keybuf, + op_entry.name, op_entry.path, op_entry.param);*/ + + op_entry_set_checksum(); + + if (fd_tmp >= 0 && fd_dat >= 0) + { + if (hash_exists(op_entry.hash, false)) + { + logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x", + __func__, keybuf, lang_id, hash); + return false; + } + + logf("writing to tmp: %s - %s", op_entry.name, op_entry.path); + + if (rb->write(fd_tmp, &op_entry, op_entry_sz) != op_entry_sz)/* add new entry */ + return false; + } + else + { + logf("%s error: bad fd dat: %d tmp: %d", __func__, fd_dat, fd_tmp); + return false; + } + + return true; +} + +static int import_from_config(const char* cfgfile) +{ + logf("%s() %s\r\n", __func__, cfgfile); + int fd; + char ch; + int entries = 0; + static char line[sizeof("openplugin:")]; + + fd = rb->open_utf8(cfgfile, O_RDONLY); + if (fd < 0) + return -1; + + int fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd_tmp < 0) + { + logf("%s error: can not open '%s'", __func__, OPEN_PLUGIN_DAT ".tmp"); + return -1; + } + + while ((rb->read(fd, line, sizeof line) - 1) == sizeof(line) - 1) + { + if (rb->strncasecmp(line, "openplugin:", sizeof("openplugin:") -1) == 0) + { + if (op_entry_config_import(fd, fd_tmp)) + entries++; + } + + while (rb->read(fd, &ch, 1) == 1) /* continue reading till EOL */ + { + if (ch == '\n') + break; + } + } /* while(...) */ + + rb->close(fd); +#if 1 + if (entries > 0 && op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_user, NULL) > 0 && + op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_builtin, NULL) > 0) +#else + if (entries > 0 && op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_invalid, 0) >= 0) +#endif + { + logf("%s imported %d entries", __func__, entries); + rb->close(fd_tmp); + rb->close(fd_dat); + fd_dat = -1; + rb->remove(OPEN_PLUGIN_DAT); + rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT); + } + else + { + logf("%s error: can not transfer entries", __func__); + if (entries > 0) + { + logf("%s error: can not transfer entries to '%s'", __func__, OPEN_PLUGIN_DAT ".tmp"); + entries = -1; + } + rb->close(fd_tmp); + rb->remove(OPEN_PLUGIN_DAT ".tmp"); + } + + return entries; +} + +static int export_to_config(const char* cfgfile) +{ + logf("%s() %s\r\n", __func__, cfgfile); + int cfg_fd; + char *lang_name; + int fd; + int index = 0; + + cfg_fd = rb->open_utf8(cfgfile, O_WRONLY | O_CREAT | O_TRUNC); + if (cfg_fd < 0) + return index; + + fd = rb->open_utf8(OPEN_PLUGIN_DAT, O_RDONLY); + + if (fd < 0) + { + logf("%s error: opening %s", __func__, OPEN_PLUGIN_DAT); + rb->close(cfg_fd); + rb->remove(cfgfile); + return index; /* OPEN_PLUGIN_NOT_FOUND */ + } + while (op_entry_read(fd, index, op_entry_sz)) + { + index++; + /* don't save the LANG_OPEN_PLUGIN entry -- it is for internal use */ + if (op_entry.lang_id == LANG_OPEN_PLUGIN) + { + continue; + } + if (op_entry.lang_id >=0) + lang_name = rb->str(op_entry.lang_id); + else + lang_name = "[USER]"; /* needs to be an invalid lang string */ + bool dblquote = rb->strchr(op_entry.name, '"') != NULL || + rb->strchr(op_entry.param, '"') != NULL; + + const char* fmtstr = "%s: \"%s\", \"%s\", \"%s\", \"%s\"\n"; + + if (dblquote) /* if using double quotes export with single quotes */ + { + fmtstr = "%s: '%s', '%s', '%s', '%s'\n"; + } + rb->fdprintf(cfg_fd,fmtstr, OPEN_PLUGIN_CFGNAME, + lang_name, op_entry.name, op_entry.path, op_entry.param); + + logf("openplugin[%d]: \"%s\", \"%s\", \"%s\", \"%s\"\n lang id: %d hash: %x, csum: %x\n", + index, lang_name, op_entry.name, op_entry.path, op_entry.param, + op_entry.lang_id, op_entry.hash, op_entry.checksum); + } + rb->close(fd); + rb->close(cfg_fd); + logf("%s exported %d entries", __func__, index); + if (index == 0) + { + /* Nothing to export */ + rb->remove(cfgfile); + } + + return index; +} + enum plugin_status plugin_start(const void* parameter) { int ret = PLUGIN_OK; @@ -817,11 +1330,20 @@ enum plugin_status plugin_start(const void* parameter) const int creat_flags = O_RDWR | O_CREAT; reopen_datfile: + fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666); + + hash_exists(0, true); + while (rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz) + { + hash_exists(op_entry.hash, false); + } + if (!fd_dat) exit = true; items = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz; + if (parameter) { path = (char*)parameter; @@ -833,13 +1355,18 @@ reopen_datfile: parameter = NULL; op_entry_browse_add(-1); rb->close(fd_dat); + fd_dat = -1; goto reopen_datfile; } - } + if(rb->filetype_get_attr(path) == FILE_ATTR_CFG) + { + int ret = import_from_config(path); + rb->close(fd_dat); + if (ret >= 0) + return PLUGIN_OK; + return PLUGIN_ERROR; + } - if (parameter) - { - path = (char*)parameter; res = op_entry_read_opx(path); if (res >= 0) { @@ -910,6 +1437,7 @@ reopen_datfile: if (op_entry_add_path(rb->str(LANG_ADD), cur_filename, "-add", true)) { rb->close(fd_dat); + fd_dat = -1; parameter = NULL; goto reopen_datfile; } @@ -917,8 +1445,6 @@ reopen_datfile: return PLUGIN_ERROR; } - - if (!exit) { synclist_set(MENU_ID_MAIN, selection, items, 1); @@ -947,7 +1473,7 @@ reopen_datfile: rb->gui_synclist_draw(&lists); break; } - /* Inentional fallthrough */ + /* fallthrough */ case ACTION_STD_OK: if (op_entry_read(fd_dat, selection, op_entry_sz)) { @@ -962,6 +1488,14 @@ reopen_datfile: exit = true; break; } + default: + if (rb->default_event_handler(action) == SYS_USB_CONNECTED) + { + op_entry_remove_empty(); + rb->close(fd_dat); + return PLUGIN_USB_CONNECTED; + } + break; } } op_entry_remove_empty(); diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c index e024c6d493..a709c78a9b 100644 --- a/apps/plugins/pacbox/pacbox.c +++ b/apps/plugins/pacbox/pacbox.c @@ -411,6 +411,7 @@ static void start_sound(void) .get_more = get_more, }; rb->mixer_set_frequency(caps->samprs[sr_index]); + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, &cbs, NULL, 0); sound_playing = true; @@ -424,6 +425,7 @@ static void stop_sound(void) if (!sound_playing) return; + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); rb->mixer_set_frequency(HW_SAMPR_DEFAULT); diff --git a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c index fd3c764645..94779e6123 100644 --- a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c +++ b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c @@ -65,6 +65,9 @@ void rockbox_open_audio(int rate) /* Set sample rate of the audio buffer. */ rb->mixer_set_frequency(rate); + /* Be sure channel is audible */ + rb->pcmbuf_fade(false, true); + /* Initialize output buffer. */ for(i = 0; i < OUTBUFSIZE; i++) outbuf[i].fill = 0; @@ -77,6 +80,9 @@ void rockbox_open_audio(int rate) /* Close audio. */ void rockbox_close_audio(void) { + /* Mute channel */ + rb->pcmbuf_fade(false, false); + /* Stop playback. */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); diff --git a/apps/plugins/pitch_screen.c b/apps/plugins/pitch_screen.c index 4af34fed3b..62cb170408 100644 --- a/apps/plugins/pitch_screen.c +++ b/apps/plugins/pitch_screen.c @@ -1106,9 +1106,9 @@ int gui_syncpitchscreen_run(void) /* when needed */ new_speed = 0; } + rb->yield(); } - //rb->pcmbuf_set_low_latency(false); //pop_current_activity(); /* Clean up */ @@ -1215,6 +1215,7 @@ enum plugin_status plugin_start(const void* parameter) * -s=90 sets speed to 90% if timestrech is enabled * -k=true -k1 enables time stretch -k0 -kf-kn disables */ + enum plugin_status ret = PLUGIN_OK; bool gui = false; rb->pcmbuf_set_low_latency(true); @@ -1274,7 +1275,7 @@ enum plugin_status plugin_start(const void* parameter) } if (gui && gui_syncpitchscreen_run() == 1) - return PLUGIN_USB_CONNECTED; + ret = PLUGIN_USB_CONNECTED; rb->pcmbuf_set_low_latency(false); - return PLUGIN_OK; + return ret; } diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c index 316b33292a..f61f8bcabb 100644 --- a/apps/plugins/properties.c +++ b/apps/plugins/properties.c @@ -187,7 +187,8 @@ static int browse_file_or_dir(struct dir_stats *stats) continue; switch(button) { - case ACTION_STD_OK:; + case ACTION_STD_OK: + rb->gui_synclist_scroll_stop(&properties_lists); int sel_pos = rb->gui_synclist_get_sel_pos(&properties_lists); /* "Show Track Info..." selected? */ @@ -197,17 +198,10 @@ static int browse_file_or_dir(struct dir_stats *stats) return -1; else { + const unsigned char* const *props = (props_type == PROPS_DIR) ? + props_dir : props_file; /* Display field in fullscreen */ - FOR_NB_SCREENS(i) - rb->viewportmanager_theme_enable(i, false, NULL); - if (props_type == PROPS_DIR) - view_text((char *) p2str(props_dir[sel_pos]), - (char *) props_dir[sel_pos + 1]); - else - view_text((char *) p2str(props_file[sel_pos]), - (char *) props_file[sel_pos + 1]); - FOR_NB_SCREENS(i) - rb->viewportmanager_theme_undo(i, false); + view_text((char *) p2str(props[sel_pos]), props[sel_pos + 1]); rb->gui_synclist_set_title(&properties_lists, rb->str(props_type == PROPS_DIR ? @@ -335,6 +329,8 @@ enum plugin_status plugin_start(const void* parameter) { static struct dir_stats stats; const char *file = parameter; + static struct viewport ui_vp; + #ifdef HAVE_TOUCHSCREEN rb->touchscreen_set_mode(rb->global_settings->touch_mode); #endif @@ -346,6 +342,17 @@ enum plugin_status plugin_start(const void* parameter) return PLUGIN_OK; } + /* erase background behind progress bar to prevent glitches + for themes adjusting viewport for context menu activity */ + if (props_type != PROPS_DIR) + { + struct screen* display = rb->screens[SCREEN_MAIN]; + rb->viewport_set_defaults(&ui_vp, SCREEN_MAIN); + struct viewport *last_vp = display->set_viewport(&ui_vp); + display->clear_viewport(); + display->set_viewport(last_vp); + } + if (props_type == PROPS_MUL_ID3) ret = assemble_track_info(NULL, NULL); else if (props_type != PROPS_ID3) diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c index 172207b63b..452f647954 100644 --- a/apps/plugins/puzzles/rockbox.c +++ b/apps/plugins/puzzles/rockbox.c @@ -3066,6 +3066,7 @@ static void tune_input(const char *name) "Signpost", "Slide", "Untangle", + NULL }; input_settings.sticky_mouse = string_in_list(name, sticky_mouse_games); diff --git a/apps/plugins/rockboy/rbsound.c b/apps/plugins/rockboy/rbsound.c index 7d10dbdb83..7e06f5a0aa 100644 --- a/apps/plugins/rockboy/rbsound.c +++ b/apps/plugins/rockboy/rbsound.c @@ -56,12 +56,14 @@ void rockboy_pcm_init(void) #endif rb->mixer_set_frequency(pcm.hz); /* 44100 22050 11025 */ + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ } void rockboy_pcm_close(void) { memset(&pcm, 0, sizeof pcm); newly_started = true; + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); rb->mixer_set_frequency(HW_SAMPR_DEFAULT); } diff --git a/apps/plugins/rockboy/rockboy.make b/apps/plugins/rockboy/rockboy.make index ba8ffe2390..190f1ac59f 100644 --- a/apps/plugins/rockboy/rockboy.make +++ b/apps/plugins/rockboy/rockboy.make @@ -19,7 +19,7 @@ ifeq ($(findstring YES, $(call preprocess, $(APPSDIR)/plugins/BUILD_OVERLAY)), Y ## lowmem targets ROCKS += $(ROCKBOY_OBJDIR)/rockboy.ovl ROCKBOY_OUTLDS = $(ROCKBOY_OBJDIR)/rockboy.link - ROCKBOY_OVLFLAGS = -T$(ROCKBOY_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map + ROCKBOY_OVLFLAGS = -T$(ROCKBOY_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map $(GLOBAL_LDOPTS) else ROCKS += $(ROCKBOY_OBJDIR)/rockboy.rock endif diff --git a/apps/plugins/sdl/SDL_mixer/wavestream.c b/apps/plugins/sdl/SDL_mixer/wavestream.c index 489863908f..8dbb2a934a 100644 --- a/apps/plugins/sdl/SDL_mixer/wavestream.c +++ b/apps/plugins/sdl/SDL_mixer/wavestream.c @@ -286,7 +286,7 @@ static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec, /* WAV magic header */ Uint32 RIFFchunk; -// Uint32 wavelen; + Uint32 wavelen; Uint32 WAVEmagic; /* FMT chunk */ @@ -296,8 +296,12 @@ static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec, /* Check the magic header */ RIFFchunk = SDL_ReadLE32(src); -// wavelen = SDL_ReadLE32(src); + wavelen = SDL_ReadLE32(src); WAVEmagic = SDL_ReadLE32(src); + + /* Unused */ + (void)wavelen; + if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) { Mix_SetError("Unrecognized file type (not WAVE)"); was_error = 1; diff --git a/apps/plugins/sdl/progs/quake/draw.c b/apps/plugins/sdl/progs/quake/draw.c index c870e6baba..5489810e1f 100644 --- a/apps/plugins/sdl/progs/quake/draw.c +++ b/apps/plugins/sdl/progs/quake/draw.c @@ -548,7 +548,7 @@ void Draw_ConsoleBackground (int lines) conback = Draw_CachePic ("gfx/conback.lmp"); dest = conback->data + 320 - 43 + 320*186; - sprintf (ver, "%.2f", (float)VERSION); + snprintf (ver, sizeof(ver), "%.2f", (float)VERSION); for (x=0 ; xpcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); if ( this->hidden->mixbuf != NULL ) { SDL_FreeAudioMem(this->hidden->mixbuf); @@ -260,6 +261,7 @@ static int ROCKBOXAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) LOGF("samplerate %d", spec->freq); rb->mixer_set_frequency(spec->freq); + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ /* Allocate mixing buffer */ this->hidden->mixlen = spec->size; diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c index dfaea58183..e3a0b0c420 100644 --- a/apps/plugins/test_sampr.c +++ b/apps/plugins/test_sampr.c @@ -214,6 +214,7 @@ static void play_tone(bool volume_set) #if INPUT_SRC_CAPS != 0 /* Select playback */ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); + rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif #ifdef HAVE_ADJUSTABLE_CPU_FREQ @@ -221,11 +222,7 @@ static void play_tone(bool volume_set) #endif rb->mixer_set_frequency(hw_sampr); - -#if INPUT_SRC_CAPS != 0 - /* Recordable targets can play back from other sources */ - rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); -#endif + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ gen_quit = false; output_clear(); @@ -263,6 +260,7 @@ static void play_tone(bool volume_set) rb->thread_wait(gen_thread_id); + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); #ifdef HAVE_ADJUSTABLE_CPU_FREQ diff --git a/apps/plugins/xrick/system/syssnd_rockbox.c b/apps/plugins/xrick/system/syssnd_rockbox.c index ec8886f881..63cddbd10f 100644 --- a/apps/plugins/xrick/system/syssnd_rockbox.c +++ b/apps/plugins/xrick/system/syssnd_rockbox.c @@ -257,6 +257,9 @@ bool syssnd_init(void) rb->mixer_set_frequency(HW_FREQ_44); + /* Be sure channel is audible */ + rb->pcmbuf_fade(false, true); + rb->memset(channels, 0, sizeof(channels)); rb->memset(mixBuffers, 0, sizeof(mixBuffers)); @@ -280,6 +283,9 @@ void syssnd_shutdown(void) return; } + /* Mute channel */ + rb->pcmbuf_fade(false, false); + /* Stop playback. */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); diff --git a/apps/plugins/xworld/sys.c b/apps/plugins/xworld/sys.c index a8c2d695fe..f51d7e3357 100644 --- a/apps/plugins/xworld/sys.c +++ b/apps/plugins/xworld/sys.c @@ -1032,12 +1032,14 @@ void sys_startAudio(struct System* sys, AudioCallback callback, void *param) static const struct mixer_play_cbs cbs = { .get_more = get_more, }; + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, &cbs, NULL, 0); } void sys_stopAudio(struct System* sys) { (void) sys; + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); } diff --git a/apps/plugins/zxbox/spsound.c b/apps/plugins/zxbox/spsound.c index 4b38e68171..28f65c5fe8 100644 --- a/apps/plugins/zxbox/spsound.c +++ b/apps/plugins/zxbox/spsound.c @@ -112,12 +112,14 @@ static void open_snd(void) rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif rb->mixer_set_frequency(SAMPR_44); + rb->pcmbuf_fade(false, true); /* Be sure channel is audible */ } static void close_snd(int normal) { (void)normal; sound_avail = 0; + rb->pcmbuf_fade(false, false); /* Mute channel */ rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); } diff --git a/apps/screens.c b/apps/screens.c index 6cbd44c3d4..e6031d8f57 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -827,6 +827,7 @@ refresh_info: { if (key == ACTION_STD_OK) { + gui_synclist_scroll_stop(&id3_lists); int header_id = id3_headers[info.info_id[id3_lists.selected_item/2]]; char* title_and_text[2]; title_and_text[0] = str(header_id); @@ -835,13 +836,7 @@ refresh_info: title_and_text[1] = (char*)id3_get_or_speak_info(id3_lists.selected_item+1,&info, buffer, sizeof(buffer), false); if (view_text) - { - FOR_NB_SCREENS(i) - viewportmanager_theme_enable(i, false, NULL); view_text(title_and_text[0], title_and_text[1]); - FOR_NB_SCREENS(i) - viewportmanager_theme_undo(i, false); - } else plugin_load(VIEWERS_DIR"/view_text.rock", title_and_text); gui_synclist_set_title(&id3_lists, str(LANG_TRACK_INFO), NOICON); diff --git a/apps/settings.c b/apps/settings.c index 7ed4257084..a87844ac24 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -405,7 +405,7 @@ bool settings_load_config(const char* file, bool apply) int fd; char line[128]; bool theme_changed = false; - + bool import_open_plugins = false; fd = open_utf8(file, O_RDONLY); if (fd < 0) return false; @@ -418,17 +418,25 @@ bool settings_load_config(const char* file, bool apply) if (!string_to_cfg(name, value, &theme_changed)) { -#ifndef __PCTOOL__ /* if we are here then name was not a valid setting */ - if (!strcmp(name, "openplugin")) + if (strcmp(name, OPEN_PLUGIN_CFGNAME) == 0) { - open_plugin_import(value); + import_open_plugins = true; + char ch; + while (read(fd, &ch, 1) == 1 && ch != '\n'){}; } -#endif } } /* while(...) */ close(fd); + +#ifndef __PCTOOL__ + if (import_open_plugins) + open_plugin_import(file); +#else + (void) import_open_plugins; +#endif + if (apply) { settings_save(); diff --git a/apps/settings_list.c b/apps/settings_list.c index 59fd4537a0..7601cf66ca 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -67,6 +67,7 @@ #endif #include "playlist.h" #include "tree.h" +#include "iap-usb.h" #include "voice_thread.h" @@ -790,15 +791,17 @@ static void shuffle_playlist_callback(bool shuffle) } } } + iap_on_shuffle_state(shuffle); } static void repeat_mode_callback(int repeat) { + (void)repeat; if ((audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY) { audio_flush_and_reload_tracks(); } - (void)repeat; + iap_on_repeat_state(repeat); } static void treesort_callback(int value) @@ -2337,30 +2340,38 @@ const struct settings_list settings[] = { #endif #ifdef HAVE_HOTKEY +/* WPS HOTKEY */ TABLE_SETTING(F_CB_ON_SELECT_ONLY, hotkey_wps, LANG_HOTKEY_WPS, HOTKEY_VIEW_PLAYLIST, "hotkey wps", "off,view playlist,show track info,pitchscreen,open with,delete,bookmark,plugin,bookmark list" - ,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,9, HOTKEY_OFF, - HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN, - HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN, HOTKEY_BOOKMARK_LIST), +#ifdef HAVE_ALBUMART + ",show_album_art,context menu" + ,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,11, +#else + ",context menu" + ,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,10, +#endif + HOTKEY_OFF, HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN, + HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN, HOTKEY_BOOKMARK_LIST, +#ifdef HAVE_ALBUMART + HOTKEY_ALBUMART, +#endif + HOTKEY_CONTEXT_MENU), +/* TREE HOTKEY */ TABLE_SETTING(0, hotkey_tree, LANG_HOTKEY_FILE_BROWSER, HOTKEY_OFF, "hotkey tree", #ifdef HAVE_TAGCACHE - "off,properties,pictureflow,open with,delete,insert,insert shuffled", + "off,properties,pictureflow,open with,delete,insert,insert shuffled,context menu", + UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 8, #else - "off,properties,open with,delete,insert,insert shuffled", -#endif - UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, -#ifdef HAVE_TAGCACHE - 7, -#else - 6, + "off,properties,open with,delete,insert,insert shuffled,context menu", + UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 7, #endif HOTKEY_OFF,HOTKEY_PROPERTIES, #ifdef HAVE_TAGCACHE HOTKEY_PICTUREFLOW, #endif - HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED), + HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED, HOTKEY_CONTEXT_MENU), #endif /* HAVE_HOTKEY */ INT_SETTING(F_TIME_SETTING, resume_rewind, LANG_RESUME_REWIND, 0, diff --git a/apps/tree.c b/apps/tree.c index 387a070e3a..797e5a3936 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -413,6 +413,9 @@ static int update_dir(void) const bool id3db = false; #endif + /* Ensure that list is initialized before update_dir returns */ + gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL); + #ifdef HAVE_TAGCACHE /* Checks for changes */ if (id3db) { @@ -461,8 +464,6 @@ static int update_dir(void) } } - gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL); - #ifdef HAVE_TAGCACHE if (id3db) { diff --git a/firmware/SOURCES b/firmware/SOURCES index 888d296fd0..b80dc1a272 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -957,6 +957,20 @@ usbstack/usb_charging_only.c #ifdef USB_ENABLE_HID usbstack/usb_hid.c #endif +#ifdef USB_ENABLE_IAP +usbstack/iap/audio.c +usbstack/iap/buffer.c +usbstack/iap/debug.c +usbstack/iap/libiap/debug.c +usbstack/iap/libiap/fid-token-values.c +usbstack/iap/libiap/hid.c +usbstack/iap/libiap/iap.c +usbstack/iap/libiap/notification.c +usbstack/iap/libiap/span.c +usbstack/iap/notification.c +usbstack/iap/platform.c +usbstack/usb_iap.c +#endif #if CONFIG_USBOTG == USBOTG_M66591 drivers/m66591.c #elif CONFIG_USBOTG == USBOTG_ARC diff --git a/firmware/asm/SOURCES b/firmware/asm/SOURCES index 463c120213..74ba94c204 100644 --- a/firmware/asm/SOURCES +++ b/firmware/asm/SOURCES @@ -65,3 +65,7 @@ lcd-as-memframe.c #endif /* CPU_ARM */ #endif /* LCD_DEPTH */ #endif + +#if defined(USE_STACK_PROTECTOR) +stack-protector.c +#endif diff --git a/firmware/asm/arm/pcm-mixer-armv4.c b/firmware/asm/arm/pcm-mixer-armv4.c index dc2edb781c..cb138fc565 100644 --- a/firmware/asm/arm/pcm-mixer-armv4.c +++ b/firmware/asm/arm/pcm-mixer-armv4.c @@ -19,9 +19,6 @@ * ****************************************************************************/ -#define MIXER_OPTIMIZED_WRITE_SAMPLES -#define MIXER_OPTIMIZED_MIX_SAMPLES - /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, const void *src0, diff --git a/firmware/asm/arm/pcm-mixer-armv5.c b/firmware/asm/arm/pcm-mixer-armv5.c index add1522fd9..399e6a4373 100644 --- a/firmware/asm/arm/pcm-mixer-armv5.c +++ b/firmware/asm/arm/pcm-mixer-armv5.c @@ -19,9 +19,6 @@ * ****************************************************************************/ -#define MIXER_OPTIMIZED_WRITE_SAMPLES -#define MIXER_OPTIMIZED_MIX_SAMPLES - /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, const void *src0, diff --git a/firmware/asm/arm/pcm-mixer-armv6.c b/firmware/asm/arm/pcm-mixer-armv6.c index c1334bcd6a..7bf83ecf66 100644 --- a/firmware/asm/arm/pcm-mixer-armv6.c +++ b/firmware/asm/arm/pcm-mixer-armv6.c @@ -18,8 +18,6 @@ * KIND, either express or implied. * ****************************************************************************/ -#define MIXER_OPTIMIZED_MIX_SAMPLES -#define MIXER_OPTIMIZED_WRITE_SAMPLES /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, diff --git a/firmware/asm/m68k/pcm-mixer.c b/firmware/asm/m68k/pcm-mixer.c index 2969546e56..cee2074cd1 100644 --- a/firmware/asm/m68k/pcm-mixer.c +++ b/firmware/asm/m68k/pcm-mixer.c @@ -19,8 +19,6 @@ * ****************************************************************************/ -#define MIXER_OPTIMIZED_MIX_SAMPLES -#define MIXER_OPTIMIZED_WRITE_SAMPLES static struct emac_context { unsigned long saved; diff --git a/firmware/asm/stack-protector.c b/firmware/asm/stack-protector.c new file mode 100644 index 0000000000..b0de405980 --- /dev/null +++ b/firmware/asm/stack-protector.c @@ -0,0 +1,29 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2026 by 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 "panic.h" +#include + +const uint32_t __stack_chk_guard = 0x1BADC0DE; + +void __stack_chk_fail(void) +{ + panicf("stack smashing detected"); +} diff --git a/firmware/backlight.c b/firmware/backlight.c index ee7b147aee..3623162adb 100644 --- a/firmware/backlight.c +++ b/firmware/backlight.c @@ -24,6 +24,7 @@ #if !defined(BOOTLOADER) #include "settings.h" #include "action.h" +#include "../apps/gui/skin_engine/skin_engine.h" #endif #include #include "cpu.h" @@ -887,6 +888,9 @@ void backlight_set_timeout_plugged(int value) /* Hold button change event handler. */ void backlight_hold_changed(bool hold_button) { +#ifndef BOOTLOADER + skin_request_update_locked(hold_button); +#endif if (!hold_button || (backlight_on_button_hold > 0)) { /* if unlocked or override in effect */ diff --git a/firmware/common/unicode.c b/firmware/common/unicode.c index c1e187a709..f7d44e1ee2 100644 --- a/firmware/common/unicode.c +++ b/firmware/common/unicode.c @@ -44,6 +44,10 @@ #define O_NOISODECODE 0 #endif +#ifdef UTF8PROC_EXPORTS +#include "utf8proc.h" +#endif + #define getle16(p) (p[0] | (p[1] << 8)) #define getbe16(p) ((p[0] << 8) | p[1]) @@ -59,8 +63,9 @@ #define open_noiso_internal open #endif /* !APPLICATION */ -#if 0 /* not needed just now (will probably end up a spinlock) */ #include "mutex.h" + +#if 0 /* not needed just now (will probably end up a spinlock) */ static struct mutex cp_mutex SHAREDBSS_ATTR; #define cp_lock_init() mutex_init(&cp_mutex) #define cp_lock_enter() mutex_lock(&cp_mutex) @@ -651,9 +656,32 @@ const char * get_codepage_name(int cp) return cp_info[cp].name; } -#if 0 /* not needed just now */ +#ifdef UTF8PROC_EXPORTS +static utf8proc_int32_t normbuf[2048]; +static struct mutex norm_mutex SHAREDBSS_ATTR; + +void utf8_normalize(char *string) +{ + utf8proc_ssize_t result, orig; + + if (!string || !*string) + return; + + mutex_lock(&norm_mutex); + orig = strlen(string); + result = utf8proc_decompose(string, 0, normbuf, sizeof(normbuf)/4 -1, UTF8PROC_NULLTERM); + if (result > 0) { + result = utf8proc_reencode(normbuf, result, UTF8PROC_NULLTERM|UTF8PROC_COMPOSE|UTF8PROC_STABLE); + if (result > 0 && result <= orig && strcmp((char*)normbuf, string)) + strcpy(string, (char*)normbuf); + } + mutex_unlock(&norm_mutex); +} + void unicode_init(void) { cp_lock_init(); + mutex_init(&norm_mutex); } + #endif diff --git a/firmware/drivers/isp1583.c b/firmware/drivers/isp1583.c index 39b9d1190e..93aba17a7b 100644 --- a/firmware/drivers/isp1583.c +++ b/firmware/drivers/isp1583.c @@ -626,18 +626,17 @@ void usb_drv_cancel_all_transfers(void) endpoints[i].halt[0] = endpoints[i].halt[1] = 1; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { - (void)max_packet_size; /* FIXME: support max packet size override */ - (void)type; - (void)endpoint; - return 0; + /* FIXME: support max packet size override */ + (void)ctx; + (void)ep; } -int usb_drv_deinit_endpoint(int endpoint) +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { - (void)endpoint; - return 0; + (void)ctx; + (void)ep; } static void bus_reset(void) diff --git a/firmware/drivers/m66591.c b/firmware/drivers/m66591.c index cf41aced31..6095e085e3 100644 --- a/firmware/drivers/m66591.c +++ b/firmware/drivers/m66591.c @@ -640,8 +640,11 @@ void usb_drv_set_test_mode(int mode) { M66591_TESTMODE |= mode; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + /* FIXME: support max packet size override */ + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; int pipecfg = 0; @@ -651,41 +654,38 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { } else if(type == USB_ENDPOINT_XFER_BULK) { pipecfg |= 1<<13; } else { - /* Not a supported type */ - return -1; + panicf("mxx: unsupported type %d", type); } - int num = endpoint & USB_ENDPOINT_NUMBER_MASK; - int dir = endpoint & USB_ENDPOINT_DIR_MASK; - if (dir == USB_DIR_IN) { + if (epdir == DIR_IN) { pipecfg |= (1<<4); } - M66591_eps[num].dir = dir; + M66591_eps[epnum].dir = epdir == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; - M66591_PIPE_CFGSEL=num; + M66591_PIPE_CFGSEL = epnum; /* Enable pipe (15) */ pipecfg |= 1<<15; - pipe_handshake(num, PIPE_SHAKE_NAK); + pipe_handshake(epnum, PIPE_SHAKE_NAK); /* Setup the flags */ M66591_PIPE_CFGWND=pipecfg; - pipe_init(num); + pipe_init(epnum); - logf("mxx: ep req ep#: %d config: 0x%04x", num, M66591_PIPE_CFGWND); - - return 0; + logf("mxx: ep req ep#: %d config: 0x%04x", epnum, M66591_PIPE_CFGWND); } /* Used by stack to tell the helper functions that the pipe is not in use */ -int usb_drv_deinit_endpoint(int endpoint) { - int num = endpoint & USB_ENDPOINT_NUMBER_MASK; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + (void)ctx; + + int num = ep & USB_ENDPOINT_NUMBER_MASK; if (num < 1 || num > USB_NUM_ENDPOINTS) { - return -1; + return; } int flags = disable_irq_save(); @@ -695,8 +695,6 @@ int usb_drv_deinit_endpoint(int endpoint) { M66591_eps[num].dir = -1; restore_irq(flags); - - return 0; } /* Periodically called to check if a cable was plugged into the device */ diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index d1274f5e41..9968778dde 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -24,6 +24,8 @@ #include #include +#include "usb-designware.h" + #include "config.h" #include "cpu.h" #include "system.h" @@ -35,8 +37,6 @@ #include "usb_ch9.h" #include "usb_core.h" -#include "usb-designware.h" - /* Define LOGF_ENABLE to enable logf output in this file */ /*#define LOGF_ENABLE*/ #include "logf.h" @@ -155,9 +155,6 @@ struct usb_dw_ep0 struct usb_ctrlrequest pending_req; }; -struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; /* filled in usb_drv_init */ -uint8_t usb_drv_ep_specs_flags = 0; - static const char* const dw_dir_str[USB_DW_NUM_DIRS] = { [USB_DW_EPDIR_IN] = "IN", @@ -195,7 +192,6 @@ static uint32_t usb_endpoints; /* available EPs mask */ (usually 1), otherwise it is the number of dedicated Tx FIFOs (not counting NPTX FIFO that is always dedicated for IN0). */ static int n_ptxfifos; -static uint16_t ptxfifo_usage; static uint32_t hw_maxbytes; static uint32_t hw_maxpackets; @@ -672,47 +668,28 @@ static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir) #endif ep_periodic_msk &= ~(1 << epnum); #endif - ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum)); } usb_dw_flush_endpoint(epnum, epdir); DWC_EPCTL(epnum, epdir) = epctl; } -static int usb_dw_configure_ep(int epnum, - enum usb_dw_epdir epdir, int type, int maxpktsize) +static void usb_dw_configure_ep(const struct usb_drv_ep_alloc_ctx* ctx, int epnum, enum usb_dw_epdir epdir, int type, int maxpktsize) { uint32_t epctl = SETD0PIDEF|EPTYP(type)|USBAEP|maxpktsize; - if (epdir == USB_DW_EPDIR_IN) + if (epdir == USB_DW_EPDIR_IN && ctx->assigned_txfifos[epnum] > 0) { - /* - * If the hardware has dedicated fifos, we must give each - * IN EP a unique tx-fifo even if it is non-periodic. - */ #ifdef USB_DW_SHARED_FIFO + ep_periodic_msk |= (1 << epnum); #ifndef USB_DW_ARCH_SLAVE epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf); #endif - if (type == USB_ENDPOINT_XFER_INT) #endif - { - int fnum; - for (fnum = 1; fnum <= n_ptxfifos; fnum++) - if (~ptxfifo_usage & (1 << fnum)) - break; - if (fnum > n_ptxfifos) - return -1; /* no available fifos */ - ptxfifo_usage |= (1 << fnum); - epctl |= DTXFNUM(fnum); -#ifdef USB_DW_SHARED_FIFO - ep_periodic_msk |= (1 << epnum); -#endif - } + epctl |= DTXFNUM(ctx->assigned_txfifos[epnum]); } DWC_EPCTL(epnum, epdir) = epctl; - return 0; /* ok */ } static void usb_dw_reset_endpoints(void) @@ -753,7 +730,6 @@ static void usb_dw_reset_endpoints(void) usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN); } - ptxfifo_usage = 0; #ifdef USB_DW_SHARED_FIFO ep_periodic_msk = 0; #endif @@ -1509,17 +1485,6 @@ static void usb_dw_init(void) /* Soft reconnect */ udelay(3000); DWC_DCTL &= ~SDIS; - - /* Fill endpoint spec table FIXME: should be done in usb_drv_startup() */ - usb_drv_ep_specs[0].type[DIR_OUT] = USB_ENDPOINT_XFER_CONTROL; - usb_drv_ep_specs[0].type[DIR_IN] = USB_ENDPOINT_XFER_CONTROL; - for(int i = 1; i < USB_NUM_ENDPOINTS; i += 1) { - bool out_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_OUT))); - usb_drv_ep_specs[i].type[DIR_OUT] = out_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE; - - bool in_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_IN))); - usb_drv_ep_specs[i].type[DIR_IN] = in_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE; - } } static void usb_dw_exit(void) @@ -1617,50 +1582,95 @@ void INT_USB_FUNC(void) usb_dw_irq(); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) +void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx) { - (void)max_packet_size; /* FIXME: support max packet size override */ - - enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; - struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir); - - int maxpktsize; - if(type == EPTYP_ISOCHRONOUS) - { - maxpktsize = 1023; - } - else - { - maxpktsize = usb_drv_port_speed() ? 512 : 64; - } - - usb_dw_target_disable_irq(); - int res = usb_dw_configure_ep(EP_NUM(endpoint), epdir, type, maxpktsize); - usb_dw_target_enable_irq(); - - if(res >= 0) - { - dw_ep->active = true; - return 0; - } - else - { - return -1; - } + memset(ctx, 0, sizeof(*ctx)); } -int usb_drv_deinit_endpoint(int endpoint) +bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size) { - enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; - struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir); + const uint8_t epnum = EP_NUM(ep); + const uint8_t epdir = EP_DIR(ep); + + if(ep == EP_CONTROL) + { + return false; + } + + enum usb_dw_epdir dwdir = epdir == DIR_IN ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + if(!(usb_endpoints & (1 << (epnum + USB_DW_DIR_OFF(dwdir))))) + { + return false; + } + + bool need_fifo = epdir == DIR_IN; +#ifdef USB_DW_SHARED_FIFO + /* in shared fifo mode, only periodic endpoints need dedicated fifo */ + need_fifo &= type == USB_ENDPOINT_XFER_ISOC || type == USB_ENDPOINT_XFER_INT; +#endif + if(!need_fifo) + { + goto ok; + } + + for (int fnum = 1; fnum <= n_ptxfifos; fnum++) + { + if (~ctx->txfifo_usage & (1 << fnum)) + { + ctx->txfifo_usage |= 1 << fnum; + ctx->assigned_txfifos[epnum] = fnum; + goto ok; + } + } + + return false; + +ok: + ctx->type[epnum][epdir] = type; + ctx->max_packet_size[epnum][epdir] = max_packet_size; + return true; +} + +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + const int epnum = EP_NUM(ep); + const int epdir_ = EP_DIR(ep); + const int type = ctx->type[epnum][epdir_]; + + enum usb_dw_epdir epdir = (epdir_ == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir); + + int mps = ctx->max_packet_size[epnum][epdir_]; + if(mps == -1) + { + if(type == EPTYP_ISOCHRONOUS) + { + mps = 1023; + } + else + { + mps = usb_drv_port_speed() ? 512 : 64; + } + } usb_dw_target_disable_irq(); - usb_dw_unconfigure_ep(EP_NUM(endpoint), epdir); + usb_dw_configure_ep(ctx, epnum, epdir, type, mps); + usb_dw_target_enable_irq(); + + dw_ep->active = true; +} + +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + enum usb_dw_epdir epdir = (EP_DIR(ep) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir); + + usb_dw_target_disable_irq(); + usb_dw_unconfigure_ep(EP_NUM(ep), epdir); usb_dw_target_enable_irq(); dw_ep->active = false; - - return 0; } int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) diff --git a/firmware/export/axp-2101.h b/firmware/export/axp-2101.h index e84f552730..cd810ff006 100644 --- a/firmware/export/axp-2101.h +++ b/firmware/export/axp-2101.h @@ -25,6 +25,7 @@ #include "config.h" #include #include +#include /* ADC channels */ #define AXP2101_ADC_VBAT_VOLTAGE 0 diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h index 24c992dea3..77ef1cc356 100644 --- a/firmware/export/axp-pmu.h +++ b/firmware/export/axp-pmu.h @@ -25,6 +25,7 @@ #include "config.h" #include #include +#include /* ADC channels */ #define ADC_ACIN_VOLTAGE 0 diff --git a/firmware/export/config.h b/firmware/export/config.h index 4cd178e405..4b0a16f0f1 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -1396,6 +1396,14 @@ Lyre prototype 1 */ #define USB_ENABLE_AUDIO #endif +#if defined(USB_HAS_INTERRUPT) && defined(USB_HAS_ISOCHRONOUS) && USB_VENDOR_ID == 0x05ac +#define USB_ENABLE_IAP +#endif + +#if defined(USB_ENABLE_IAP) +#define HAVE_MULTIMEDIA_KEYS +#endif + #endif /* BOOTLOADER */ #endif /* HAVE_USBSTACK */ diff --git a/firmware/export/iap-usb.h b/firmware/export/iap-usb.h new file mode 100644 index 0000000000..24f837981b --- /dev/null +++ b/firmware/export/iap-usb.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +#include "config.h" + +#ifdef USB_ENABLE_IAP +/* usbstack/iap/notification.c */ +void iap_on_track_time_position(uint32_t pos_ms); +void iap_on_track_playback_index(uint32_t index, bool track_ready); +void iap_on_tracks_count(uint32_t count); +void iap_on_play_status(int status /* AUDIO_STATUS_* */); +void iap_on_volume(int volume); +void iap_on_shuffle_state(bool state); +void iap_on_repeat_state(int state); +#else +static inline void iap_on_track_time_position(uint32_t pos_ms) { + (void)pos_ms; +} +static inline void iap_on_track_playback_index(uint32_t index, bool track_ready) { + (void)index; + (void)track_ready; +} +static inline void iap_on_tracks_count(uint32_t count) { + (void)count; +} +static inline void iap_on_play_status(int status) { + (void)status; +} +static inline void iap_on_volume(int volume) { + (void)volume; +} +static inline void iap_on_shuffle_state(bool state) { + (void)state; +} +static inline void iap_on_repeat_state(int state) { + (void)state; +} +#endif diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h index 5f1509ad97..42931fbf21 100644 --- a/firmware/export/pcm.h +++ b/firmware/export/pcm.h @@ -64,6 +64,7 @@ bool pcm_is_initialized(void); enum pcm_sink_ids pcm_current_sink(void); const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink); +bool pcm_switch_sink(enum pcm_sink_ids sink); /* shortcut for plugins */ const struct pcm_sink_caps* pcm_current_sink_caps(void); diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h index 0607b474f6..d98a24e54a 100644 --- a/firmware/export/pcm_mixer.h +++ b/firmware/export/pcm_mixer.h @@ -23,6 +23,8 @@ #define PCM_MIXER_H #include +#include "pcm.h" +#include "pcm_sink.h" /** Simple config **/ @@ -111,6 +113,9 @@ void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play); /* Stop playback on a channel */ void mixer_channel_stop(enum pcm_mixer_channel channel); +/* Switch playback sink */ +bool mixer_switch_sink(enum pcm_sink_ids sink); + /* Set channel's amplitude factor */ void mixer_channel_set_amplitude(enum pcm_mixer_channel channel, unsigned int amplitude); diff --git a/firmware/export/pcm_sink.h b/firmware/export/pcm_sink.h index 77c64428f6..a4945d3534 100644 --- a/firmware/export/pcm_sink.h +++ b/firmware/export/pcm_sink.h @@ -21,6 +21,8 @@ #include #include +#include "config.h" + struct pcm_sink_caps { const unsigned long* samprs; uint16_t num_samprs; @@ -52,6 +54,10 @@ struct pcm_sink { enum pcm_sink_ids { PCM_SINK_BUILTIN = 0, +#ifdef USB_ENABLE_IAP + PCM_SINK_IAP, +#endif + PCM_SINK_NUM }; /* defined in each platform pcm source */ diff --git a/firmware/export/reggen.h b/firmware/export/reggen.h index ce66bd718b..4a15ddb912 100644 --- a/firmware/export/reggen.h +++ b/firmware/export/reggen.h @@ -22,7 +22,9 @@ #ifndef __REGGEN_H__ #define __REGGEN_H__ +#ifndef __ASSEMBLER__ #include +#endif #define __REGGEN_VAR_OR1(p, s1) \ ((p ## s1)) diff --git a/firmware/export/s5l87xx.h b/firmware/export/s5l87xx.h index bb3aab16ba..60317e60d0 100644 --- a/firmware/export/s5l87xx.h +++ b/firmware/export/s5l87xx.h @@ -26,9 +26,10 @@ #include #endif -#define REG16_PTR_T volatile uint16_t * -#define REG32_PTR_T volatile uint32_t * -#define VOID_PTR_PTR_T void* volatile* +#define REG_BIT(x) (1 << (x)) +#define REG16_PTR_T volatile uint16_t * +#define REG32_PTR_T volatile uint32_t * +#define VOID_PTR_PTR_T void* volatile* #if CONFIG_CPU==S5L8700 || CONFIG_CPU==S5L8701 #define CACHEALIGN_BITS (4) /* 2^4 = 16 bytes */ @@ -1612,19 +1613,30 @@ Information for them was gathered solely by reverse-engineering Apple's firmware #define SHA1_BASE 0x38000000 #endif -#define SHA1CONFIG (*((REG32_PTR_T)(SHA1_BASE))) -#define SHA1RESET (*((REG32_PTR_T)(SHA1_BASE + 0x04))) +#define SHA1_CONFIG (*((REG32_PTR_T)(SHA1_BASE))) +#define SHA1_SWRESET (*((REG32_PTR_T)(SHA1_BASE + 0x04))) +#define SHA1_INT_SRC (*((REG32_PTR_T)(SHA1_BASE + 0x08))) +#define SHA1_INT_MASK (*((REG32_PTR_T)(SHA1_BASE + 0x0C))) +#define SHA1_ENDIAN (*((REG32_PTR_T)(SHA1_BASE + 0x10))) -#if CONFIG_CPU == S5L8720 -#define SHA1UNK10 (*((REG32_PTR_T)(SHA1_BASE + 0x10))) -#endif +// Result is 20 bytes (160 bits) 0x20-0x33 +#define SHA1_RESULT ((REG32_PTR_T)(SHA1_BASE + 0x20)) -#define SHA1RESULT ((REG32_PTR_T)(SHA1_BASE + 0x20)) -#define SHA1DATAIN ((REG32_PTR_T)(SHA1_BASE + 0x40)) +// Input is 64 bytes (512 bits) 0x40-0x7F +#define SHA1_DATA ((REG32_PTR_T)(SHA1_BASE + 0x40)) -#if CONFIG_CPU == S5L8720 -#define SHA1UNK80 (*((REG32_PTR_T)(SHA1_BASE + 0x80))) -#endif +#define SHA1_MASTER_MODE (*((REG32_PTR_T)(SHA1_BASE + 0x80))) +#define SHA1_MS_START_ADDR (*((REG32_PTR_T)(SHA1_BASE + 0x84))) +#define SHA1_VERSION (*((REG32_PTR_T)(SHA1_BASE + 0x88))) +#define SHA1_MS_SIZE (*((REG32_PTR_T)(SHA1_BASE + 0x8C))) +#define SHA1_FIFO_PARAM (*((REG32_PTR_T)(SHA1_BASE + 0x90))) +#define SHA1_FIFO_CMD (*((REG32_PTR_T)(SHA1_BASE + 0x94))) +#define SHA1_TX_FIFO_STAT (*((REG32_PTR_T)(SHA1_BASE + 0x98))) +#define SHA1_TX_FIFO (*((REG32_PTR_T)(SHA1_BASE + 0xA0))) + +#define SHA1_CONFIG_BUSY REG_BIT(0) +#define SHA1_CONFIG_GO REG_BIT(1) +#define SHA1_CONFIG_CONT REG_BIT(3) /* Clickwheel controller - S5L8701+ */ #if CONFIG_CPU==S5L8701 || CONFIG_CPU==S5L8702 || CONFIG_CPU==S5L8720 diff --git a/firmware/export/system.h b/firmware/export/system.h index e1d999dcd8..fce3c85b39 100644 --- a/firmware/export/system.h +++ b/firmware/export/system.h @@ -266,8 +266,10 @@ static inline void cpu_boost_unlock(void) #endif #endif -/* ARM ABIs generally require 8-byte stack alignment */ -#ifdef CPU_ARM +/* + * ARM and MIPS ABIs generally require 8-byte stack alignment. + */ +#if defined(CPU_ARM) || defined(CPU_MIPS) #define MIN_STACK_ALIGN 8 #endif diff --git a/firmware/export/usb-designware.h b/firmware/export/usb-designware.h index ccef6d3d0d..993ebcfbc1 100644 --- a/firmware/export/usb-designware.h +++ b/firmware/export/usb-designware.h @@ -24,7 +24,9 @@ #define __USB_DESIGNWARE_H__ #include +#include #include "config.h" +#include "cpu.h" #ifndef REG32_PTR_T #define REG32_PTR_T volatile uint32_t * @@ -288,4 +290,15 @@ extern void usb_dw_target_enable_irq(void); extern void usb_dw_target_disable_irq(void); extern void usb_dw_target_clear_irq(void); +/* endpoint allocation */ +struct usb_drv_ep_alloc_ctx_dw +{ + int8_t type[USB_NUM_ENDPOINTS][2]; + int max_packet_size[USB_NUM_ENDPOINTS][2]; + + uint16_t txfifo_usage; + uint8_t assigned_txfifos[USB_NUM_ENDPOINTS]; +}; +#define usb_drv_ep_alloc_ctx usb_drv_ep_alloc_ctx_dw + #endif /* __USB_DESIGNWARE_H__ */ diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 6d9784f93e..ed76ade521 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -172,6 +172,9 @@ enum { #endif #ifdef USB_ENABLE_AUDIO USB_DRIVER_AUDIO, +#endif +#ifdef USB_ENABLE_IAP + USB_DRIVER_IAP, #endif USB_NUM_DRIVERS }; diff --git a/firmware/export/usb_drv.h b/firmware/export/usb_drv.h index 6c8949200d..405ea1d9dd 100644 --- a/firmware/export/usb_drv.h +++ b/firmware/export/usb_drv.h @@ -62,19 +62,42 @@ enum usb_control_response { USB_CONTROL_RECEIVE, }; +/* endpoint allocation: + * there are two ways to implement endpoint allocation. + * 1. define usb_drv_ep_specs and usb_drv_ep_specs_flags in the driver. + * this is the simplest option when the types accepted by each endpoint are mutually independent. + * these variables are set only once during driver initialization and should not be modified afterward. + * 2. define usb_drv_ep_alloc_ctx and implement usb_drv_ep_reset_alloc_ctx and usb_drv_ep_allocate in the driver. + * if the available endpoint types change based on allocation status, + * these functions can be overridden to allow the driver to track the endpoint state. + * */ + +#ifndef usb_drv_ep_alloc_ctx +/* option 1 */ #define USB_ENDPOINT_TYPE_ANY (-1) #define USB_ENDPOINT_TYPE_NONE (-2) - struct usb_drv_ep_spec { int8_t type[2]; /* USB_ENDPOINT_TYPE_{ANY,NONE} USB_ENDPOINT_XFER_* */ }; - extern struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; #define USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH (1 << 0) #define USB_ENDPOINT_SPEC_IO_EXCLUSIVE (1 << 1) extern uint8_t usb_drv_ep_specs_flags; +struct usb_drv_ep_alloc_ctx { + int8_t type[USB_NUM_ENDPOINTS][2]; + int max_packet_size[USB_NUM_ENDPOINTS][2]; +}; +#else +/* option 2 */ +void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx); +bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size); +#endif + +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep); + /* one-time initialisation of the USB driver */ void usb_drv_startup(void); void usb_drv_int_enable(bool enable); /* Target implemented */ @@ -98,8 +121,6 @@ int usb_drv_port_speed(void); void usb_drv_cancel_all_transfers(void); void usb_drv_set_test_mode(int mode); bool usb_drv_connected(void); -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size); -int usb_drv_deinit_endpoint(int endpoint); #ifdef USB_HAS_ISOCHRONOUS /* returns the last received frame number (the 11-bit number contained in the last SOF): * - full-speed: the host sends one SOF every 1ms (so 1000 SOF/s) diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h index 2cf838e539..d610c63245 100644 --- a/firmware/include/dircache.h +++ b/firmware/include/dircache.h @@ -44,7 +44,7 @@ the limiting factor is the scanning thread stack size, not the implementation -- tune the two together */ #define DIRCACHE_MAX_DEPTH 15 -#define DIRCACHE_STACK_SIZE (DEFAULT_STACK_SIZE + 0x100) +#define DIRCACHE_STACK_SIZE (DEFAULT_STACK_SIZE + 0x400) /* memory buffer constants that control allocation */ #define DIRCACHE_RESERVE (1024*64) /* 64 KB - new entry slack */ diff --git a/firmware/include/gcc_extensions.h b/firmware/include/gcc_extensions.h index ecead8cbb5..3c4f0476a9 100644 --- a/firmware/include/gcc_extensions.h +++ b/firmware/include/gcc_extensions.h @@ -77,5 +77,19 @@ #define UNUSED_ATTR #endif +/* + * Tell the compiler to assume 'x' is true. With a new enough GCC + * there's an attribute for this based on C++23's assume attribute. + * On older GCC we need to play tricks with __builtin_unreachable(). + * As a result 'x' may or may not be evaluated at runtime and should + * be side-effect free to ensure it doesn't have any runtime impact. + */ +#if defined(__GNUC__) && (__GNUC__ >= 13) +# define ASSUME(x) __attribute__((assume((x)))) +#elif defined(__GNUC__) +# define ASSUME(x) do { if(!(x)) __builtin_unreachable(); } while (0) +#else +# define ASSUME(x) +#endif #endif /* _GCC_EXTENSIONS_H_ */ diff --git a/firmware/include/rbunicode.h b/firmware/include/rbunicode.h index 48ca1b2583..d243c08596 100644 --- a/firmware/include/rbunicode.h +++ b/firmware/include/rbunicode.h @@ -76,8 +76,9 @@ const char *get_current_codepage_name_linux(void); #endif #endif /* APPLICATION */ -#if 0 /* not needed just now */ -void unicode_init(void); +#ifdef UTF8PROC_EXPORTS +void utf8_normalize(char *string); +void unicode_init(void) INIT_ATTR; #else #define unicode_init() do {} while (0) #endif diff --git a/firmware/libc/include/inttypes.h b/firmware/libc/include/inttypes.h index ddcaa988b1..ed6fd6d47c 100644 --- a/firmware/libc/include/inttypes.h +++ b/firmware/libc/include/inttypes.h @@ -18,11 +18,11 @@ * KIND, either express or implied. * ****************************************************************************/ - #ifndef __INTTYPES_H__ #define __INTTYPES_H__ #include +#include /* could possibly have (f)scanf format specifiers here */ diff --git a/firmware/libc/include/stdint.h b/firmware/libc/include/stdint.h deleted file mode 100644 index a36dcf59b7..0000000000 --- a/firmware/libc/include/stdint.h +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2005 by Dave Chapman - * - * 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 __STDINT_H__ -#define __STDINT_H__ - -#include - -/* 8 bit */ -#define INT8_MIN SCHAR_MIN -#define INT8_MAX SCHAR_MAX -#define UINT8_MAX UCHAR_MAX -#define int8_t signed char -#define uint8_t unsigned char - -/* 16 bit */ -#if USHRT_MAX == 0xffff - -#define INT16_MIN SHRT_MIN -#define INT16_MAX SHRT_MAX -#define UINT16_MAX USHRT_MAX -#define int16_t short -#define uint16_t unsigned short - -#endif - -/* 32 bit */ -#if ULONG_MAX == 0xfffffffful - -#define SIZE_MAX ULONG_MAX -#define INT32_MIN LONG_MIN -#define INT32_MAX LONG_MAX -#define UINT32_MAX ULONG_MAX -#define int32_t long -#define uint32_t unsigned long - -#define INTPTR_MIN LONG_MIN -#define INTPTR_MAX LONG_MAX -#define UINTPTR_MAX ULONG_MAX -#define intptr_t long -#define uintptr_t unsigned long - -#elif UINT_MAX == 0xffffffffu - -#define SIZE_MAX UINT_MAX -#define INT32_MIN INT_MIN -#define INT32_MAX INT_MAX -#define UINT32_MAX UINT_MAX -#define int32_t int -#define uint32_t unsigned int - -#endif - -/* 64 bit */ -#ifndef LLONG_MIN -#define LLONG_MIN ((long long)9223372036854775808ull) -#endif - -#ifndef LLONG_MAX -#define LLONG_MAX 9223372036854775807ll -#endif - -#ifndef ULLONG_MAX -#define ULLONG_MAX 18446744073709551615ull -#endif - -#if ULONG_MAX == 0xffffffffffffffffull - -#define SIZE_MAX ULONG_MAX -#define INT64_MIN LONG_MIN -#define INT64_MAX LONG_MAX -#define UINT64_MAX ULONG_MAX -#define int64_t long -#define uint64_t unsigned long - -#define INTPTR_MIN LONG_MIN -#define INTPTR_MAX LONG_MAX -#define UINTPTR_MAX ULONG_MAX -#define intptr_t long -#define uintptr_t unsigned long - -#else - -#define INT64_MIN LLONG_MIN -#define INT64_MAX LLONG_MAX -#define UINT64_MAX ULLONG_MAX -#define int64_t long long -#define uint64_t unsigned long long - -#endif - -#define uintmax_t unsigned long long -#define intmax_t long long -#endif /* __STDINT_H__ */ diff --git a/firmware/libc/sprintf.c b/firmware/libc/sprintf.c index a56f454c34..a78b5ed665 100644 --- a/firmware/libc/sprintf.c +++ b/firmware/libc/sprintf.c @@ -109,3 +109,21 @@ overflow: errno = EOVERFLOW; return -1; } + +int sprintf(char *buf, const char *fmt, ...) +{ + int bytes; + struct for_snprintf pr; + va_list ap; + + pr.ptr = buf; + pr.rem = INT_MAX; + + va_start(ap, fmt); + bytes = vuprintf(sprfunc, &pr, fmt, ap); + va_end(ap); + + *pr.ptr = '\0'; + + return bytes; +} diff --git a/firmware/libc/sscanf.c b/firmware/libc/sscanf.c index 1bbb5abcbf..23c0d450d6 100644 --- a/firmware/libc/sscanf.c +++ b/firmware/libc/sscanf.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -21,7 +22,8 @@ static inline bool my_isxdigit(char c) static int parse_dec(int (*peek)(void *userp), void (*pop)(void *userp), void *userp, - long *vp) + long *vp, + int max_len) { long v = 0; int n = 0; @@ -45,7 +47,7 @@ static int parse_dec(int (*peek)(void *userp), (*pop)(userp); n++; ch = (*peek)(userp); - } while (my_isdigit(ch)); + } while (my_isdigit(ch) && n < max_len); *vp = minus ? -v : v; return n; @@ -55,13 +57,14 @@ static int parse_chars(int (*peek)(void *userp), void (*pop)(void *userp), void *userp, char *vp, - bool fake) + bool fake, + int max_len) { int n = 0; char *pt=vp; - while (!my_isspace((*peek)(userp))) + while (!my_isspace((*peek)(userp)) && n < max_len) { if(fake==false) *(pt++) = (*peek)(userp); @@ -79,7 +82,8 @@ static int parse_chars(int (*peek)(void *userp), static int parse_hex(int (*peek)(void *userp), void (*pop)(void *userp), void *userp, - unsigned long *vp) + unsigned long *vp, + int max_len) { unsigned long v = 0; int n = 0; @@ -101,7 +105,7 @@ static int parse_hex(int (*peek)(void *userp), (*pop)(userp); n++; ch = (*peek)(userp); - } while (my_isxdigit(ch)); + } while (my_isxdigit(ch) && n < max_len); *vp = v; return n; @@ -151,11 +155,24 @@ static int scan(int (*peek)(void *userp), skip=false; } + int max_len = 0; + + while('0' <= ch && ch <= '9') + { + max_len *= 10; + max_len += ch - '0'; + ch=*fmt++; + } + + if (max_len == 0) { + max_len = INT_MAX; + } + switch (ch) { case 'x': n_chars += skip_spaces(peek, pop, userp); - if ((r = parse_hex(peek, pop, userp, &ulval)) >= 0) + if ((r = parse_hex(peek, pop, userp, &ulval, max_len)) >= 0) { if(skip==false) { @@ -169,7 +186,7 @@ static int scan(int (*peek)(void *userp), break; case 'd': n_chars += skip_spaces(peek, pop, userp); - if ((r = parse_dec(peek, pop, userp, &lval)) >= 0) + if ((r = parse_dec(peek, pop, userp, &lval, max_len)) >= 0) { if(skip==false) { @@ -194,7 +211,7 @@ static int scan(int (*peek)(void *userp), switch (ch) { case 'x': - if ((r = parse_hex(peek, pop, userp, &ulval)) >= 0) + if ((r = parse_hex(peek, pop, userp, &ulval, max_len)) >= 0) { if(skip==false) { @@ -207,7 +224,7 @@ static int scan(int (*peek)(void *userp), return n; break; case 'd': - if ((r = parse_dec(peek, pop, userp, &lval)) >= 0) + if ((r = parse_dec(peek, pop, userp, &lval, max_len)) >= 0) { if(skip==false) { @@ -228,7 +245,7 @@ static int scan(int (*peek)(void *userp), break; case 's': n_chars += skip_spaces(peek, pop, userp); - n_chars += parse_chars(peek,pop, userp,skip?0:va_arg(ap, char *), skip ); + n_chars += parse_chars(peek,pop, userp,skip?0:va_arg(ap, char *), skip, max_len); if(skip==false) { n++; diff --git a/firmware/pcm.c b/firmware/pcm.c index eebcf70e1c..be5b47673a 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -21,6 +21,7 @@ #include #include "system.h" #include "kernel.h" +#include "panic.h" /* Define LOGF_ENABLE to enable logf output in this file */ //#define LOGF_ENABLE @@ -76,8 +77,15 @@ * */ -static struct pcm_sink* sinks[1] = { +#ifdef USB_ENABLE_IAP +extern struct pcm_sink iap_pcm_sink; +#endif + +static struct pcm_sink* sinks[PCM_SINK_NUM] = { [PCM_SINK_BUILTIN] = &builtin_pcm_sink, +#ifdef USB_ENABLE_IAP + [PCM_SINK_IAP] = &iap_pcm_sink, +#endif }; static enum pcm_sink_ids cur_sink = PCM_SINK_BUILTIN; @@ -247,7 +255,7 @@ void pcm_init(void) { logf("pcm_init"); - for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { + for(size_t i = 0; i < PCM_SINK_NUM; i += 1) { struct pcm_sink* sink = sinks[i]; sink->pending_freq = sink->caps.default_freq; sink->configured_freq = -1U; @@ -261,7 +269,7 @@ void pcm_postinit(void) { logf("pcm_postinit"); - for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { + for(size_t i = 0; i < PCM_SINK_NUM; i += 1) { struct pcm_sink* sink = sinks[i]; sink->ops.postinit(); sink->pcm_is_ready = true; @@ -291,6 +299,57 @@ const struct pcm_sink_caps* pcm_current_sink_caps(void) return pcm_sink_caps(pcm_current_sink()); } +bool pcm_switch_sink(enum pcm_sink_ids sink) +{ + logf("pcm_switch_sink %d to %d", cur_sink, sink); + if(sink >= PCM_SINK_NUM) { + return false; + } + + if(cur_sink == sink) { + return true; + } + + /* + * If PCM_SINK_NUM == 1, GCC 9.5 can infer that cur_sink + * must be nonzero here (because of the above checks) and + * issue a -Warray-bounds warning. This only happens on + * some architectures (ARM), and oddly enough, only when + * cur_sink is an enum type. + * + * Since this situation isn't possible outside of memory + * corruption we can just tell the compiler to assume it + * can't happen. This avoids the warning, and saves a bit + * of code size since none of the code below is reachable + * when there's only one PCM sink. + */ + ASSUME(cur_sink < PCM_SINK_NUM); + + /* save current sink before switching */ + struct pcm_sink* old_sink = sinks[cur_sink]; + + /* update sink index */ + cur_sink = sink; + /* synchronize frequency */ + unsigned long cur_sampr = old_sink->caps.samprs[old_sink->pending_freq]; + pcm_set_frequency(cur_sampr); + pcm_apply_settings(); + /* when playing, continue playing on new sink */ + if(pcm_playing) { + old_sink->ops.stop(); + /* need more */ + const void *start; + size_t size; + if(pcm_get_more_int(&start, &size)) { + pcm_play_dma_start_int(start, size); + } else { + pcm_play_stop_int(); + } + } + + return true; +} + void pcm_play_data(pcm_play_callback_type get_more, pcm_status_callback_type status_cb, const void *start, size_t size) diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c index e49e3648b0..607db70839 100644 --- a/firmware/pcm_mixer.c +++ b/firmware/pcm_mixer.c @@ -163,7 +163,7 @@ fill_frame: if (chan->size == 0) { - if (chan->play_cbs->get_more) + if (chan->play_cbs && chan->play_cbs->get_more) { chan->play_cbs->get_more(&chan->start, &chan->size); ALIGN_AUDIOBUF(chan->start, chan->size); @@ -286,6 +286,56 @@ static void mixer_start_pcm(void) start, mix_frame_size); } +/* Notify users of samplerate change */ +static void mixer_handle_sampr_change(unsigned int sampr) +{ + for (size_t i = 0; i < ARRAYLEN(active_channels) && active_channels[i]; i += 1) + { + struct mixer_channel* chan = active_channels[i]; + + /* Notify upstreams */ + if (chan->play_cbs) + { + if (chan->play_cbs->sampr_changed) + { + chan->play_cbs->sampr_changed(sampr); + } + if (chan->play_cbs->get_more) + { + /* Remake buffer */ + const void *start = NULL; + size_t size; + chan->play_cbs->get_more(&start, &size); + if (start && size) { + chan->start = start; + chan->size = size; + chan->last_size = 0; + } else { + channel_stopped(chan); + } + } + } + /* Notify buffer monitor */ + if (chan->buf_cbs) + { + if (chan->buf_cbs->sampr_changed) + { + chan->buf_cbs->sampr_changed(sampr); + } + } + } + + /* Work out how much space we really need */ + if (sampr > SAMPR_96) + mix_frame_size = 4; + else if (sampr > SAMPR_48) + mix_frame_size = 2; + else + mix_frame_size = 1; + + mix_frame_size *= MIX_FRAME_SAMPLES * 4; +} + /** Public interfaces **/ /* Start playback on a channel */ @@ -371,6 +421,19 @@ void mixer_channel_stop(enum pcm_mixer_channel channel) pcm_play_unlock(); } +/* Switch playback sink */ +bool mixer_switch_sink(enum pcm_sink_ids sink) +{ + if(pcm_current_sink() == sink) + return true; + + if(!pcm_switch_sink(sink)) + return false; + + mixer_handle_sampr_change(SAMPR_NUM(pcm_get_frequency())); + return true; +} + /* Set channel's amplitude factor */ void mixer_channel_set_amplitude(enum pcm_mixer_channel channel, unsigned int amplitude) @@ -461,53 +524,7 @@ void mixer_set_frequency(unsigned int samplerate) return; pcm_set_frequency(samplerate); - - for (size_t i = 0; i < ARRAYLEN(active_channels) && active_channels[i]; i += 1) - { - struct mixer_channel* chan = active_channels[i]; - - /* Notify upstreams */ - if (chan->play_cbs) - { - if (chan->play_cbs->sampr_changed) - { - chan->play_cbs->sampr_changed(SAMPR_NUM(samplerate)); - } - if (chan->play_cbs->get_more) - { - /* Remake buffer */ - const void *start = NULL; - size_t size; - chan->play_cbs->get_more(&start, &size); - if (start && size) { - chan->start = start; - chan->size = size; - chan->last_size = 0; - } else { - channel_stopped(chan); - } - } - } - /* Notify buffer monitor */ - if (chan->buf_cbs) - { - if (chan->buf_cbs->sampr_changed) - { - chan->buf_cbs->sampr_changed(SAMPR_NUM(samplerate)); - } - } - } - - /* Work out how much space we really need */ - if (SAMPR_NUM(samplerate) > SAMPR_96) - mix_frame_size = 4; - else if (SAMPR_NUM(samplerate) > SAMPR_48) - mix_frame_size = 2; - else - mix_frame_size = 1; - - mix_frame_size *= MIX_FRAME_SAMPLES * 4; - + mixer_handle_sampr_change(SAMPR_NUM(pcm_get_frequency())); if (pcm_is_initialized()) pcm_apply_settings(); } diff --git a/firmware/target/arm/as3525/usb-drv-as3525.c b/firmware/target/arm/as3525/usb-drv-as3525.c index 863b553164..56216bec5e 100644 --- a/firmware/target/arm/as3525/usb-drv-as3525.c +++ b/firmware/target/arm/as3525/usb-drv-as3525.c @@ -343,37 +343,37 @@ int usb_drv_port_speed(void) return (USB_DEV_STS & USB_DEV_STS_MASK_SPD) ? 0 : 1; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; - int i = EP_NUM(endpoint); -// int d = EP_DIR(endpoint) == DIR_IN ? 0 : 1; - - if (EP_DIR(endpoint) == DIR_IN) { - USB_IEP_CTRL(i) = USB_EP_CTRL_FLUSH | - USB_EP_CTRL_SNAK | - USB_EP_CTRL_ACT | - (type << 4); - USB_DEV_EP_INTR_MASK &= ~(1< +#endif #include "config.h" diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c index 28be80aa96..8e6a8610bd 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c @@ -1489,17 +1489,21 @@ void usb_drv_set_test_mode(int mode) tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ - (void)type; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); - return tnetv_gadget_ep_enable(num, dir == DIR_IN); + int num = EP_NUM(ep); + int dir = EP_DIR(ep); + tnetv_gadget_ep_enable(num, dir == DIR_IN); } -int usb_drv_deinit_endpoint(int endpoint) { - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); - return tnetv_gadget_ep_disable(num, dir == DIR_IN); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + + int num = EP_NUM(ep); + int dir = EP_DIR(ep); + tnetv_gadget_ep_disable(num, dir == DIR_IN); } diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c index 09c1ade438..cd78de764f 100644 --- a/firmware/target/arm/usb-drv-arc.c +++ b/firmware/target/arm/usb-drv-arc.c @@ -451,6 +451,42 @@ void usb_drv_startup(void) ((type) == USB_ENDPOINT_XFER_INT ? "INTR" : "INVL")))) #endif +static void init_endpoint(int ep, int type, int mps) { + const int ep_num = EP_NUM(ep); + const int ep_dir = EP_DIR(ep); + + logf("ep init: %d %s %s", ep_num, XFER_DIR_STR(ep_dir), XFER_TYPE_STR(type)); + + struct queue_head* qh; + unsigned int ctrl = REG_ENDPTCTRL(ep_num); + if(ep_dir == DIR_IN) { + ctrl &= ~EPCTRL_TX_TYPE; + ctrl |= EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE | type << EPCTRL_TX_EP_TYPE_SHIFT; + qh = &qh_array[ep_num * 2 + 1]; + } else { + ctrl &= ~EPCTRL_RX_TYPE; + ctrl |= EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | type << EPCTRL_RX_EP_TYPE_SHIFT; + qh = &qh_array[ep_num * 2]; + } + REG_ENDPTCTRL(ep_num) = ctrl; + + if(mps == -1) { + if(type == USB_ENDPOINT_XFER_ISOC) { + mps = 1024; + } else { + mps = usb_drv_port_speed() ? 512 : 64; + } + } + if(type == USB_ENDPOINT_XFER_ISOC) + /* FIXME: we can adjust the number of packets per frame, currently use one */ + qh->max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS; + else + qh->max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; + + qh->dtd.next_td_ptr = QH_NEXT_TERMINATE; +} + + /* manual: 32.14.1 Device Controller Initialization */ void usb_drv_init(void) { @@ -494,8 +530,8 @@ void usb_drv_init(void) * will cause undefined behavior for the data pid tracking on the active * endpoint/direction. */ for(int ep_num=1;ep_nummax_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS; - else - qh->max_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; - - qh->dtd.next_td_ptr = QH_NEXT_TERMINATE; - - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + const int ep_num = EP_NUM(ep); + const int ep_dir = EP_DIR(ep); + init_endpoint(ep, ctx->type[ep_num][ep_dir], ctx->max_packet_size[ep_num][ep_dir]); } -int usb_drv_deinit_endpoint(int endpoint) { - int ep_num = EP_NUM(endpoint); - int ep_dir = EP_DIR(endpoint); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + (void)ctx; + int ep_num = EP_NUM(ep); + int ep_dir = EP_DIR(ep); logf("ep deinit: %d %s", ep_num, XFER_DIR_STR(ep_dir)); @@ -1032,8 +1038,6 @@ int usb_drv_deinit_endpoint(int endpoint) { } else { REG_ENDPTCTRL(ep_num) &= ~EPCTRL_RX_ENABLE & ~EPCTRL_RX_TYPE; } - - return 0; } static void prepare_td(struct transfer_descriptor* td, diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c index e570d07e31..c26072d70c 100644 --- a/firmware/target/arm/usb-s3c6400x.c +++ b/firmware/target/arm/usb-s3c6400x.c @@ -588,19 +588,23 @@ void INT_USB_FUNC(void) GINTSTS = sts; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); - bool out = dir == DIR_OUT; - DEPCTL(num, out) = (DEPCTL(num, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; + + bool out = epdir == DIR_OUT; + DEPCTL(epnum, out) = (DEPCTL(epnum, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) | DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp) | DEPCTL_usbactep; - return 0; } -int usb_drv_deinit_endpoint(int endpoint) { - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; } void usb_drv_cancel_all_transfers() diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c index ba055b4cb7..ab2260e48e 100644 --- a/firmware/target/arm/usb-tcc.c +++ b/firmware/target/arm/usb-tcc.c @@ -100,16 +100,17 @@ static struct tcc_ep tcc_endpoints[] = { static bool usb_drv_write_ep(struct tcc_ep *ep); static void usb_set_speed(int); -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ - - tcc_endpoints[EP_NUM(endpoint)].dir = EP_DIR(endpoint) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + tcc_endpoints[EP_NUM(ep)].dir = EP_DIR(ep) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; } -int usb_drv_deinit_endpoint(int endpoint) { - tcc_endpoints[EP_NUM(endpoint)].dir = -1; - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + tcc_endpoints[EP_NUM(ep)].dir = -1; } static inline void pullup_on(void) diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c index c923d8d97a..13542b0a5d 100644 --- a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c @@ -836,14 +836,15 @@ void usb_drv_cancel_all_transfers(void) restore_irq(flags); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)endpoint; - (void)type; - (void)max_packet_size; /* FIXME: support max packet size override */ - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + (void)ep; } -int usb_drv_deinit_endpoint(int endpoint) { - (void)endpoint; - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + (void)ep; } diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c index 1e44a0a7aa..28a8d3619a 100644 --- a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c @@ -1192,29 +1192,29 @@ void usb_drv_cancel_all_transfers(void) restore_irq(flags); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ - (void)type; - - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + int num = EP_NUM(ep); + int dir = EP_DIR(ep); int index = num * 2 + (dir == DIR_OUT ? 1 : 0); endpoints[index].allocated = true; if(dir == DIR_IN) REG_USB_INTRINE |= USB_INTR_EP(num); else REG_USB_INTROUTE |= USB_INTR_EP(num); - return 0; } -int usb_drv_deinit_endpoint(int endpoint) { - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + int num = EP_NUM(ep); + int dir = EP_DIR(ep); int index = num * 2 + (dir == DIR_OUT ? 1 : 0); endpoints[index].allocated = false; if(dir == DIR_IN) REG_USB_INTRINE &= ~USB_INTR_EP(num); else REG_USB_INTROUTE &= ~USB_INTR_EP(num); - return 0; } diff --git a/firmware/usb.c b/firmware/usb.c index 2645d3b290..406a635140 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -86,7 +86,12 @@ static int usb_mmc_countdown = 0; #ifndef USB_EXTRA_STACK # define USB_EXTRA_STACK 0x0 /*Define in firmware/export/config/[target].h*/ #endif -static long usb_stack[(DEFAULT_STACK_SIZE*4 + DUMP_BMP_LINESIZE + USB_EXTRA_STACK)/sizeof(long)]; +#ifdef USB_ENABLE_IAP +#define IAP_EXTRA_STACK DEFAULT_STACK_SIZE*2 +#else +#define IAP_EXTRA_STACK 0 +#endif +static long usb_stack[(DEFAULT_STACK_SIZE*4 + DUMP_BMP_LINESIZE + IAP_EXTRA_STACK + USB_EXTRA_STACK)/sizeof(long)]; static const char usb_thread_name[] = "usb"; static unsigned int usb_thread_entry = 0; static bool usb_monitor_enabled = false; @@ -214,6 +219,9 @@ static inline void usb_configure_drivers(int for_state) #ifdef USB_ENABLE_AUDIO usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 2)); // while "always" or "only in charge-only mode" #endif /* USB_ENABLE_AUDIO */ +#ifdef USB_ENABLE_IAP + usb_core_enable_driver(USB_DRIVER_IAP, false); +#endif #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); @@ -233,6 +241,9 @@ static inline void usb_configure_drivers(int for_state) #ifdef USB_ENABLE_AUDIO usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 3)); // while "always" or "only in mass-storage mode" #endif /* USB_ENABLE_AUDIO */ +#ifdef USB_ENABLE_IAP + usb_core_enable_driver(USB_DRIVER_IAP, true); +#endif #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); #endif @@ -255,23 +266,14 @@ static inline void usb_slave_mode(bool on) if(on) { - trigger_cpu_boost(); -#ifdef HAVE_PRIORITY_SCHEDULING - thread_set_priority(thread_self(), PRIORITY_REALTIME); -#endif disk_unmount_all(); } else { -#ifdef HAVE_PRIORITY_SCHEDULING - thread_set_priority(thread_self(), PRIORITY_SYSTEM); -#endif /* Entered exclusive mode */ rc = disk_mount_all(); if(rc <= 0) /* no partition */ panicf("mount: %d",rc); - - cancel_cpu_boost(); } } diff --git a/firmware/usbstack/iap/audio.c b/firmware/usbstack/iap/audio.c new file mode 100644 index 0000000000..d01b133d69 --- /dev/null +++ b/firmware/usbstack/iap/audio.c @@ -0,0 +1,268 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "core_alloc.h" +#include "pcm-internal.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" +#include "system.h" +#include "usb_drv.h" + +#include "../usb_iap.h" +#include "buffer.h" +#include "libiap/iap.h" +#include "macros.h" +#include "platform.h" + +static const unsigned long samprs[] = { + SAMPR_48, + SAMPR_44, +}; + +struct StagingBuffer { + struct IAPAllocResult buf; + uint16_t cursor; +}; +static struct StagingBuffer staging_buffers[USB_BATCH_SLOTS + 1]; /* +1 for silent buffer */ +static int staging_buffer_index; + +#define zero_buffer (staging_buffers[USB_BATCH_SLOTS]) + +static const uint8_t* pulled_buf; +static size_t pulled_buf_size; +static size_t pulled_buf_cursor; +static int8_t set_freq; /* requested freq from rockbox */ +static int8_t cur_freq; /* requested freq from accessory */ +static uint8_t packet_count; + +static bool enabled; +static bool exhausted; +static bool track_attrs_sent; + +extern struct pcm_sink iap_pcm_sink; + +static void sink_set_freq(uint16_t freq) { + LOG("freq=%d", freq); + + track_attrs_sent = true; + + set_freq = freq; + + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_select_sampr(ctx, samprs[freq]), ); + _iap_release_ctx(); +} + +static size_t calc_packet_size(uint8_t cur_sampr, uint8_t packet_count) { + /* packet size calculation + * (4 = sizeof(int16_t) * channels) + * (1000 = usb frames per second) + * 48000Hz: + * 48000 * 4 / 1000 = 192.0 + * => 192 + ... + * 44100Hz: + * 44100 * 4 / 1000 = 176.4 + * => (9 * 176 + 180) + (... + */ + if(cur_sampr == 0) { + /*48k*/ + return 192; + } else { + /*44.1k*/ + return packet_count % 10 == 0 ? 180 : 176; + } +} + +static void batch_get_more(const void** ptr, size_t* len) { + const size_t packet_size = calc_packet_size(cur_freq, packet_count); +#if 0 + const int cur_frame = usb_drv_get_frame_number(); + LOG("ex=%d set=%d cur=%d", exhausted, set_freq, cur_freq); +#endif + +start: + if(exhausted || cur_freq != set_freq) { + *ptr = zero_buffer.buf.ptr; + *len = packet_size; + packet_count += 1; + return; + } + + if(pulled_buf_cursor == pulled_buf_size) { + /* run out of previously pulled data. + * let's start filling them, by requesting new data from upstream */ + if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, (const void**)&pulled_buf, &pulled_buf_size)) { + /* no more data, but we have to keep sending something as long as the audio stream interface is enabled */ + exhausted = true; + goto start; + } + + /* pushing_{buf,buf_size} are filled. reset cursor and continue filling */ + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + pulled_buf_cursor = 0; + } + + /* fill this single packet */ + struct StagingBuffer* stage = &staging_buffers[staging_buffer_index]; + const size_t copy = MIN(packet_size - stage->cursor, pulled_buf_size - pulled_buf_cursor); + memcpy(stage->buf.ptr + stage->cursor, pulled_buf + pulled_buf_cursor, copy); + pulled_buf_cursor += copy; + stage->cursor += copy; +#define AUDIO_STAT 0 +#if AUDIO_STAT == 1 + static int last_hz; + static int sample; +#endif + if(stage->cursor == packet_size) { + *ptr = stage->buf.ptr; + *len = stage->cursor; + stage->cursor = 0; + staging_buffer_index = (staging_buffer_index + 1) % USB_BATCH_SLOTS; + packet_count += 1; +#if AUDIO_STAT == 1 + sample += packet_size / 4; + if(current_tick >= last_hz + HZ) { + logf("pushed %d %d", packet_size, sample); + sample = 0; + last_hz = current_tick; + } +#endif + } else { /* pushing_buf_cursor == pushing_buf_size */ + goto start; + } +} + +static void sink_play(const void* addr, size_t size) { + LOG("play"); + + pulled_buf = addr; + pulled_buf_size = size; + pulled_buf_cursor = 0; + exhausted = false; + + /* resolve pending play request before sending TrackNewAudioAttributes (by sink_set_freq). + * some accessories will get confused when receiving it while waiting for an ack */ + struct IAPContext* ctx = _iap_acquire_ctx(true); + struct Platform* plt = ctx->platform; + if(plt->control_pending) { + check_act(iap_control_response(ctx, plt->pending_control, true), ); + plt->control_pending = false; + } + _iap_release_ctx(); + + /* rockbox only calls set_freq when changes occur, but we must call + * set_freq(and iap_select_sampr) at least once per connection. */ + if(!track_attrs_sent) { + sink_set_freq(iap_pcm_sink.configured_freq); + } +} + +static void sink_stop(void) { + LOG("stop"); + /* we don't call usb_drv_batch_stop() here, + * because we need to send something, even not playing. */ +} + +static void sink_nop(void) { +} + +struct pcm_sink iap_pcm_sink = { + .caps = { + .samprs = samprs, + .num_samprs = ARRAYLEN(samprs), + .default_freq = 0, + }, + .ops = { + .init = sink_nop, + .postinit = sink_nop, + .set_freq = sink_set_freq, + .lock = sink_nop, + .unlock = sink_nop, + .play = sink_play, + .stop = sink_stop, + }, +}; + +bool iap_audio_init(void) { + check_act(usb_drv_batch_init(AS_EP_IN, batch_get_more) == 0, return false); + + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + check_act(iap_alloc_usb_send_buffer(AS_PACKET_SIZE, &staging_buffers[i].buf), goto error); + staging_buffers[i].cursor = 0; + } + staging_buffer_index = 0; + + memset(zero_buffer.buf.ptr, 0, AS_PACKET_SIZE); + + set_freq = -1; + cur_freq = -2; /* something <0 && !=set_freq */ + enabled = false; + exhausted = true; + track_attrs_sent = false; + packet_count = 0; + + return true; + +error: + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + if(staging_buffers[i].buf.ptr != NULL) { + core_free(staging_buffers[i].buf.handle); + staging_buffers[i].buf.ptr = NULL; + } + } + return false; +} + +bool iap_audio_deinit(void) { + check_act(usb_drv_batch_deinit() == 0, ); + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + core_free(staging_buffers[i].buf.handle); + staging_buffers[i].buf.ptr = NULL; + } + return true; +} + +bool iap_audio_enable(void) { + LOG("enabled=%d", enabled); + check_act(enabled || usb_drv_batch_start() == 0, return false); + enabled = true; + return true; +} + +bool iap_audio_disable(void) { + LOG("enabled=%d", enabled); + check_act(!enabled || usb_drv_batch_stop() == 0, return false); + enabled = false; + return true; +} + +bool iap_audio_set_sampr(uint32_t sampr) { + uint16_t freq = 0; + for(; freq < ARRAYLEN(samprs); freq += 1) { + if(samprs[freq] == sampr) { + break; + } + } + check_act(freq < ARRAYLEN(samprs), return false); + cur_freq = freq; + + LOG("sampr=%lu, set_freq=%d cur_freq=%d", sampr, set_freq, cur_freq); + + return true; +} diff --git a/firmware/usbstack/iap/audio.h b/firmware/usbstack/iap/audio.h new file mode 100644 index 0000000000..334ce41ebb --- /dev/null +++ b/firmware/usbstack/iap/audio.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include +#include + +bool iap_audio_init(void); +bool iap_audio_deinit(void); +bool iap_audio_enable(void); +bool iap_audio_disable(void); +bool iap_audio_set_sampr(uint32_t sampr); diff --git a/firmware/usbstack/iap/buffer.c b/firmware/usbstack/iap/buffer.c new file mode 100644 index 0000000000..32b6cbdcfd --- /dev/null +++ b/firmware/usbstack/iap/buffer.c @@ -0,0 +1,47 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "core_alloc.h" + +#include "buffer.h" +#include "macros.h" + +bool iap_alloc_buffer(size_t size, struct IAPAllocResult* result) { + int handle = core_alloc_ex(size, &buflib_ops_locked); + check_act(handle > 0, return false); + uint8_t* ptr = core_get_data(handle); + + result->handle = handle; + result->ptr = ptr; + return true; +} + +bool iap_alloc_usb_send_buffer(size_t size, struct IAPAllocResult* result) { + /* + 31 to handle worst-case misalignment */ + check_act(iap_alloc_buffer(size + 31, result), return false); + + /* align to 32 */ + result->ptr = (void*)((uintptr_t)(result->ptr + 31) & 0xffffffe0); + /* uncached */ +#if defined(UNCACHED_ADDR) && CONFIG_CPU != AS3525 + result->ptr = UNCACHED_ADDR(result->ptr); +#endif + + return true; +} diff --git a/firmware/usbstack/iap/buffer.h b/firmware/usbstack/iap/buffer.h new file mode 100644 index 0000000000..2620fe3960 --- /dev/null +++ b/firmware/usbstack/iap/buffer.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include +#include + +struct IAPAllocResult { + void* ptr; + int handle; +}; + +bool iap_alloc_buffer(size_t size, struct IAPAllocResult* result); +bool iap_alloc_usb_send_buffer(size_t size, struct IAPAllocResult* result); diff --git a/firmware/usbstack/iap/debug.c b/firmware/usbstack/iap/debug.c new file mode 100644 index 0000000000..80aa02741e --- /dev/null +++ b/firmware/usbstack/iap/debug.c @@ -0,0 +1,89 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 +#include +#include + +#include "logf.h" +#include "system.h" +#include "tick.h" + +#include "font.h" +#include "lcd.h" + +#define MAX_COLS 64 + +static int rows; +static int columns; +static unsigned count; + +static void update_color(void) { + unsigned avail = rows - 1; + if((count % (avail * 2)) > avail) { + lcd_set_drawinfo(DRMODE_SOLID, LCD_BLACK, LCD_WHITE); + } else { + lcd_set_drawinfo(DRMODE_SOLID, LCD_WHITE, LCD_BLACK); + } +} + +void iap_lcd_scatter(const char* fmt, ...) { + if(rows == 0) { + int w, h; + font_getstringsize((unsigned char*)"A", &w, &h, FONT_SYSFIXED); + columns = MIN(LCD_WIDTH / w, MAX_COLS); + rows = LCD_HEIGHT / h; + } + + va_list ap; + char buf[256]; + va_start(ap, fmt); + int len = vsnprintf(buf, sizeof(buf), (char*)fmt, ap); + va_end(ap); + + logf("%s", buf); + + lcd_set_backdrop(NULL); + lcd_setfont(FONT_SYSFIXED); + + update_color(); + lcd_putsf(0, 0, (unsigned char*)"count %u", count); + for(int i = 0; i < len;) { + char line[MAX_COLS]; + int copy = MIN(len - i, columns - 1); + memcpy(line, buf + i, copy); + memset(line + copy, ' ', columns - 1 - copy); + line[columns - 1] = '\0'; + update_color(); + lcd_puts(0, 1 + count % (rows - 1), (unsigned char*)line); + count += 1; + i += copy; + } + lcd_update(); +} + +static unsigned long timestamp_epoch; + +unsigned long iap_debug_timestamp(void) { + return current_tick - timestamp_epoch; +} + +void iap_debug_reset_timestamp(void) { + timestamp_epoch = current_tick; +} diff --git a/firmware/usbstack/iap/debug.h b/firmware/usbstack/iap/debug.h new file mode 100644 index 0000000000..034a050f7f --- /dev/null +++ b/firmware/usbstack/iap/debug.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once + +/* debug switches */ +#define DEBUG_ENABLE_INFO 0 +#define DEBUG_ENABLE_ERROR 1 +#define DEBUG_LCD_PRINT 0 +#define DEBUG_DUMP_TX 0 +#define DEBUG_DUMP_RX 0 +#define DEBUG_HEXDUMP_NOLIMIT 0 + +#if DEBUG_ENABLE_INFO == 1 || DEBUG_ENABLE_ERROR == 1 +#define LOGF_ENABLE +#include "logf.h" +#endif + +void iap_lcd_scatter(const char* fmt, ...); +unsigned long iap_debug_timestamp(void); +void iap_debug_reset_timestamp(void); + +#if DEBUG_LCD_PRINT == 1 +#undef logf +#define logf(...) iap_lcd_scatter(__VA_ARGS__) +#endif diff --git a/firmware/usbstack/iap/libiap-sync.sh b/firmware/usbstack/iap/libiap-sync.sh new file mode 100755 index 0000000000..030c29f370 --- /dev/null +++ b/firmware/usbstack/iap/libiap-sync.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +url=https://github.com/mojyack/libiap.git +src=/tmp/libiap +dst=$PWD/libiap + +if [[ ! -e $src ]]; then + git clone "$url" "$src" +else + git -C "$src" pull +fi + +rsync --recursive --delete "$src/src/iap/" "$dst/" --exclude /README.rockbox +cp "$src/LICENSE" "$dst/LICENSE" +git -C "$src" rev-parse HEAD > "$dst/HEAD" diff --git a/firmware/usbstack/iap/libiap/HEAD b/firmware/usbstack/iap/libiap/HEAD new file mode 100644 index 0000000000..30d689eb0d --- /dev/null +++ b/firmware/usbstack/iap/libiap/HEAD @@ -0,0 +1 @@ +ad06cdfc66f5db55d16b741df265269309105af3 diff --git a/firmware/usbstack/iap/libiap/LICENSE b/firmware/usbstack/iap/libiap/LICENSE new file mode 100644 index 0000000000..fb320d67e7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 mojyack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/firmware/usbstack/iap/libiap/README.rockbox b/firmware/usbstack/iap/libiap/README.rockbox new file mode 100644 index 0000000000..e6dbf7f555 --- /dev/null +++ b/firmware/usbstack/iap/libiap/README.rockbox @@ -0,0 +1,5 @@ +Files under this directory were imported from libiap(https://github.com/mojyack/libiap) except for: +- README.rockbox: this file, Rockbox specific notes +- HEAD: libiap commit hash + +To sync with the upstream, use ../libiap-sync.sh script. diff --git a/firmware/usbstack/iap/libiap/bool.h b/firmware/usbstack/iap/libiap/bool.h new file mode 100644 index 0000000000..f94595ede1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/bool.h @@ -0,0 +1,5 @@ +#pragma once + +typedef unsigned char IAPBool; +#define iap_true 1 +#define iap_false 0 diff --git a/firmware/usbstack/iap/libiap/constants.h b/firmware/usbstack/iap/libiap/constants.h new file mode 100644 index 0000000000..2dca15d05f --- /dev/null +++ b/firmware/usbstack/iap/libiap/constants.h @@ -0,0 +1,11 @@ +#pragma once + +#define HID_BUFFER_SIZE 1024 +#define SEND_BUFFER_SIZE 767 /* 0x2ff - 1: input_report_size_table_hs[0x0C].size - sizeof(report_id) */ + +enum IAPPhase { + IAPPhase_Connected = 0, /* initial state, waiting for StartIDPS */ + IAPPhase_IDPS, /* idps started */ + IAPPhase_Auth, /* idps completed, authenticating accessory */ + IAPPhase_Authed, /* authentication completed, processing requests */ +}; diff --git a/firmware/usbstack/iap/libiap/context.h b/firmware/usbstack/iap/libiap/context.h new file mode 100644 index 0000000000..f7e6ca3600 --- /dev/null +++ b/firmware/usbstack/iap/libiap/context.h @@ -0,0 +1,107 @@ +#pragma once +#include +#include + +#include "bool.h" +#include "notification.h" +#include "platform.h" + +struct IAPContext; +struct IAPSpan; + +typedef IAPBool (*IAPOnSendComplete)(struct IAPContext* ctx); + +struct IAPOpts { + /* indicate usb transport is highspeed */ + IAPBool usb_highspeed : 1; + /* needed to support some accessories which don't set correct hid report id */ + IAPBool ignore_hid_report_id : 1; + /* limit packet size while sending artworks for stability */ + IAPBool artwork_single_report : 1; + /* dump each packets */ + IAPBool enable_packet_dump : 1; +}; + +struct IAPContextButtons { + IAPBool play_pause : 1; + IAPBool volume_up : 1; + IAPBool volume_down : 1; + IAPBool next_track : 1; + IAPBool prev_track : 1; + IAPBool next_album : 1; + IAPBool prev_album : 1; + IAPBool stop : 1; + IAPBool play : 1; + IAPBool pause : 1; + IAPBool mute_toggle : 1; + IAPBool next_chapter : 1; + IAPBool prev_chapter : 1; + IAPBool next_playlist : 1; + IAPBool prev_playlist : 1; + IAPBool shuffle_advance : 1; + IAPBool repeat_advance : 1; +}; + +struct IAPActiveEvent; + +typedef IAPBool (*IAPActiveEventReadyCallback)(struct IAPContext* ctx, struct IAPActiveEvent* event); + +struct IAPActiveEvent { + IAPActiveEventReadyCallback callback; + union { + uint32_t sampr; + struct { + struct IAPPlatformPendingControl control; + IAPBool result; + } control_response; + }; +}; + +struct IAPContext { + /* set by user */ + void* platform; /* opaque to platform functions */ + struct IAPOpts opts; /* options */ + + /* iap_feed_hid_report */ + uint8_t* hid_recv_buf; + size_t hid_recv_buf_cursor; + /* _iap_send_packet, _iap_send_hid_reports */ + uint8_t* send_buf; + size_t send_buf_sending_cursor; + size_t send_buf_sending_range_begin; + size_t send_buf_sending_range_end; + /* _iap_send_next_report */ + /* for library-oriented, manageable events */ + IAPOnSendComplete on_send_complete; + /* for user-oriented, unexpectable events */ + struct IAPActiveEvent active_events[2]; + /* _iap_feed_packet */ + int32_t handling_trans_id; + /* iap.c */ + struct IAPContextButtons context_button_state; + struct IAPPlatformArtwork artwork; + size_t artwork_cursor; + uint16_t trans_id; + uint16_t artwork_chunk_index; + int32_t artwork_trans_id; + uint16_t artwork_data_command; + uint8_t artwork_data_lingo; + uint8_t trans_id_support; /* TransIDSupport */ + /* notification.c */ + /* DisplayRemote::SetRemoteEventNotification */ + uint32_t enabled_notifications_3; + uint32_t notifications_3; + /* ExtendedInterface::SetPlayStatusChangeNotification */ + uint32_t enabled_notifications_4; + uint32_t notifications_4; + /* notification data */ + struct _IAPNotifyState notification_data; + uint8_t notification_tick; + /* _iap_send_hid_reports */ + uint8_t* hid_send_staging_buf; + IAPBool send_busy : 1; + IAPBool flushing_notifications : 1; + IAPBool waiting_for_audio_attrs_ack : 1; + + uint8_t phase; /* IAPPhase */ +}; diff --git a/firmware/usbstack/iap/libiap/datetime.h b/firmware/usbstack/iap/libiap/datetime.h new file mode 100644 index 0000000000..00f0b13336 --- /dev/null +++ b/firmware/usbstack/iap/libiap/datetime.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPDateTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t seconds; +}; diff --git a/firmware/usbstack/iap/libiap/debug.c b/firmware/usbstack/iap/libiap/debug.c new file mode 100644 index 0000000000..c6386eb82a --- /dev/null +++ b/firmware/usbstack/iap/libiap/debug.c @@ -0,0 +1,1052 @@ +#include +#include + +#include "endian.h" +#include "macros.h" +#include "span.h" +#include "spec/iap.h" + +#define entry(lingo, command) [IAP##lingo##CommandID_##command] = #command + +static const char* strs_general[] = { + entry(General, RequestIdentify), + entry(General, Identify), + entry(General, IPodAck), + entry(General, RequestExtendedInterfaceMode), + entry(General, ReturnExtendedInterfaceMode), + entry(General, EnterExtendedInterfaceMode), + entry(General, ExitExtendedInterfaceMode), + entry(General, RequestIPodName), + entry(General, ReturnIPodName), + entry(General, RequestIPodSoftwareVersion), + entry(General, ReturnIPodSoftwareVersion), + entry(General, RequestIPodSerialNum), + entry(General, ReturnIPodSerialNum), + entry(General, RequestIPodModelNum), + entry(General, ReturnIPodModelNum), + entry(General, RequestLingoProtocolVersion), + entry(General, ReturnLingoProtocolVersion), + entry(General, RequestTransportMaxPayloadSize), + entry(General, ReturnTransportMaxPayloadSize), + entry(General, IdentifyDeviceLingoes), + entry(General, GetAccessoryAuthenticationInfo), + entry(General, RetAccessoryAuthenticationInfo), + entry(General, AckAccessoryAuthenticationInfo), + entry(General, GetAccessoryAuthenticationSignature), + entry(General, RetAccessoryAuthenticationSignature), + entry(General, AckAccessoryAuthenticationStatus), + entry(General, GetIPodAuthenticationInfo), + entry(General, RetIPodAuthenticationInfo), + entry(General, AckIPodAuthenticationInfo), + entry(General, GetIPodAuthenticationSignature), + entry(General, RetIPodAuthenticationSignature), + entry(General, AckIPodAuthenticationStatus), + entry(General, NotifyIPodStateChange), + entry(General, GetIPodOptions), + entry(General, RetIPodOptions), + entry(General, GetAccessoryInfo), + entry(General, RetAccessoryInfo), + entry(General, GetIPodPreferences), + entry(General, RetIPodPreferences), + entry(General, SetIPodPreferences), + entry(General, GetUIMode), + entry(General, RetUIMode), + entry(General, SetUIMode), + entry(General, StartIDPS), + entry(General, SetFIDTokenValues), + entry(General, AckFIDTokenValues), + entry(General, EndIDPS), + entry(General, IDPSStatus), + entry(General, OpenDataSessionForProtocol), + entry(General, CloseDataSession), + entry(General, AccessoryAck), + entry(General, AccessoryDataTransfer), + entry(General, IPodDataTransfer), + entry(General, SetAccessoryStatusNotification), + entry(General, RetAccessoryStatusNotification), + entry(General, AccessoryStatusNotification), + entry(General, SetEventNotification), + entry(General, IPodNotification), + entry(General, GetIPodOptionsForLingo), + entry(General, RetIPodOptionsForLingo), + entry(General, GetEventNotification), + entry(General, RetEventNotification), + entry(General, GetSupportedEventNotification), + entry(General, CancelCommand), + entry(General, RetSupportedEventNotification), + entry(General, SetAvailableCurrent), + entry(General, SetInternalBatteryChargingState), + entry(General, RequestApplicationLaunch), + entry(General, GetNowPlayingApplicationBundleName), + entry(General, RetNowPlayingApplicationBundleName), + entry(General, GetLocalizationInfo), + entry(General, RetLocalizationInfo), + entry(General, RequestWiFiConnectionInfo), + entry(General, WiFiConnectionInfo), +}; +static const char* strs_simple[] = { + entry(SimpleRemote, ContextButtonStatus), + entry(SimpleRemote, IPodAck), + entry(SimpleRemote, ImageButtonStatus), + entry(SimpleRemote, VideoButtonStatus), + entry(SimpleRemote, AudioButtonStatus), + entry(SimpleRemote, IPodOutButtonStatus), + entry(SimpleRemote, RotationInputStatus), + entry(SimpleRemote, RadioButtonStatus), + entry(SimpleRemote, CameraButtonStatus), + entry(SimpleRemote, RegisterDescriptor), + entry(SimpleRemote, IPodHIDReport), + entry(SimpleRemote, AccessoryHIDReport), + entry(SimpleRemote, UnregisterDescriptor), + entry(SimpleRemote, VoiceOverEvent), + entry(SimpleRemote, GetVoiceOverParameter), + entry(SimpleRemote, RetVoiceOverParameter), + entry(SimpleRemote, SetVoiceOverParameter), + entry(SimpleRemote, GetCurrentVoiceOverItemProperty), + entry(SimpleRemote, RetCurrentVoiceOverItemProperty), + entry(SimpleRemote, SetVoiceOverContext), + entry(SimpleRemote, VoiceOverParameterChanged), + entry(SimpleRemote, AccessoryAck), +}; +static const char* strs_display[] = { + entry(DisplayRemote, IPodAck), + entry(DisplayRemote, GetCurrentEQProfileIndex), + entry(DisplayRemote, RetCurrentEQProfileIndex), + entry(DisplayRemote, SetCurrentEQProfileIndex), + entry(DisplayRemote, GetNumEQProfiles), + entry(DisplayRemote, RetNumEQProfiles), + entry(DisplayRemote, GetIndexedEQProfileName), + entry(DisplayRemote, RetIndexedEQProfileName), + entry(DisplayRemote, SetRemoteEventNotification), + entry(DisplayRemote, RemoteEventNotification), + entry(DisplayRemote, GetRemoteEventStatus), + entry(DisplayRemote, RetRemoteEventStatus), + entry(DisplayRemote, GetIPodStateInfo), + entry(DisplayRemote, RetIPodStateInfo), + entry(DisplayRemote, SetIPodStateInfo), + entry(DisplayRemote, GetPlayStatus), + entry(DisplayRemote, RetPlayStatus), + entry(DisplayRemote, SetCurrentPlayingTrack), + entry(DisplayRemote, GetIndexedPlayingTrackInfo), + entry(DisplayRemote, RetIndexedPlayingTrackInfo), + entry(DisplayRemote, GetNumPlayingTracks), + entry(DisplayRemote, RetNumPlayingTracks), + entry(DisplayRemote, GetArtworkFormats), + entry(DisplayRemote, RetArtworkFormats), + entry(DisplayRemote, GetTrackArtworkData), + entry(DisplayRemote, RetTrackArtworkData), + entry(DisplayRemote, GetPowerBatteryState), + entry(DisplayRemote, RetPowerBatteryState), + entry(DisplayRemote, GetSoundCheckState), + entry(DisplayRemote, RetSoundCheckState), + entry(DisplayRemote, SetSoundCheckState), + entry(DisplayRemote, GetTrackArtworkTimes), + entry(DisplayRemote, RetTrackArtworkTimes), + entry(DisplayRemote, CreateGeniusPlaylist), + entry(DisplayRemote, IsGeniusAvailableForTrack), +}; +static const char* strs_ext[] = { + entry(ExtendedInterface, IPodAck), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterInfo), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterInfo), + entry(ExtendedInterface, SetCurrentPlayingTrackChapter), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterPlayStatus), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterPlayStatus), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterName), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterName), + entry(ExtendedInterface, GetAudiobookSpeed), + entry(ExtendedInterface, RetAudiobookSpeed), + entry(ExtendedInterface, SetAudiobookSpeed), + entry(ExtendedInterface, GetIndexedPlayingTrackInfo), + entry(ExtendedInterface, ReturnIndexedPlayingTrackInfo), + entry(ExtendedInterface, GetArtworkFormats), + entry(ExtendedInterface, RetArtworkFormats), + entry(ExtendedInterface, GetTrackArtworkData), + entry(ExtendedInterface, RetTrackArtworkData), + entry(ExtendedInterface, RequestProtocolVersion), + entry(ExtendedInterface, ReturnProtocolVersion), + entry(ExtendedInterface, RequestIPodName), + entry(ExtendedInterface, ReturnIPodName), + entry(ExtendedInterface, ResetDBSelection), + entry(ExtendedInterface, SelectDBRecord), + entry(ExtendedInterface, GetNumberCategorizedDBRecords), + entry(ExtendedInterface, ReturnNumberCategorizedDBRecords), + entry(ExtendedInterface, RetrieveCategorizedDatabaseRecords), + entry(ExtendedInterface, ReturnCategorizedDatabaseRecords), + entry(ExtendedInterface, GetPlayStatus), + entry(ExtendedInterface, ReturnPlayStatus), + entry(ExtendedInterface, GetCurrentPlayingTrackIndex), + entry(ExtendedInterface, ReturnCurrentPlayingTrackIndex), + entry(ExtendedInterface, GetIndexedPlayingTrackTitle), + entry(ExtendedInterface, ReturnIndexedPlayingTrackTitle), + entry(ExtendedInterface, GetIndexedPlayingTrackArtistName), + entry(ExtendedInterface, ReturnIndexedPlayingTrackArtistName), + entry(ExtendedInterface, GetIndexedPlayingTrackAlbumName), + entry(ExtendedInterface, ReturnIndexedPlayingTrackAlbumName), + entry(ExtendedInterface, SetPlayStatusChangeNotification), + entry(ExtendedInterface, PlayStatusChangeNotification), + entry(ExtendedInterface, PlayCurrentSelection), + entry(ExtendedInterface, PlayControl), + entry(ExtendedInterface, GetTrackArtworkTimes), + entry(ExtendedInterface, RetTrackArtworkTimes), + entry(ExtendedInterface, GetShuffle), + entry(ExtendedInterface, ReturnShuffle), + entry(ExtendedInterface, SetShuffle), + entry(ExtendedInterface, GetRepeat), + entry(ExtendedInterface, ReturnRepeat), + entry(ExtendedInterface, SetRepeat), + entry(ExtendedInterface, SetDisplayImage), + entry(ExtendedInterface, GetMonoDisplayImageLimits), + entry(ExtendedInterface, ReturnMonoDisplayImageLimits), + entry(ExtendedInterface, GetNumPlayingTracks), + entry(ExtendedInterface, ReturnNumPlayingTracks), + entry(ExtendedInterface, SetCurrentPlayingTrack), + entry(ExtendedInterface, SelectSortDBRecord), + entry(ExtendedInterface, GetColorDisplayImageLimits), + entry(ExtendedInterface, ReturnColorDisplayImageLimits), + entry(ExtendedInterface, ResetDBSelectionHierarchy), + entry(ExtendedInterface, GetDBITunesInfo), + entry(ExtendedInterface, RetDBITunesInfo), + entry(ExtendedInterface, GetUIDTrackInfo), + entry(ExtendedInterface, RetUIDTrackInfo), + entry(ExtendedInterface, GetDBTrackInfo), + entry(ExtendedInterface, RetDBTrackInfo), + entry(ExtendedInterface, GetPBTrackInfo), + entry(ExtendedInterface, RetPBTrackInfo), + entry(ExtendedInterface, CreateGeniusPlaylist), + entry(ExtendedInterface, RefreshGeniusPlaylist), + entry(ExtendedInterface, IsGeniusAvailableForTrack), + entry(ExtendedInterface, GetPlaylistInfo), + entry(ExtendedInterface, RetPlaylistInfo), + entry(ExtendedInterface, PrepareUIDList), + entry(ExtendedInterface, PlayPreparedUIDList), + entry(ExtendedInterface, GetArtworkTimes), + entry(ExtendedInterface, RetArtworkTimes), + entry(ExtendedInterface, GetArtworkData), + entry(ExtendedInterface, RetArtworkData), +}; +static const char* strs_da[] = { + entry(DigitalAudio, AccessoryAck), + entry(DigitalAudio, IPodAck), + entry(DigitalAudio, GetAccessorySampleRateCaps), + entry(DigitalAudio, RetAccessorySampleRateCaps), + entry(DigitalAudio, TrackNewAudioAttributes), + entry(DigitalAudio, SetVideoDelay), +}; + +#undef entry + +#define entry(lingo, strs, size) [IAPLingoID_##lingo] = {#lingo, \ + strs, \ + size} +static struct { + const char* lingo; + const char** strs; + size_t size; +} strs[] = { + entry(General, strs_general, array_size(strs_general)), + entry(Microphone, NULL, 0), + entry(SimpleRemote, strs_simple, array_size(strs_simple)), + entry(DisplayRemote, strs_display, array_size(strs_display)), + entry(ExtendedInterface, strs_ext, array_size(strs_ext)), + entry(AccessoryPower, NULL, 0), + entry(USBHostMode, NULL, 0), + entry(RFTuner, NULL, 0), + entry(AccessoryEqualizer, NULL, 0), + entry(Sports, NULL, 0), + entry(DigitalAudio, strs_da, array_size(strs_da)), + entry(Storage, NULL, 0), + entry(IPodOut, NULL, 0), + entry(Location, NULL, 0), +}; + +#undef entry + +const char* _iap_lingo_str_or_null(uint8_t lingo) { + check_ret(lingo < array_size(strs), NULL); + return strs[lingo].lingo; +} + +const char* _iap_lingo_str(uint8_t lingo) { + const char* ret = _iap_lingo_str_or_null(lingo); + return ret ? ret : "?"; +} + +const char* _iap_command_str_or_null(uint8_t lingo, uint16_t command) { + check_ret(lingo < array_size(strs), NULL); + check_ret(command < strs[lingo].size, NULL); + return strs[lingo].strs[command]; +} + +const char* _iap_command_str(uint8_t lingo, uint16_t command) { + const char* ret = _iap_command_str_or_null(lingo, command); + return ret ? ret : "?"; +} + +IAPBool _iap_span_is_str(const struct IAPSpan* span) { + return span->size > 0 && span->ptr[span->size - 1] == '\0'; +} + +const char* _iap_span_as_str(const struct IAPSpan* span) { + return _iap_span_is_str(span) ? (char*)span->ptr : "(invalid)"; +} + +#if !defined(IAP_LOGF_MUTED) +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span) { + const char* lingo_str = _iap_lingo_str_or_null(lingo); + const char* command_str = _iap_command_str_or_null(lingo, command); + if(lingo_str == NULL) { + IAP_LOGF("?(0x%02" PRIX8 ") trans=%" PRIi32, lingo, trans_id); + return; + } else if(command_str == NULL) { + IAP_LOGF("%s:?(0x%02" PRIX8 ") trans=%" PRIi32, lingo_str, command, trans_id); + return; + } else { + IAP_LOGF("%s:%s trans=%" PRIi32, lingo_str, command_str, trans_id); + } + +#define span_read(Type) \ + const struct Type* payload = iap_span_read(&span, sizeof(*payload)); \ + check_act(payload != NULL, return); + + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPGeneralCommandID_ReturnExtendedInterfaceMode: { + span_read(IAPReturnExtendedInterfaceModePayload); + IAP_LOGF(" mode=%" PRIu8, payload->is_ext_mode); + } break; + case IAPGeneralCommandID_ReturnIPodSoftwareVersion: { + span_read(IAPReturnIPodSoftwareVersionPayload); + IAP_LOGF(" version=%" PRIu8 ".%" PRIu8 ".%" PRIu8, payload->major, payload->minor, payload->revision); + } break; + case IAPGeneralCommandID_ReturnIPodSerialNum: { + IAP_LOGF(" serial=%s", _iap_span_as_str(&span)); + } break; + case IAPGeneralCommandID_ReturnIPodModelNum: { + IAP_LOGF(" model=%s", _iap_span_as_str(&span)); + } break; + case IAPGeneralCommandID_RequestLingoProtocolVersion: { + span_read(IAPRequestLingoProtocolVersionPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo)); + } break; + case IAPGeneralCommandID_ReturnLingoProtocolVersion: { + span_read(IAPReturnLingoProtocolVersionPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo)); + IAP_LOGF(" version=%" PRIu8 ".%" PRIu8, payload->major, payload->minor); + } break; + case IAPGeneralCommandID_ReturnTransportMaxPayloadSize: { + span_read(IAPReturnTransportMaxPayloadSizePayload); + IAP_LOGF(" size=%" PRIu16, swap_16(payload->max_payload_size)); + } break; + case IAPGeneralCommandID_IdentifyDeviceLingoes: { + span_read(IAPIdentifyDeviceLingoesPayload); + uint32_t bits = swap_32(payload->lingoes_bits); + for(int i = 0; i < 16; i += 1) { + if(bits & (1u << i)) { + IAP_LOGF(" supports %s", _iap_lingo_str(i)); + } + } + IAP_LOGF(" option=0x%02" PRIX32, swap_32(payload->options)); + IAP_LOGF(" device_id=0x%04" PRIX32, swap_32(payload->device_id)); + } break; + case IAPGeneralCommandID_RetIPodOptions: { + span_read(IAPRetIPodOptionsPayload); + IAP_LOGF(" state=0x%08" PRIX64, swap_64(payload->state)); + } break; + case IAPGeneralCommandID_GetIPodPreferences: { + span_read(IAPGetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02" PRIX8, payload->class_id); + } break; + case IAPGeneralCommandID_RetIPodPreferences: { + span_read(IAPSetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02X", payload->class_id); + IAP_LOGF(" setting=0x%02X", payload->setting_id); + } break; + case IAPGeneralCommandID_SetIPodPreferences: { + span_read(IAPSetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02" PRIX8, payload->class_id); + IAP_LOGF(" setting=0x%02" PRIX8, payload->setting_id); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPGeneralCommandID_SetUIMode: { + span_read(IAPSetUIModePayload); + IAP_LOGF(" mode=0x%02" PRIX8, payload->ui_mode); + } break; + case IAPGeneralCommandID_SetFIDTokenValues: { + span_read(IAPSetFIDTokenValuesPayload); + for(int i = 0; i < payload->num_token_values; i += 1) { + check_act(span.size > sizeof(struct IAPFIDTokenValuesToken), return); + struct IAPFIDTokenValuesToken* token_header = (void*)span.ptr; + struct IAPSpan token_span = { + span.ptr, + token_header->length + 1 /* length does not include sizeof itself */, + }; + check_act(span.size >= token_span.size, return); + iap_span_read(&span, token_span.size); + + switch(token_header->type << 8 | token_header->subtype) { + case IAPFIDTokenTypes_Identify: { + /* IAPFIDTokenValuesIdentifyToken contains vla, need to parse manually */ + const struct IAPFIDTokenValuesIdentifyTokenHead* token_head = iap_span_read(&token_span, sizeof(*token_head)); + check_act(token_head != NULL, return); + print("accessory supported lingoes(%" PRIu8 "):", token_head->num_lingoes); + for(int i = 0; i < token_head->num_lingoes; i += 1) { + uint8_t lingo_id; + check_act(iap_span_read_8(&token_span, &lingo_id), return); + IAP_LOGF(" %s(0x%" PRIX8 ")", _iap_lingo_str(lingo_id), lingo_id); + } + const struct IAPFIDTokenValuesIdentifyTokenTail* token_tail = iap_span_read(&token_span, sizeof(*token_tail)); + check_act(token_tail != NULL, return); + const uint32_t opt = swap_32(token_tail->device_option); + const uint32_t id = swap_32(token_tail->device_id); + print("options=%04" PRIX32 " device_id=%04" PRIX32, opt, id); + } break; + case IAPFIDTokenTypes_AccCaps: { + const struct IAPFIDTokenValuesAccCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + const uint64_t caps = swap_64(token->caps_bits); + print("accessory caps: %" PRIX64, caps); + } break; + case IAPFIDTokenTypes_AccInfo: { + const struct IAPFIDTokenValuesAccInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + switch(token->info_type) { + case IAPFIDTokenValuesAccInfoTypes_AccName: + print("accessory name: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_FirmwareVersion: + check_act(token_span.size == 3, return); + print("accessory firmware version: %" PRIX8 ".%" PRIX8 ".%" PRIX8, token_span.ptr[0], token_span.ptr[1], token_span.ptr[2]); + break; + case IAPFIDTokenValuesAccInfoTypes_HardwareVersion: + check_act(token_span.size == 3, return); + print("accessory hardware version: %" PRIX8 ".%" PRIX8 ".%" PRIX8, token_span.ptr[0], token_span.ptr[1], token_span.ptr[2]); + break; + case IAPFIDTokenValuesAccInfoTypes_Manufacture: + print("accessory manufacture: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_ModelNumber: + print("accessory model number: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_SerialNumber: + print("accessory serial number: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize: { + uint16_t val; + check_act(iap_span_read_16(&token_span, &val), return); + print("accessory max payload size: %" PRIu16, val); + } break; + case IAPFIDTokenValuesAccInfoTypes_AccStatus: { + uint32_t val; + check_act(iap_span_read_32(&token_span, &val), return); + print("accessory status: %" PRIX32, val); + } break; + case IAPFIDTokenValuesAccInfoTypes_RFCerts: { + uint32_t val; + check_act(iap_span_read_32(&token_span, &val), return); + print("accessory rf cert: %" PRIX32, val); + } break; + } + } break; + case IAPFIDTokenTypes_IPodPreference: { + const struct IAPFIDTokenValuesIPodPreferenceToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory setting %" PRIX8 "=%" PRIX8, token->class_id, token->setting_id); + } break; + case IAPFIDTokenTypes_EAProtocol: { + const struct IAPFIDTokenValuesEAProtocolToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("ea protocol %" PRIX8 "=%s", token->protocol_index, _iap_span_as_str(&token_span)); + } break; + case IAPFIDTokenTypes_BundleSeedIDPref: { + const struct IAPFIDTokenValuesBundleSeedIDPrefToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("bundle seed id %.10s", token->bundle_seed_id_string); + } break; + case IAPFIDTokenTypes_ScreenInfo: { + const struct IAPFIDTokenValuesScreenInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("screen info:"); + IAP_LOGF(" screen size(inch): %" PRIu16 "x%" PRIu16, swap_16(token->total_screen_width_inches), swap_16(token->total_screen_height_inches)); + IAP_LOGF(" screen size(pixel): %" PRIu16 "x%" PRIu16, swap_16(token->total_screen_width_pixels), swap_16(token->total_screen_height_pixels)); + IAP_LOGF(" ipod out size(pixel): %" PRIu16 "x%" PRIu16, swap_16(token->ipod_out_screen_width_pixels), swap_16(token->ipod_out_screen_height_pixels)); + IAP_LOGF(" feature: %" PRIX8, token->screen_feature_mask); + IAP_LOGF(" gamma: %" PRIu8, token->screen_gamma_value); + } break; + case IAPFIDTokenTypes_EAProtocolMetadata: { + const struct IAPFIDTokenValuesEAProtocolMetadataToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("ea protocol metadata %" PRIX8 "=%" PRIX8, token->protocol_index, token->metadata_type); + } break; + case IAPFIDTokenTypes_AccDigitalAudioSampleRates: { + const struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory supported audio sample rates:"); + while(token_span.size > 0) { + uint32_t rate; + check_act(iap_span_read_32(&token_span, &rate), return); + IAP_LOGF(" %" PRIu32, rate); + } + } break; + case IAPFIDTokenTypes_AccDigitalAudioVideoDelay: { + const struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory video delay: %" PRIu32, token->delay); + } break; + case IAPFIDTokenTypes_MicrophoneCaps: { + const struct IAPFIDTokenValuesMicrophoneCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory microphone caps: %" PRIX32, token->caps_bits); + } break; + default: + print("unknown fid %04" PRIX16, token_header->type << 8 | token_header->subtype); + } + } + } break; + case IAPGeneralCommandID_EndIDPS: { + span_read(IAPEndIDPSPayload); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPGeneralCommandID_GetIPodOptionsForLingo: { + span_read(IAPGetIPodOptionsForLingoPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo_id)); + } break; + case IAPGeneralCommandID_RetSupportedEventNotification: { + span_read(IAPRetSupportedEventNotificationPayload); + IAP_LOGF(" mask=0x%08" PRIX64, swap_64(payload->mask)); + } break; + case IAPGeneralCommandID_SetAvailableCurrent: { + span_read(IAPSetAvailableCurrentPayload); + IAP_LOGF(" current=%" PRIu16 "mA", swap_16(payload->current_limit_ma)); + } break; + case IAPGeneralCommandID_SetEventNotification: { + span_read(IAPSetEventNotificationPayload); + IAP_LOGF(" mask=0x%08" PRIX64, swap_64(payload->mask)); + } break; + } + break; + case IAPLingoID_SimpleRemote: + switch(command) { + case IAPSimpleRemoteCommandID_ContextButtonStatus: { + uint8_t bits; + check_ret(iap_span_read_8(&span, &bits), ); + IAP_LOGF(" bits0=0x%02" PRIX8, bits); + uint8_t index = 1; + while(span.size > 0) { + iap_span_read_8(&span, &bits); + IAP_LOGF(" bits%d=0x%02" PRIX8, index, bits); + index += 1; + } + } break; + case IAPSimpleRemoteCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + } + break; + case IAPLingoID_DisplayRemote: + switch(command) { + case IAPDisplayRemoteCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex: { + span_read(IAPSetCurrentEQProfileIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPDisplayRemoteCommandID_SetRemoteEventNotification: { + span_read(IAPSetRemoteEventNotificationPayload); + IAP_LOGF(" mask=0x%04" PRIX32, swap_32(payload->mask)); + } break; + case IAPDisplayRemoteCommandID_RemoteEventNotification: { + check_ret(span.size >= 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIPodStateType_TrackTimePositionMSec: { + span_read(IAPIPodStateTrackTimePositionMSecPayload); + IAP_LOGF(" position=%" PRIu32 "ms", swap_32(payload->position_ms)); + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + span_read(IAPIPodStateTrackPlaybackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPIPodStateType_ChapterIndex: { + span_read(IAPIPodStateChapterIndexPayload); + IAP_LOGF(" chapter_index=%" PRIu16, swap_16(payload->chapter_index)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIPodStateType_PlayStatus: { + span_read(IAPIPodStatePlayStatusPayload); + IAP_LOGF(" play_status=%" PRIu8, payload->status); + } break; + case IAPIPodStateType_Volume: { + span_read(IAPIPodStateVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" volume=%" PRIu8, payload->ui_volume); + } break; + case IAPIPodStateType_Power: { + span_read(IAPIPodStatePowerPayload); + IAP_LOGF(" power_state=%" PRIu8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPIPodStateType_EQSetting: { + span_read(IAPIPodStateEQSettingPayload); + IAP_LOGF(" eq_index=%" PRIu32, swap_32(payload->eq_index)); + } break; + case IAPIPodStateType_ShuffleSetting: { + span_read(IAPIPodStateShuffleSettingPayload); + IAP_LOGF(" shuffle_state=%" PRIu8, payload->shuffle_state); + } break; + case IAPIPodStateType_RepeatSetting: { + span_read(IAPIPodStateRepeatSettingPayload); + IAP_LOGF(" repeat_state=%" PRIu8, payload->repeat_state); + } break; + case IAPIPodStateType_DateTimeSetting: { + span_read(IAPIPodStateDateTimeSettingPayload); + IAP_LOGF(" time=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hour, payload->minute); + } break; + case IAPIPodStateType_AlarmSetting: { + span_read(IAPIPodStateAlarmSettingPayload); + IAP_LOGF(" alarm"); + } break; + case IAPIPodStateType_BacklightLevel: { + span_read(IAPIPodStateBacklightLevelPayload); + IAP_LOGF(" backlight_level=%" PRIu8, payload->level); + } break; + case IAPIPodStateType_HoldSwitchState: { + span_read(IAPIPodStateHoldSwitchStatePayload); + IAP_LOGF(" hold_switch_state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_SoundCheckState: { + span_read(IAPIPodStateSoundCheckStatePayload); + IAP_LOGF(" sound_check_state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_AudiobookSpeeed: { + span_read(IAPIPodStateAudiobookSpeeedPayload); + IAP_LOGF(" audio_book_speed=%" PRIu8, payload->speed); + } break; + case IAPIPodStateType_TrackTimePositionSec: { + span_read(IAPIPodStateTrackTimePositionSecPayload); + IAP_LOGF(" position=%us", swap_16(payload->position_s)); + } break; + case IAPIPodStateType_AbsoluteVolume: { + span_read(IAPIPodStateAbsoluteVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" absolute_volume=%" PRIu8, payload->absolute_volume); + } break; + case IAPIPodStateType_TrackCaps: { + span_read(IAPIPodStateTrackCapsPayload); + IAP_LOGF(" track_caps=0x%04" PRIX32, swap_32(payload->caps)); + } break; + case IAPIPodStateType_PlaybackEngineContents: { + span_read(IAPIPodStatePlaybackEngineContentsPayload); + IAP_LOGF(" playback_engine_contents=%" PRIu32, swap_32(payload->count)); + } break; + } + } break; + case IAPDisplayRemoteCommandID_GetIPodStateInfo: { + span_read(IAPGetIPodStateInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + } break; + case IAPDisplayRemoteCommandID_RetIPodStateInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIPodStateType_TrackTimePositionMSec: { + span_read(IAPIPodStateTrackTimePositionMSecPayload); + IAP_LOGF(" position=%" PRIu32 "ms", swap_32(payload->position_ms)); + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + span_read(IAPIPodStateTrackPlaybackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPIPodStateType_ChapterIndex: { + span_read(IAPIPodStateChapterIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + IAP_LOGF(" cindex=%" PRIu16, swap_16(payload->chapter_index)); + IAP_LOGF(" ccount=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIPodStateType_PlayStatus: { + span_read(IAPIPodStatePlayStatusPayload); + IAP_LOGF(" status=%" PRIu8, payload->status); + } break; + case IAPIPodStateType_Volume: { + span_read(IAPIPodStateVolumePayload); + IAP_LOGF(" ui_volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" mute_state=%" PRIu8, payload->mute_state); + } break; + case IAPIPodStateType_Power: { + span_read(IAPIPodStatePowerPayload); + IAP_LOGF(" power_state=0x%02" PRIX8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPIPodStateType_EQSetting: { + span_read(IAPIPodStateEQSettingPayload); + IAP_LOGF(" eq_index=%" PRIu32, swap_32(payload->eq_index)); + } break; + case IAPIPodStateType_ShuffleSetting: { + span_read(IAPIPodStateShuffleSettingPayload); + IAP_LOGF(" shuffle_state=%" PRIu8, payload->shuffle_state); + } break; + case IAPIPodStateType_RepeatSetting: { + span_read(IAPIPodStateRepeatSettingPayload); + IAP_LOGF(" repeat_state=%" PRIu8, payload->repeat_state); + } break; + case IAPIPodStateType_DateTimeSetting: { + span_read(IAPIPodStateDateTimeSettingPayload); + IAP_LOGF(" time=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hour, payload->minute); + } break; + case IAPIPodStateType_AlarmSetting: { + } break; + case IAPIPodStateType_BacklightLevel: { + span_read(IAPIPodStateBacklightLevelPayload); + IAP_LOGF(" level=%" PRIu8, payload->level); + } break; + case IAPIPodStateType_HoldSwitchState: { + span_read(IAPIPodStateHoldSwitchStatePayload); + IAP_LOGF(" state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_SoundCheckState: { + span_read(IAPIPodStateSoundCheckStatePayload); + IAP_LOGF(" state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_AudiobookSpeeed: { + span_read(IAPIPodStateAudiobookSpeeedPayload); + IAP_LOGF(" speed=%" PRIu8, payload->speed); + } break; + case IAPIPodStateType_TrackTimePositionSec: { + span_read(IAPIPodStateTrackTimePositionSecPayload); + IAP_LOGF(" position=%" PRIu16 "s", swap_16(payload->position_s)); + } break; + case IAPIPodStateType_AbsoluteVolume: { + span_read(IAPIPodStateAbsoluteVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" ui_volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" absolute_volume=%" PRIu8, payload->absolute_volume); + } break; + case IAPIPodStateType_TrackCaps: { + span_read(IAPIPodStateTrackCapsPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->caps)); + } break; + case IAPIPodStateType_PlaybackEngineContents: { + span_read(IAPIPodStatePlaybackEngineContentsPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + } break; + } + } break; + case IAPDisplayRemoteCommandID_RetPlayStatus: { + span_read(IAPRetPlayStatusPayload); + IAP_LOGF(" state=0x%02" PRIX8, payload->state); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" pos=%" PRIu32 "ms", swap_32(payload->track_pos_ms)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + } break; + case IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo: { + span_read(IAPGetIndexedPlayingTrackInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + IAP_LOGF(" track=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" chapter=%" PRIu16, swap_16(payload->chapter_index)); + } break; + case IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIndexedPlayingTrackInfoType_TrackCapsInfo: { + span_read(IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->track_caps)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIndexedPlayingTrackInfoType_ChapterTimeName: { + span_read(IAPRetIndexedPlayingTrackInfoChapterTimeNamePayload); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + IAP_LOGF(" name=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_ArtistName: + case IAPIndexedPlayingTrackInfoType_AlbumName: + case IAPIndexedPlayingTrackInfoType_GenreName: + case IAPIndexedPlayingTrackInfoType_ComposerName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_Lyrics: { + span_read(IAPRetIndexedPlayingTrackInfoLyricsPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" lyrics=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_ArtworkCount: { + span_read(IAPRetIndexedPlayingTrackInfoArtworkCountPayload); + while(span.size >= sizeof(struct IAPArtworkCount)) { + const struct IAPArtworkCount* count = iap_span_read(&span, sizeof(*count)); + IAP_LOGF(" format=%" PRIu16 ", count=%" PRIu16, swap_16(count->format), swap_16(count->count)); + } + } break; + } + } break; + case IAPDisplayRemoteCommandID_RetNumPlayingTracks: { + span_read(IAPRetNumPlayingTracksPayload); + IAP_LOGF(" tracks=%" PRIu32, swap_32(payload->num_playing_tracks)); + } break; + case IAPDisplayRemoteCommandID_RetArtworkFormats: { + while(span.size >= sizeof(struct IAPArtworkFormat)) { + const struct IAPArtworkFormat* format = iap_span_read(&span, sizeof(*format)); + IAP_LOGF(" id=%" PRIu16 ", format=%" PRIu8 ", width=%" PRIu16 ", height=%" PRIu16, swap_16(format->format_id), format->pixel_format, swap_16(format->image_width), swap_16(format->image_height)); + } + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkData: { + span_read(IAPGetTrackArtworkDataPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu8, payload->format_id); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + } break; + case IAPDisplayRemoteCommandID_RetTrackArtworkData: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + if(index == 0) { + span_read(IAPRetTrackArtworkDataFirstPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->pixel_format)); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } else { + span_read(IAPRetTrackArtworkDataSubsequenctPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + } + } break; + case IAPDisplayRemoteCommandID_RetPowerBatteryState: { + span_read(IAPRetPowerBatteryStatePayload); + IAP_LOGF(" power_state=%" PRIu8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkTimes: { + span_read(IAPGetTrackArtworkTimesPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->format_id)); + IAP_LOGF(" artwork_index=%" PRIu32, swap_32(payload->artwork_index)); + IAP_LOGF(" artwork_count=%" PRIu32, swap_32(payload->artwork_count)); + } break; + case IAPDisplayRemoteCommandID_RetTrackArtworkTimes: { + while(span.size >= sizeof(uint16_t)) { + uint16_t time; + iap_span_read_16(&span, &time); + IAP_LOGF(" time=%" PRIu16, time); + } + } break; + } + break; + case IAPLingoID_ExtendedInterface: + switch(command) { + case IAPExtendedInterfaceCommandID_IPodAck: { + span_read(IAPExtendedIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, swap_16(payload->id))); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo: { + span_read(IAPReturnCurrentPlayingTrackChapterInfoPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_RetAudiobookSpeed: { + span_read(IAPRetAudiobookSpeedPayload); + IAP_LOGF(" speed=0x%02" PRIX8, payload->speed); + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo: { + span_read(IAPExtendedGetIndexedPlayingTrackInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + IAP_LOGF(" track=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" chapter=%" PRIu16, swap_16(payload->chapter_index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->track_caps)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_PodcastName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload); + IAP_LOGF(" release=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hours, payload->minutes, payload->seconds); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackDescription: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%" PRIu8, swap_16(payload->index)); + IAP_LOGF(" desc=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%u" PRIu16, swap_16(payload->index)); + IAP_LOGF(" lyrics=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackGenre: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackComposer: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount: { + span_read(IAPRetIndexedPlayingTrackInfoArtworkCountPayload); + while(span.size >= sizeof(struct IAPArtworkCount)) { + const struct IAPArtworkCount* count = iap_span_read(&span, sizeof(*count)); + IAP_LOGF(" format=%" PRIu16 ", count=%" PRIu16, swap_16(count->format), swap_16(count->count)); + } + } break; + } + } break; + case IAPExtendedInterfaceCommandID_RetArtworkFormats: { + while(span.size >= sizeof(struct IAPArtworkFormat)) { + const struct IAPArtworkFormat* format = iap_span_read(&span, sizeof(*format)); + IAP_LOGF(" id=%" PRIu16 ", format=%" PRIu8 ", width=%" PRIu16 ", height=%" PRIu16, swap_16(format->format_id), format->pixel_format, swap_16(format->image_width), swap_16(format->image_height)); + } + } break; + case IAPExtendedInterfaceCommandID_GetTrackArtworkData: { + span_read(IAPGetTrackArtworkDataPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu8, payload->format_id); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + } break; + case IAPExtendedInterfaceCommandID_RetTrackArtworkData: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + if(index == 0) { + span_read(IAPRetTrackArtworkDataFirstPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->pixel_format)); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } else { + span_read(IAPRetTrackArtworkDataSubsequenctPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + } + } break; + case IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords: { + span_read(IAPGetNumberCategorizedDBRecordsPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + } break; + case IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords: { + span_read(IAPReturnNumberCategorizedDBRecordsPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + } break; + case IAPExtendedInterfaceCommandID_ReturnPlayStatus: { + span_read(IAPExtendedRetPlayStatusPayload); + IAP_LOGF(" state=0x%02" PRIX8, payload->state); + IAP_LOGF(" pos=%" PRIu32 "ms", swap_32(payload->track_pos_ms)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + } break; + case IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex: { + span_read(IAPReturnCurrentPlayingTrackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle: + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName: + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName: { + span_read(IAPGetIndexedPlayingTrackStringPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle: + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName: + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification: { + if(span.size == sizeof(struct IAPSetPlayStatusChangeNotification1BytePayload)) { + span_read(IAPSetPlayStatusChangeNotification1BytePayload); + IAP_LOGF(" enable=%" PRIu8, payload->enable); + } else { + span_read(IAPSetPlayStatusChangeNotification4BytesPayload); + IAP_LOGF(" mask=0x%08" PRIX32, swap_32(payload->mask)); + } + } break; + case IAPExtendedInterfaceCommandID_PlayControl: { + span_read(IAPPlayControlPayload); + IAP_LOGF(" code=0x%02" PRIX8, payload->code); + } break; + case IAPExtendedInterfaceCommandID_ReturnShuffle: { + span_read(IAPReturnShufflePayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + } break; + case IAPExtendedInterfaceCommandID_SetShuffle: { + span_read(IAPSetShufflePayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPExtendedInterfaceCommandID_ReturnRepeat: { + span_read(IAPReturnRepeatPayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + } break; + case IAPExtendedInterfaceCommandID_SetRepeat: { + span_read(IAPSetRepeatPayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPExtendedInterfaceCommandID_SetDisplayImage: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + IAP_LOGF(" index=%" PRIu16, index); + if(index == 0) { + span_read(IAPSetDisplayImageFirstPayload); + IAP_LOGF(" format=%02" PRIX8, payload->pixel_format); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } + } break; + case IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks: { + span_read(IAPRetNumPlayingTracksPayload); + IAP_LOGF(" tracks=%" PRIu32, swap_32(payload->num_playing_tracks)); + } break; + case IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack: { + span_read(IAPSetCurrentPlayingTrackPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits: { + span_read(IAPColorDisplayImageLimit); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->max_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->max_height)); + IAP_LOGF(" format=0x%02" PRIX8, payload->pixel_format); + } break; + } + break; + case IAPLingoID_DigitalAudio: + switch(command) { + case IAPDigitalAudioCommandID_AccessoryAck: { + span_read(IAPAccAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPDigitalAudioCommandID_RetAccessorySampleRateCaps: { + while(span.size >= sizeof(uint32_t)) { + uint32_t sample_rate; + iap_span_read_32(&span, &sample_rate); + IAP_LOGF(" rate=%" PRIu32, sample_rate); + } + } break; + } + break; + } +#undef span_read +} +#else +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span) { + (void)lingo; + (void)command; + (void)trans_id; + (void)span; +} +#endif diff --git a/firmware/usbstack/iap/libiap/endian.h b/firmware/usbstack/iap/libiap/endian.h new file mode 100644 index 0000000000..1a3f011ee8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/endian.h @@ -0,0 +1,28 @@ +#pragma once +#include + +__attribute__((unused)) static uint8_t swap_8(uint8_t num) { + return num; +} + +__attribute__((unused)) static uint16_t swap_16(uint16_t num) { + return (num >> 8) | (num << 8); +} + +__attribute__((unused)) static uint32_t swap_32(uint32_t num) { + return (num & 0xFF000000) >> 24 | + (num & 0x00FF0000) >> 8 | + (num & 0x0000FF00) << 8 | + (num & 0x000000FF) << 24; +} + +__attribute__((unused)) static uint64_t swap_64(uint64_t num) { + return (num & 0xFF00000000000000) >> 56 | + (num & 0x00FF000000000000) >> 40 | + (num & 0x0000FF0000000000) << 24 | + (num & 0x000000FF00000000) << 8 | + (num & 0x000000000FF00000) << 8 | + (num & 0x00000000000FF000) << 24 | + (num & 0x000000000000FF00) << 40 | + (num & 0x00000000000000FF) << 56; +} diff --git a/firmware/usbstack/iap/libiap/fid-token-values.c b/firmware/usbstack/iap/libiap/fid-token-values.c new file mode 100644 index 0000000000..eeca46533b --- /dev/null +++ b/firmware/usbstack/iap/libiap/fid-token-values.c @@ -0,0 +1,141 @@ +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "span.h" +#include "spec/iap.h" + +#define pack_accepted(Ack) \ + struct Ack* const ack = iap_span_alloc(response, sizeof(*ack)); \ + check_ret(ack != NULL, -IAPAckStatus_EOutOfResource); \ + ack->length = sizeof(struct Ack) - 1; \ + ack->type = token_header->type; \ + ack->subtype = token_header->subtype; \ + ack->status = IAPFIDTokenValuesIdentifyAckStatus_Accepted; + +int _iap_hanlde_set_fid_token_values(struct IAPSpan* request, struct IAPSpan* response) { + const struct IAPSetFIDTokenValuesPayload* request_payload = iap_span_read(request, sizeof(*request_payload)); + check_ret(request_payload != NULL, -IAPAckStatus_EBadParameter); + check_ret(iap_span_write_8(response, request_payload->num_token_values), -IAPAckStatus_EOutOfResource); + for(int i = 0; i < request_payload->num_token_values; i += 1) { + check_ret(request->size > sizeof(struct IAPFIDTokenValuesToken), -IAPAckStatus_EBadParameter); + struct IAPFIDTokenValuesToken* token_header = (void*)request->ptr; + struct IAPSpan token_span = { + request->ptr, + token_header->length + 1 /* length does not include sizeof itself */, + }; + check_ret(request->size >= token_span.size, -IAPAckStatus_EBadParameter); + iap_span_read(request, token_span.size); + + switch(token_header->type << 8 | token_header->subtype) { + case IAPFIDTokenTypes_Identify: { + /* IAPFIDTokenValuesIdentifyToken contains vla, need to parse manually */ + const struct IAPFIDTokenValuesIdentifyTokenHead* token_head = iap_span_read(&token_span, sizeof(*token_head)); + check_ret(token_head != NULL, -IAPAckStatus_EBadParameter); + for(int i = 0; i < token_head->num_lingoes; i += 1) { + uint8_t lingo_id; + check_ret(iap_span_read_8(&token_span, &lingo_id), -IAPAckStatus_EBadParameter); + } + const struct IAPFIDTokenValuesIdentifyTokenTail* token_tail = iap_span_read(&token_span, sizeof(*token_tail)); + check_ret(token_tail != NULL, -IAPAckStatus_EBadParameter); + const uint32_t opt = swap_32(token_tail->device_option); + const uint32_t id = swap_32(token_tail->device_id); + pack_accepted(IAPFIDTokenValuesIdentifyAck); + if(opt != IAPIdentifyDeviceLingoesOptions_ImmediateAuth) { + ack->status = IAPFIDTokenValuesIdentifyAckStatus_RequiredFailed; + } + (void)id; + } break; + case IAPFIDTokenTypes_AccCaps: { + const struct IAPFIDTokenValuesAccCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + const uint64_t caps = swap_64(token->caps_bits); + (void)caps; + pack_accepted(IAPFIDTokenValuesAccCapsAck); + } break; + case IAPFIDTokenTypes_AccInfo: { + const struct IAPFIDTokenValuesAccInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + switch(token->info_type) { + case IAPFIDTokenValuesAccInfoTypes_AccName: + break; + case IAPFIDTokenValuesAccInfoTypes_FirmwareVersion: + check_ret(token_span.size == 3, -IAPAckStatus_EBadParameter); + break; + case IAPFIDTokenValuesAccInfoTypes_HardwareVersion: + check_ret(token_span.size == 3, -IAPAckStatus_EBadParameter); + break; + case IAPFIDTokenValuesAccInfoTypes_Manufacture: + break; + case IAPFIDTokenValuesAccInfoTypes_ModelNumber: + break; + case IAPFIDTokenValuesAccInfoTypes_SerialNumber: + break; + case IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize: { + uint16_t val; + check_ret(iap_span_read_16(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + case IAPFIDTokenValuesAccInfoTypes_AccStatus: { + uint32_t val; + check_ret(iap_span_read_32(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + case IAPFIDTokenValuesAccInfoTypes_RFCerts: { + uint32_t val; + check_ret(iap_span_read_32(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + } + pack_accepted(IAPFIDTokenValuesAccInfoAck); + ack->info_type = token->info_type; + } break; + case IAPFIDTokenTypes_IPodPreference: { + const struct IAPFIDTokenValuesIPodPreferenceToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesIPodPreferenceAck); + ack->class_id = token->class_id; + } break; + case IAPFIDTokenTypes_EAProtocol: { + const struct IAPFIDTokenValuesEAProtocolToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesEAProtocolAck); + ack->protocol_index = token->protocol_index; + } break; + case IAPFIDTokenTypes_BundleSeedIDPref: { + const struct IAPFIDTokenValuesBundleSeedIDPrefToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesBundleSeedIDPrefAck); + } break; + case IAPFIDTokenTypes_ScreenInfo: { + const struct IAPFIDTokenValuesScreenInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesScreenInfoAck); + } break; + case IAPFIDTokenTypes_EAProtocolMetadata: { + const struct IAPFIDTokenValuesEAProtocolMetadataToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesEAProtocolMetadataAck); + } break; + case IAPFIDTokenTypes_AccDigitalAudioSampleRates: { + const struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + while(token_span.size > 0) { + uint32_t rate; + check_ret(iap_span_read_32(&token_span, &rate), -IAPAckStatus_EBadParameter); + } + pack_accepted(IAPFIDTokenValuesAccDigitalAudioSampleRatesAck); + } break; + case IAPFIDTokenTypes_AccDigitalAudioVideoDelay: { + const struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesAccDigitalAudioVideoDelayAck); + } break; + case IAPFIDTokenTypes_MicrophoneCaps: { + const struct IAPFIDTokenValuesMicrophoneCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesMicrophoneCapsAck); + } break; + default: + warn("unknown fid %04X", token_header->type << 8 | token_header->subtype); + return -IAPAckStatus_EBadParameter; + } + } + return 0; +} diff --git a/firmware/usbstack/iap/libiap/hid.c b/firmware/usbstack/iap/libiap/hid.c new file mode 100644 index 0000000000..4be2ff389c --- /dev/null +++ b/firmware/usbstack/iap/libiap/hid.c @@ -0,0 +1,181 @@ +#include + +#include "constants.h" +#include "iap.h" +#include "macros.h" +#include "platform.h" +#include "spec/hid.h" + +struct ReportSize { + uint8_t id; + uint16_t size; /* including link control byte */ +}; + +/* must match to in-driver hid report descriptor */ + +static struct ReportSize input_report_size_table_fs[] = { + {.id = 0x01, .size = 0x0C}, + {.id = 0x02, .size = 0x0E}, + {.id = 0x03, .size = 0x14}, + {.id = 0x04, .size = 0x3F}, +}; + +static struct ReportSize output_report_size_table_fs[] = { + {.id = 0x05, .size = 0x08}, + {.id = 0x06, .size = 0x0A}, + {.id = 0x07, .size = 0x0E}, + {.id = 0x08, .size = 0x14}, + {.id = 0x09, .size = 0x3F}, +}; + +static struct ReportSize input_report_size_table_hs[] = { + {.id = 0x01, .size = 0x0005}, + {.id = 0x02, .size = 0x0009}, + {.id = 0x03, .size = 0x000D}, + {.id = 0x04, .size = 0x0011}, + {.id = 0x05, .size = 0x0019}, + {.id = 0x06, .size = 0x0031}, + {.id = 0x07, .size = 0x005F}, + {.id = 0x08, .size = 0x00C1}, + {.id = 0x09, .size = 0x0101}, + {.id = 0x0A, .size = 0x0181}, + {.id = 0x0B, .size = 0x0201}, + {.id = 0x0C, .size = 0x02FF}, +}; + +static struct ReportSize output_report_size_table_hs[] = { + {.id = 0x0D, .size = 0x05}, + {.id = 0x0E, .size = 0x09}, + {.id = 0x1F, .size = 0x0D}, + {.id = 0x10, .size = 0x11}, + {.id = 0x11, .size = 0x19}, + {.id = 0x12, .size = 0x31}, + {.id = 0x13, .size = 0x5F}, + {.id = 0x14, .size = 0xC1}, + {.id = 0x15, .size = 0xFF}, +}; + +static int find_output_report_size(struct IAPContext* ctx, uint8_t id) { + const struct ReportSize* ptr = ctx->opts.usb_highspeed ? output_report_size_table_hs : output_report_size_table_fs; + const size_t len = ctx->opts.usb_highspeed ? array_size(output_report_size_table_hs) : array_size(output_report_size_table_fs); + for(size_t i = 0; i < len; i += 1) { + if(ptr[i].id == id) { + return ptr[i].size; + } + } + return -1; +} + +IAPBool iap_feed_hid_report(struct IAPContext* ctx, const uint8_t* const data, const size_t size) { + check_ret(size > sizeof(struct IAPHIDReport), iap_false); + struct IAPHIDReport* report = (struct IAPHIDReport*)data; + + int report_size; + if(ctx->opts.ignore_hid_report_id) { + report_size = size - 1; + } else { + report_size = find_output_report_size(ctx, report->report_id); + check_ret(report_size == (int)size - 1, iap_false, "%d != %d", report_size, (int)size - 1); + } + + const uint8_t payload_size = report_size - 1; + check_act(ctx->hid_recv_buf_cursor + payload_size <= HID_BUFFER_SIZE, { ctx->hid_recv_buf_cursor = 0; return iap_false; }, "hid buffer overflow"); + if(!(report->link_control & IAPHIDReportLinkControlBits_Continue)) { + /* not continue, first packet */ + if(ctx->hid_recv_buf_cursor != 0) { + warn("not continue and cursor was set"); + ctx->hid_recv_buf_cursor = 0; + } + } else { + check_ret(ctx->hid_recv_buf_cursor > 0, iap_false); + } + memcpy(ctx->hid_recv_buf + ctx->hid_recv_buf_cursor, report->data, payload_size); + ctx->hid_recv_buf_cursor += payload_size; + if(!(report->link_control & IAPHIDReportLinkControlBits_MoreToFollow)) { + /* no more to follow, last packet */ + const IAPBool ret = _iap_feed_packet(ctx, ctx->hid_recv_buf, ctx->hid_recv_buf_cursor); + ctx->hid_recv_buf_cursor = 0; + check_ret(ret, iap_false); + } + return iap_true; +} + +IAPBool iap_notify_send_complete(struct IAPContext* ctx) { + ctx->send_busy = iap_false; + check_ret(_iap_send_next_report(ctx), iap_false); + return iap_true; +} + +IAPBool _iap_send_hid_reports(struct IAPContext* ctx, size_t begin, size_t end) { + if(ctx->send_buf_sending_cursor < ctx->send_buf_sending_range_end) { + warn("another transmission in progress, aborting it"); + } + ctx->send_buf_sending_cursor = begin; + ctx->send_buf_sending_range_begin = begin; + ctx->send_buf_sending_range_end = end; + if(!ctx->send_busy) { + check_ret(_iap_send_next_report(ctx), iap_false); + } + return iap_true; +} + +static const struct ReportSize* find_optimal_report_size(struct IAPContext* ctx, size_t size) { + const struct ReportSize* ptr = ctx->opts.usb_highspeed ? input_report_size_table_hs : input_report_size_table_fs; + const size_t len = ctx->opts.usb_highspeed ? array_size(input_report_size_table_hs) : array_size(input_report_size_table_fs); + for(size_t i = 0; i < len; i += 1) { + if(ptr[i].size >= size + 1 /* link control byte*/) { + return &ptr[i]; + } + } + return &ptr[len - 1]; +} + +IAPBool _iap_send_next_report(struct IAPContext* ctx) { + if(ctx->send_buf_sending_cursor >= ctx->send_buf_sending_range_end) { + if(ctx->on_send_complete != NULL) { + IAPOnSendComplete cb = ctx->on_send_complete; + ctx->on_send_complete = NULL; + check_ret(cb(ctx), iap_false); + } else if(ctx->active_events[0].callback != NULL) { + struct IAPActiveEvent event = ctx->active_events[0]; + /* shift queue */ + size_t i = 0; + for(; i < array_size(ctx->active_events) - 1 && ctx->active_events[i + 1].callback != NULL; i += 1) { + ctx->active_events[i] = ctx->active_events[i + 1]; + } + ctx->active_events[i].callback = NULL; + /* process event */ + check_ret(event.callback(ctx, &event), iap_false); + } else if(ctx->flushing_notifications) { + check_ret(_iap_flush_notification(ctx), iap_false); + } + return iap_true; + } + + check_ret(!ctx->send_busy, iap_false); + + const size_t send_buf_left = ctx->send_buf_sending_range_end - ctx->send_buf_sending_cursor; + const struct ReportSize* const report_size = find_optimal_report_size(ctx, send_buf_left); + const size_t take_size = min((size_t)report_size->size - 1 /* link control */, send_buf_left); + + struct IAPHIDReport* const report = (struct IAPHIDReport*)ctx->hid_send_staging_buf; + + report->report_id = report_size->id; + + const IAPBool is_first = ctx->send_buf_sending_cursor == ctx->send_buf_sending_range_begin; + const IAPBool is_last = ctx->send_buf_sending_cursor + take_size == ctx->send_buf_sending_range_end; + report->link_control = + (!is_first ? IAPHIDReportLinkControlBits_Continue : 0) | + (!is_last ? IAPHIDReportLinkControlBits_MoreToFollow : 0); + + memcpy(report->data, ctx->send_buf + ctx->send_buf_sending_cursor, take_size); + memset(report->data + take_size, 0, report_size->size - 1 - take_size); /* clear rest */ + + ctx->send_buf_sending_cursor += take_size; + ctx->send_busy = iap_true; + + const size_t send_size = 1 + report_size->size; + check_ret(iap_platform_send_hid_report(ctx, report, send_size) == (int)send_size, iap_false); + + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/iap.c b/firmware/usbstack/iap/libiap/iap.c new file mode 100644 index 0000000000..b5f700b0a2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/iap.c @@ -0,0 +1,1361 @@ +#include +#include + +#include "constants.h" +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "pack-util.h" +#include "platform.h" +#include "span.h" +#include "spec/iap.h" +#include "unaligned.h" + +enum TransIDSupport { + TransIDUnknown, + TransIDSupported, + TransIDNotSupported, +}; + +static struct IAPContextButtons parse_context_button_bits(uint8_t bits[4], struct IAPContextButtons* current) { + struct IAPContextButtons new; + struct IAPContextButtons released; + +#define process(field, Mask, bits) \ + new.field = !!(bits & IAPContextButtonStatusButtons_##Mask); \ + released.field = current->field && !new.field; + + process(play_pause, PlayPause, bits[0]); + process(volume_up, VolumeUp, bits[0]); + process(volume_down, VolumeDown, bits[0]); + process(next_track, NextTrack, bits[0]); + process(prev_track, PrevTrack, bits[0]); + process(next_album, NextAlbum, bits[0]); + process(prev_album, PrevAlbum, bits[0]); + process(stop, Stop, bits[0]); + process(play, PlayResume, bits[1]); + process(pause, Pause, bits[1]); + process(mute_toggle, MuteToggle, bits[1]); + process(next_chapter, NextChapter, bits[1]); + process(prev_chapter, PrevChapter, bits[1]); + process(next_playlist, NextPlaylist, bits[1]); + process(prev_playlist, PrevPlaylist, bits[1]); + process(shuffle_advance, ShuffleSettingAdvance, bits[1]); + process(repeat_advance, RepeatSettingAdvance, bits[2]); + +#undef process + + *current = new; + return released; +} + +IAPBool iap_init_ctx(struct IAPContext* ctx, struct IAPOpts opts, void* platform) { + const uint16_t max_input_hid_desc_size = opts.usb_highspeed ? 0x02FF : 0x3F; + + memset(ctx, 0, sizeof(*ctx)); + ctx->opts = opts; + ctx->platform = platform; + + ctx->hid_recv_buf = iap_platform_malloc(ctx, HID_BUFFER_SIZE, 0); + check_ret(ctx->hid_recv_buf != NULL, iap_false); + ctx->send_buf = iap_platform_malloc(ctx, SEND_BUFFER_SIZE, 0); + check_ret(ctx->send_buf != NULL, iap_false); + ctx->handling_trans_id = -1; + ctx->trans_id_support = TransIDUnknown; + ctx->hid_send_staging_buf = iap_platform_malloc(ctx, max_input_hid_desc_size + 1 /* report id */, IAPPlatformMallocFlags_Uncached); + check_ret(ctx->hid_send_staging_buf != NULL, iap_false); + ctx->phase = IAPPhase_Connected; + return iap_true; +} + +IAPBool iap_deinit_ctx(struct IAPContext* ctx) { + if(ctx->artwork.valid) { + iap_platform_close_artwork(ctx, &ctx->artwork); + } + iap_platform_free(ctx, ctx->hid_send_staging_buf); + iap_platform_free(ctx, ctx->hid_recv_buf); + iap_platform_free(ctx, ctx->send_buf); + return iap_true; +} + +#define read_request(Type) \ + const struct Type* request = iap_span_read(request_span, sizeof(*request)); \ + check_ret(request != NULL, -IAPAckStatus_EBadParameter); + +#define alloc_response_extra(Type, extra) \ + struct Type* response = iap_span_alloc(response_span, sizeof(*response) + extra); \ + check_ret(response != NULL, -IAPAckStatus_EOutOfResource); + +#define alloc_response(Type) alloc_response_extra(Type, 0) + +static uint32_t play_stage_change_notification_set_mask_to_type_mask(uint32_t mask) { + uint32_t ret = 0; + if(mask & IAPStatusChangeNotificationBits_Basic) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackStopped | + 1 << IAPStatusChangeNotificationType_PlaybackFEWSeekStop | + 1 << IAPStatusChangeNotificationType_PlaybackREWSeekStop; + } + if(mask & IAPStatusChangeNotificationBits_Extended) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackStatusExtended; + } + if(mask & IAPStatusChangeNotificationBits_TrackIndex) { + ret |= 1 << IAPStatusChangeNotificationType_TrackIndex; + } + if(mask & IAPStatusChangeNotificationBits_TrackTimeOffsetMSec) { + ret |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec; + } + if(mask & IAPStatusChangeNotificationBits_TrackTimeOffsetSec) { + ret |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetSec; + } + if(mask & IAPStatusChangeNotificationBits_PlaybackEngineContentsChanged) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackEngineContentsChanged; + } + return ret; +} + +static IAPBool send_artwork_chunk_cb(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + if(ctx->artwork_chunk_index == 0) { + struct IAPRetTrackArtworkDataFirstPayload* payload = iap_span_alloc(&request, sizeof(*payload)); + + payload->index = swap_16(ctx->artwork_chunk_index); + payload->pixel_format = ctx->artwork.color ? IAPArtworkPixelFormats_RGB565LE : IAPArtworkPixelFormats_Mono; + payload->pixel_width = swap_16(ctx->artwork.width); + payload->pixel_height = swap_16(ctx->artwork.height); + payload->inset_top_left_x = 0; + payload->inset_top_left_y = 0; + payload->inset_bottom_right_x = payload->pixel_width; + payload->inset_bottom_right_y = payload->pixel_height; + payload->stride = swap_32(ctx->artwork.width * 2); /* TODO: support stride */ + } else { + struct IAPRetTrackArtworkDataSubsequenctPayload* payload = iap_span_alloc(&request, sizeof(*payload)); + + payload->index = swap_16(ctx->artwork_chunk_index); + } + struct IAPSpan artwork; + size_t copy_size = 0; + if(!ctx->opts.artwork_single_report || ctx->artwork_chunk_index != 0) { + check_ret(iap_platform_get_artwork_ptr(ctx, &ctx->artwork, &artwork), iap_false); + check_ret(iap_span_read(&artwork, ctx->artwork_cursor) != NULL, iap_false); /* skip already read chunk */ + copy_size = min((ctx->opts.artwork_single_report ? 48 : request.size), artwork.size); + memcpy(iap_span_alloc(&request, copy_size), iap_span_read(&artwork, copy_size), copy_size); + } + check_ret(_iap_send_packet(ctx, ctx->artwork_data_lingo, ctx->artwork_data_command, ctx->artwork_trans_id, request.ptr), iap_false); + if(artwork.size > 0) { + /* more to send, ask to call again */ + ctx->artwork_cursor += copy_size; + ctx->artwork_chunk_index += 1; + ctx->on_send_complete = send_artwork_chunk_cb; + print("track artwork left %zu bytes", artwork.size); + } else { + /* finished, free artwork */ + check_ret(iap_platform_close_artwork(ctx, &ctx->artwork), iap_false); + ctx->artwork.valid = iap_false; + print("track artwork done"); + } + return iap_true; +} + +static int32_t start_artwork_data(struct IAPContext* ctx, struct IAPSpan* request_span, IAPBool ext) { + read_request(IAPGetTrackArtworkDataPayload); + check_ret(request->format_id == 0, -IAPAckStatus_EBadParameter); + check_ret(request->offset_ms == 0, -IAPAckStatus_EBadParameter); + check_ret(!ctx->artwork.valid, -IAPAckStatus_EBadParameter); + + check_ret(iap_platform_open_artwork(ctx, swap_32(request->track_index), &ctx->artwork), -IAPAckStatus_EBadParameter); + ctx->artwork.valid = iap_true; + ctx->artwork_cursor = 0; + ctx->artwork_chunk_index = 0; + ctx->artwork_trans_id = ctx->handling_trans_id; + if(ext) { + ctx->artwork_data_lingo = IAPLingoID_ExtendedInterface; + ctx->artwork_data_command = IAPExtendedInterfaceCommandID_RetTrackArtworkData; + } else { + ctx->artwork_data_lingo = IAPLingoID_DisplayRemote; + ctx->artwork_data_command = IAPDisplayRemoteCommandID_RetTrackArtworkData; + } + check_ret(send_artwork_chunk_cb(ctx), -IAPAckStatus_ECommandFailed); + return 0; +} + +static int32_t ipod_ack(uint16_t command, enum IAPAckStatus status, struct IAPSpan* response_span, uint16_t ret) { + alloc_response(IAPIPodAckPayload); + response->status = status; + response->id = command; + return ret; +} + +static int32_t ipod_ack_ext(uint16_t command, enum IAPAckStatus status, struct IAPSpan* response_span) { + alloc_response(IAPExtendedIPodAckPayload); + response->id = swap_16(command); + response->status = status; + return IAPExtendedInterfaceCommandID_IPodAck; +} + +static int32_t handle_command(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_RequestExtendedInterfaceMode: { + alloc_response(IAPReturnExtendedInterfaceModePayload); + response->is_ext_mode = 1; + return IAPGeneralCommandID_ReturnExtendedInterfaceMode; + } break; + case IAPGeneralCommandID_RequestIPodSoftwareVersion: { + alloc_response(IAPReturnIPodSoftwareVersionPayload); + response->major = 18; + response->minor = 7; + response->revision = 2; + return IAPGeneralCommandID_ReturnIPodSoftwareVersion; + } break; + case IAPGeneralCommandID_RequestIPodSerialNum: { + check_ret(iap_platform_get_ipod_serial_num(ctx, response_span), -IAPAckStatus_ECommandFailed); + return IAPGeneralCommandID_ReturnIPodSerialNum; + } break; + case IAPGeneralCommandID_RequestIPodModelNum: { + static const char* model_num = "MTAY2J/A"; + check_ret(iap_span_append(response_span, model_num, strlen(model_num) + 1), -IAPAckStatus_EOutOfResource); + return IAPGeneralCommandID_ReturnIPodModelNum; + } break; + case IAPGeneralCommandID_RequestTransportMaxPayloadSize: { + alloc_response(IAPReturnTransportMaxPayloadSizePayload); + response->max_payload_size = swap_16(HID_BUFFER_SIZE - 1 /*sync*/ - 1 /*sof*/ - 3 /*length*/ - 1 /*checksum*/); + return IAPGeneralCommandID_ReturnTransportMaxPayloadSize; + } break; + case IAPGeneralCommandID_RequestLingoProtocolVersion: { + static const struct { + uint8_t major; + uint8_t minor; + } table[] = { + [IAPLingoID_General] = {1, 9}, + [IAPLingoID_Microphone] = {1, 1}, + [IAPLingoID_SimpleRemote] = {1, 4}, + [IAPLingoID_DisplayRemote] = {1, 5}, + [IAPLingoID_ExtendedInterface] = {1, 14}, + [IAPLingoID_AccessoryPower] = {1, 1}, + [IAPLingoID_USBHostMode] = {1, 0}, + [IAPLingoID_RFTuner] = {1, 1}, + [IAPLingoID_AccessoryEqualizer] = {1, 0}, + [IAPLingoID_Sports] = {1, 1}, + [IAPLingoID_DigitalAudio] = {1, 3}, + [IAPLingoID_Storage] = {1, 2}, + [IAPLingoID_IPodOut] = {1, 0}, + [IAPLingoID_Location] = {1, 0}, + }; + + read_request(IAPRequestLingoProtocolVersionPayload); + check_ret(request->lingo < array_size(table), -IAPAckStatus_EBadParameter); + + alloc_response(IAPReturnLingoProtocolVersionPayload); + response->lingo = request->lingo; + response->major = table[request->lingo].major; + response->minor = table[request->lingo].minor; + return IAPGeneralCommandID_ReturnLingoProtocolVersion; + } break; + case IAPGeneralCommandID_GetIPodOptions: { + alloc_response(IAPRetIPodOptionsPayload); + response->state = 0; + return IAPGeneralCommandID_RetIPodOptions; + } break; + case IAPGeneralCommandID_GetIPodPreferences: { + read_request(IAPGetIPodPreferencesPayload); + alloc_response(IAPRetIPodPreferencesPayload); + response->class_id = request->class_id; + response->setting_id = 0; /* TODO: return actual value */ + return IAPGeneralCommandID_RetIPodPreferences; + } break; + case IAPGeneralCommandID_SetIPodPreferences: { + read_request(IAPSetIPodPreferencesPayload); + /* TODO: handle preferences */ + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_SetUIMode: { + read_request(IAPSetUIModePayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_GetIPodOptionsForLingo: { + read_request(IAPGetIPodOptionsForLingoPayload); + alloc_response(IAPRetIPodOptionsForLingoPayload); + response->lingo_id = request->lingo_id; + switch(request->lingo_id) { + case IAPLingoID_SimpleRemote: + response->bits = swap_64(IAPRetIPodOptionsForLingoSimpleRemoteBits_ContextSpecificControls); + break; + case IAPLingoID_General: + case IAPLingoID_DisplayRemote: + case IAPLingoID_ExtendedInterface: + case IAPLingoID_DigitalAudio: + case IAPLingoID_Storage: /* TODO: this is not supported */ + response->bits = 0; + break; + case IAPLingoID_USBHostMode: + case IAPLingoID_RFTuner: + case IAPLingoID_Sports: + case IAPLingoID_IPodOut: + case IAPLingoID_Location: { /* not supported */ + return -IAPAckStatus_EBadParameter; + } + } + return IAPGeneralCommandID_RetIPodOptionsForLingo; + } break; + case IAPGeneralCommandID_GetSupportedEventNotification: { + alloc_response(IAPRetSupportedEventNotificationPayload); + response->mask = swap_64(IAPSetEventNotificationEvents_FlowControl); + return IAPGeneralCommandID_RetSupportedEventNotification; + } break; + case IAPGeneralCommandID_SetAvailableCurrent: { + read_request(IAPSetAvailableCurrentPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_SetEventNotification: { + read_request(IAPSetEventNotificationPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + } + break; + case IAPLingoID_SimpleRemote: + switch(command) { + case IAPSimpleRemoteCommandID_ContextButtonStatus: { + response_span->ptr = NULL; + + uint8_t bits[4] = {0}; + for(int i = 0; i < 4 && request_span->size > 0; i += 1) { + iap_span_read_8(request_span, &bits[i]); + } + const struct IAPContextButtons released = parse_context_button_bits(bits, &ctx->context_button_state); + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = -1, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + const struct { + IAPBool released; + enum IAPPlatformControl control; + } table[] = { + {released.play_pause, IAPPlatformControl_TogglePlayPause}, + {released.volume_up, IAPPlatformControl_VolumeUp}, + {released.volume_down, IAPPlatformControl_VolumeDown}, + {released.next_track, IAPPlatformControl_Next}, + {released.prev_track, IAPPlatformControl_Prev}, + {released.next_album, IAPPlatformControl_Next}, + {released.prev_album, IAPPlatformControl_Prev}, + {released.stop, IAPPlatformControl_Stop}, + {released.play, IAPPlatformControl_Play}, + {released.pause, IAPPlatformControl_Pause}, + {released.mute_toggle, IAPPlatformControl_ToggleMute}, + {released.next_chapter, IAPPlatformControl_Next}, + {released.prev_chapter, IAPPlatformControl_Prev}, + {released.next_playlist, IAPPlatformControl_Next}, + {released.prev_playlist, IAPPlatformControl_Prev}, + }; + for(size_t i = 0; i < array_size(table); i += 1) { + if(table[i].released) { + iap_platform_control(ctx, table[i].control, pending); + } + } + if(released.shuffle_advance) { + uint8_t current; + check_act(iap_platform_get_shuffle_setting(ctx, ¤t), return 0); + current = (current + 1) % IAPIPodStateShuffleSettingState_Albums; + check_act(iap_platform_set_shuffle_setting(ctx, current), return 0); + } + if(released.repeat_advance) { + uint8_t current; + check_act(iap_platform_get_repeat_setting(ctx, ¤t), return 0); + current = (current + 1) % IAPIPodStateRepeatSettingState_All; + check_act(iap_platform_set_repeat_setting(ctx, current), return 0); + } + return 0; + } break; + } + break; + case IAPLingoID_DisplayRemote: + switch(command) { + case IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex: { + read_request(IAPSetCurrentEQProfileIndexPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPDisplayRemoteCommandID_IPodAck); + } break; + case IAPDisplayRemoteCommandID_SetRemoteEventNotification: { + read_request(IAPSetRemoteEventNotificationPayload); + ctx->notifications_3 = 0; + ctx->enabled_notifications_3 = swap_32(request->mask); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPDisplayRemoteCommandID_IPodAck); + } break; + case IAPDisplayRemoteCommandID_GetIPodStateInfo: { + read_request(IAPGetIPodStateInfoPayload); + check_ret(response_span->size >= sizeof(struct IAPIPodStatePayload), -IAPAckStatus_EOutOfResource); + ((struct IAPIPodStatePayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPIPodStateType_TrackTimePositionMSec: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackTimePositionMSecPayload); + response->position_ms = swap_32(status.track_pos_ms); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackPlaybackIndexPayload); + response->index = swap_32(status.track_index); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_ChapterIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateChapterIndexPayload); + response->index = swap_32(status.track_index); + /* no chapters */ + response->chapter_count = 0; + response->chapter_index = -1; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_PlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStatePlayStatusPayload); + response->status = status.state; /* TODO: convert enum */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_Volume: { + struct IAPPlatformVolumeStatus status; + check_ret(iap_platform_get_volume(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateVolumePayload); + response->mute_state = status.muted; + response->ui_volume = status.volume; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_Power: { + struct IAPPlatformPowerStatus status; + check_ret(iap_platform_get_power_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStatePowerPayload); + response->power_state = status.state; /* TODO: convert enum */ + response->battery_level = status.battery_level; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_EQSetting: { + alloc_response(IAPIPodStateEQSettingPayload); + /* no eq setting support yet */ + response->eq_index = 0; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_ShuffleSetting: { + alloc_response(IAPIPodStateShuffleSettingPayload); + check_ret(iap_platform_get_shuffle_setting(ctx, &response->shuffle_state), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_RepeatSetting: { + alloc_response(IAPIPodStateRepeatSettingPayload); + check_ret(iap_platform_get_repeat_setting(ctx, &response->repeat_state), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_DateTimeSetting: { + struct IAPDateTime time; + check_ret(iap_platform_get_date_time(ctx, &time), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateDateTimeSettingPayload); + response->year = swap_16(time.year); + response->month = time.month; + response->day = time.day; + response->hour = time.hour; + response->minute = time.minute; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AlarmSetting: { + alloc_response(IAPIPodStateAlarmSettingPayload); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_BacklightLevel: { + alloc_response(IAPIPodStateBacklightLevelPayload); + check_ret(iap_platform_get_backlight_level(ctx, &response->level), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_HoldSwitchState: { + IAPBool state; + check_ret(iap_platform_get_hold_switch_state(ctx, &state), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateHoldSwitchStatePayload); + response->state = state; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_SoundCheckState: { + alloc_response(IAPIPodStateSoundCheckStatePayload); + response->state = 0; /* no sound check */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AudiobookSpeeed: { + alloc_response(IAPIPodStateAudiobookSpeeedPayload); + response->speed = IAPIPodStateAudiobookSpeeed_Normal; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackTimePositionSec: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackTimePositionSecPayload); + response->position_s = swap_16(status.track_pos_ms / 1000); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AbsoluteVolume: { + struct IAPPlatformVolumeStatus status; + check_ret(iap_platform_get_volume(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateAbsoluteVolumePayload); + response->mute_state = status.muted; + response->ui_volume = status.volume; + response->absolute_volume = status.volume; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackCaps: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackCapsPayload); + response->caps = swap_32(status.track_caps); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_PlaybackEngineContents: { + alloc_response(IAPIPodStatePlaybackEngineContentsPayload); + response->count = 0; /* TODO: shoud be supported? */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + default: + warn("invalid request type 0x%02" PRIX8, request->type); + return -IAPAckStatus_EBadParameter; + } + } break; + case IAPDisplayRemoteCommandID_GetPlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetPlayStatusPayload); + response->state = status.state; /* TODO: convert enum */ + response->track_index = swap_32(status.track_index); + response->track_pos_ms = swap_32(status.track_pos_ms); + response->track_total_ms = swap_32(status.track_total_ms); + return IAPDisplayRemoteCommandID_RetPlayStatus; + } break; + case IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo: { + read_request(IAPGetIndexedPlayingTrackInfoPayload); + check_ret(response_span->size > sizeof(struct IAPRetIndexedPlayingTrackInfoPayload), -IAPAckStatus_EBadParameter); + ((struct IAPRetIndexedPlayingTrackInfoPayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPIndexedPlayingTrackInfoType_TrackCapsInfo: { + uint32_t length; + uint32_t caps; + struct IAPPlatformTrackInfo info = {.total_ms = &length, .caps = &caps}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + response->track_caps = swap_32(caps); + response->track_total_ms = swap_32(length); + response->chapter_count = 0; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ChapterTimeName: { + return -IAPAckStatus_EBadParameter; + } break; + case IAPIndexedPlayingTrackInfoType_ArtistName: { + alloc_response(IAPRetIndexedPlayingTrackInfoArtistNamePayload); + struct IAPPlatformTrackInfo info = {.artist = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_AlbumName: { + alloc_response(IAPRetIndexedPlayingTrackInfoAlbumNamePayload); + struct IAPPlatformTrackInfo info = {.album = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_GenreName: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoGenreNamePayload, 1); + response->name[0] = '\0'; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_TrackTitle: { + alloc_response(IAPRetIndexedPlayingTrackInfoTrackTitlePayload); + struct IAPPlatformTrackInfo info = {.title = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ComposerName: { + alloc_response(IAPRetIndexedPlayingTrackInfoComposerNamePayload); + struct IAPPlatformTrackInfo info = {.composer = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_Lyrics: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoLyricsPayload, 1); + response->info_bits = 0; + response->index = 0; + response->lyrics[0] = '\0'; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ArtworkCount: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoArtworkCountPayload, sizeof(struct IAPArtworkCount)); + response->data[0].format = 0; + response->data[0].count = swap_16(1); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + } + } break; + case IAPDisplayRemoteCommandID_GetArtworkFormats: { + alloc_response_extra(IAPArtworkFormat, sizeof(struct IAPArtworkFormat)); + response->format_id = 0; + response->pixel_format = IAP_COLOR_ARTWORK ? IAPArtworkPixelFormats_RGB565LE : IAPArtworkPixelFormats_Mono; + response->image_width = swap_16(IAP_ARTWORK_WIDTH); + response->image_height = swap_16(IAP_ARTWORK_HEIGHT); + return IAPDisplayRemoteCommandID_RetArtworkFormats; + } break; + case IAPDisplayRemoteCommandID_GetNumPlayingTracks: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetNumPlayingTracksPayload); + response->num_playing_tracks = swap_32(status.track_count); + return IAPDisplayRemoteCommandID_RetNumPlayingTracks; + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkData: { + const int32_t ret = start_artwork_data(ctx, request_span, iap_false); + check_ret(ret == 0, ret); + /* responded in send_artwork_chunk_cb, no need to do it here */ + response_span->ptr = NULL; + return 0; + } break; + case IAPDisplayRemoteCommandID_GetPowerBatteryState: { + struct IAPPlatformPowerStatus status; + check_ret(iap_platform_get_power_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetPowerBatteryStatePayload); + response->power_state = status.state; /* TODO: convert enum */ + response->battery_level = status.battery_level; + return IAPDisplayRemoteCommandID_RetPowerBatteryState; + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkTimes: { + read_request(IAPGetTrackArtworkTimesPayload); + const uint16_t count = swap_16(request->artwork_count); + check_ret(count == 0 || count == 1, -IAPAckStatus_ECommandFailed, "not implemented"); + + void* payload = iap_span_alloc(response_span, sizeof(uint32_t) * count); + check_ret(payload != NULL, iap_false); + memset(payload, 0, sizeof(uint32_t) * count); + return IAPDisplayRemoteCommandID_RetTrackArtworkTimes; + } break; + } + break; + case IAPLingoID_ExtendedInterface: + switch(command) { + case IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterInfo: { + alloc_response(IAPReturnCurrentPlayingTrackChapterInfoPayload); + /* no chapters */ + response->count = 0; + response->index = -1; + return IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo; + } break; + case IAPExtendedInterfaceCommandID_GetAudiobookSpeed: { + alloc_response(IAPRetAudiobookSpeedPayload); + response->speed = IAPIPodStateAudiobookSpeeed_Normal; + return IAPExtendedInterfaceCommandID_RetAudiobookSpeed; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo: { + read_request(IAPExtendedGetIndexedPlayingTrackInfoPayload); + check_ret(response_span->size > sizeof(struct IAPExtendedRetIndexedPlayingTrackInfoPayload), -IAPAckStatus_EBadParameter); + ((struct IAPExtendedRetIndexedPlayingTrackInfoPayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo: { + uint32_t length; + uint32_t caps; + struct IAPPlatformTrackInfo info = {.total_ms = &length, .caps = &caps}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + response->track_caps = swap_32(caps); + response->track_total_ms = swap_32(length); + response->chapter_count = 0; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_PodcastName: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoPodcastNamePayload, 1); + response->name[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate: { + struct IAPDateTime time; + struct IAPPlatformTrackInfo info = {.release_date = &time}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload); + response->seconds = time.seconds; + response->minutes = time.minute; + response->hours = time.hour; + response->day = time.day; + response->month = time.month; + response->year = swap_16(time.year); + response->weekday = 0; /* TODO: set weekday? */ + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackDescription: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload, 1); + response->info_bits = 0; + response->index = 0; + response->description[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload, 1); + response->info_bits = 0; + response->index = 0; + response->lyrics[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackGenre: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackGenrePayload, 1); + response->genre[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackComposer: { + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackComposerPayload); + struct IAPPlatformTrackInfo info = {.composer = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount: { + // IAPArtworkPixelFormats_RGB565LE; + // struct IAPExtendedRetIndexedPlayingTrackInfoTrackArtworkCountPayload payload = { + // .}; + warn("artwork not implemented"); + return -IAPAckStatus_ECommandFailed; + } break; + default: + warn("invalid request type 0x%02" PRIX8, request->type); + return -IAPAckStatus_EBadParameter; + } + } break; + case IAPExtendedInterfaceCommandID_GetArtworkFormats: { + /* same as DisplayRemote::GetArtworkFormats */ + const int32_t ret = handle_command(ctx, IAPLingoID_DisplayRemote, IAPDisplayRemoteCommandID_GetArtworkFormats, request_span, response_span); + check_ret(ret == IAPDisplayRemoteCommandID_RetArtworkFormats, ret); + return IAPExtendedInterfaceCommandID_RetArtworkFormats; + } break; + case IAPExtendedInterfaceCommandID_GetTrackArtworkData: { + const int32_t ret = start_artwork_data(ctx, request_span, iap_true); + check_ret(ret == 0, ret); + /* responded in send_artwork_chunk_cb, no need to do it here */ + response_span->ptr = NULL; + return 0; + } break; + case IAPExtendedInterfaceCommandID_ResetDBSelection: { + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords: { + read_request(IAPGetNumberCategorizedDBRecordsPayload); + uint32_t count; + switch(request->type) { + case IAPDatabaseType_Playlist: { + /* TODO: implement platform callback */ + count = 99; + } break; + case IAPDatabaseType_Track: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + /* track_count is invalid while stopped. + * return non-zero dummy value in this case, because reporting zero tracks + * may cause empty library error. */ + /* TODO: maybe add dedicated platform callback? */ + count = status.state == IAPIPodStatePlayStatus_PlaybackStopped ? 99 : status.track_count; + } break; + default: { + warn("unsupported type 0x%02" PRIX8, request->type); + count = 0; + } break; + } + + alloc_response(IAPReturnNumberCategorizedDBRecordsPayload); + response->count = swap_32(count); + return IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords; + } break; + case IAPExtendedInterfaceCommandID_GetPlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPExtendedRetPlayStatusPayload); + response->state = status.state; /* TODO: convert enum */ + response->track_pos_ms = swap_32(status.track_pos_ms); + response->track_total_ms = swap_32(status.track_total_ms); + return IAPExtendedInterfaceCommandID_ReturnPlayStatus; + } break; + case IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPReturnCurrentPlayingTrackIndexPayload); + response->index = swap_32(status.state == IAPIPodStatePlayStatus_PlaybackStopped ? (uint32_t)-1 : status.track_index); + return IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.title = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.artist = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.album = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName; + } break; + case IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification: { + if(request_span->size == sizeof(struct IAPSetPlayStatusChangeNotification1BytePayload)) { + read_request(IAPSetPlayStatusChangeNotification1BytePayload); + ctx->notifications_4 = 0; + if(request->enable) { + ctx->enabled_notifications_4 = 1 << IAPStatusChangeNotificationType_PlaybackStopped | + 1 << IAPStatusChangeNotificationType_TrackIndex | + 1 << IAPStatusChangeNotificationType_PlaybackFEWSeekStop | + 1 << IAPStatusChangeNotificationType_PlaybackREWSeekStop | + 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec | + 1 << IAPStatusChangeNotificationType_ChapterIndex; + } else { + ctx->enabled_notifications_4 = 0; + } + } else if(request_span->size == sizeof(struct IAPSetPlayStatusChangeNotification4BytesPayload)) { + read_request(IAPSetPlayStatusChangeNotification4BytesPayload); + ctx->enabled_notifications_4 = play_stage_change_notification_set_mask_to_type_mask(swap_32(request->mask)); + } + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_PlayCurrentSelection: { + read_request(IAPPlayCurrentSelectionPayload); + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = IAPExtendedInterfaceCommandID_IPodAck, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + iap_platform_control(ctx, IAPPlatformControl_Play, pending); + response_span->ptr = NULL; + return 0; + } break; + case IAPExtendedInterfaceCommandID_PlayControl: { + read_request(IAPPlayControlPayload); + static const int enum_table[][2] = { + {IAPPlayControlCode_TogglePlayPause, IAPPlatformControl_TogglePlayPause}, + {IAPPlayControlCode_Stop, IAPPlatformControl_Stop}, + {IAPPlayControlCode_NextTrack, IAPPlatformControl_Next}, + {IAPPlayControlCode_PrevTrack, IAPPlatformControl_Prev}, + {IAPPlayControlCode_StartFF, -1}, + {IAPPlayControlCode_StartRew, -1}, + {IAPPlayControlCode_EndFFRew, -1}, + {IAPPlayControlCode_Next, IAPPlatformControl_Next}, + {IAPPlayControlCode_Prev, IAPPlatformControl_Prev}, + {IAPPlayControlCode_Play, IAPPlatformControl_Play}, + {IAPPlayControlCode_Pause, IAPPlatformControl_Pause}, + {IAPPlayControlCode_NextChapter, IAPPlatformControl_Next}, + {IAPPlayControlCode_PrevChapter, IAPPlatformControl_Prev}, + {IAPPlayControlCode_ResumeIPod, -1}, + }; + int control = -1; + for(size_t i = 0; i < array_size(enum_table); i += 1) { + if(enum_table[i][0] == request->code) { + control = enum_table[i][1]; + break; + } + } + if(control >= 0) { + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = IAPExtendedInterfaceCommandID_IPodAck, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + iap_platform_control(ctx, control, pending); + response_span->ptr = NULL; + return 0; + } + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetShuffle: { + alloc_response(IAPReturnShufflePayload); + check_ret(iap_platform_get_shuffle_setting(ctx, &response->mode), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnShuffle; + } break; + case IAPExtendedInterfaceCommandID_SetShuffle: { + read_request(IAPSetShufflePayload); + check_ret(iap_platform_set_shuffle_setting(ctx, request->mode), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetRepeat: { + alloc_response(IAPReturnRepeatPayload); + check_ret(iap_platform_get_repeat_setting(ctx, &response->mode), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnRepeat; + } break; + case IAPExtendedInterfaceCommandID_SetRepeat: { + read_request(IAPSetRepeatPayload); + check_ret(iap_platform_set_repeat_setting(ctx, request->mode), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_SetDisplayImage: { + /* TODO: pass downloaded image to user */ + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetNumPlayingTracks: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetNumPlayingTracksPayload); + response->num_playing_tracks = swap_32(status.track_count); + return IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks; + } break; + case IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack: { + read_request(IAPSetCurrentPlayingTrackPayload); + check_ret(iap_platform_set_playing_track(ctx, swap_32(request->index)), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_SelectSortDBRecord: { + /* ignored */ + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetColorDisplayImageLimits: { + alloc_response(IAPColorDisplayImageLimit); + response->max_width = 0; + response->max_height = 0; + response->pixel_format = IAPArtworkPixelFormats_RGB565LE; + return IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits; + } break; + } + break; + case IAPLingoID_DigitalAudio: + switch(command) { + case IAPDigitalAudioCommandID_AccessoryAck: { + read_request(IAPAccAckPayload); + response_span->ptr = NULL; + if(request->id == IAPDigitalAudioCommandID_TrackNewAudioAttributes) { + check_ret(ctx->waiting_for_audio_attrs_ack, 0, "unexpected ack"); + ctx->waiting_for_audio_attrs_ack = iap_false; + } + check_ret(request->status == IAPAckStatus_Success, 0); + return 0; + } break; + case IAPDigitalAudioCommandID_RetAccessorySampleRateCaps: { + check_ret(iap_platform_on_acc_samprs_received(ctx, request_span), -IAPAckStatus_ECommandFailed); + response_span->ptr = NULL; /* no response */ + return 0; + } break; + } + break; + } + + return -IAPAckStatus_EUnknownID; +} + +static IAPBool transition_idps_to_auth_cb(struct IAPContext* ctx) { + print("starting accessory authentication"); + ctx->phase = IAPPhase_Auth; + check_ret(_iap_send_packet(ctx, IAPLingoID_General, IAPGeneralCommandID_GetAccessoryAuthenticationInfo, _iap_next_trans_id(ctx), _iap_get_buffer_for_send_payload(ctx).ptr), iap_false); + return iap_true; +} + +static int32_t handle_in_connected(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_IdentifyDeviceLingoes: { + read_request(IAPIdentifyDeviceLingoesPayload); + switch(swap_32(request->options)) { + case IAPIdentifyDeviceLingoesOptions_NoAuth: + break; + case IAPIdentifyDeviceLingoesOptions_DeferAuth: + warn("unsupported option 0x%04" PRIX32, swap_32(request->options)); + return -IAPAckStatus_EBadParameter; + case IAPIdentifyDeviceLingoesOptions_ImmediateAuth: + ctx->on_send_complete = transition_idps_to_auth_cb; + break; + } + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_StartIDPS: { + ctx->phase = IAPPhase_IDPS; + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + } + break; + } + return -IAPAckStatus_EUnknownID; +} + +static int32_t handle_in_idps(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_SetFIDTokenValues: { + const int ret = _iap_hanlde_set_fid_token_values(request_span, response_span); + check_ret(ret == 0, ret); + return IAPGeneralCommandID_AckFIDTokenValues; + } break; + case IAPGeneralCommandID_EndIDPS: { + read_request(IAPEndIDPSPayload); + check_ret(request->status == IAPEndIDPSStatus_Success, -IAPAckStatus_ECommandFailed); + ctx->on_send_complete = transition_idps_to_auth_cb; + alloc_response(IAPIDPSStatusPayload); + response->status = IAPIDPSStatus_Success; + return IAPGeneralCommandID_IDPSStatus; + } break; + } + break; + } + return iap_false; +} + +static IAPBool send_auth_challenge_sig_cb(struct IAPContext* ctx) { + check_ret(ctx->phase == IAPPhase_Auth, iap_false); + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + struct IAPGetAccAuthSigPayload2p0* request = iap_span_alloc(&request_span, sizeof(*request)); + check_ret(request != NULL, iap_false); + request->retry = 1; + check_ret(_iap_send_packet(ctx, IAPLingoID_General, IAPGeneralCommandID_GetAccessoryAuthenticationSignature, _iap_next_trans_id(ctx), request_span.ptr), iap_false); + return iap_true; +} + +static IAPBool send_sample_rate_caps_cb(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + check_ret(_iap_send_packet(ctx, IAPLingoID_DigitalAudio, IAPDigitalAudioCommandID_GetAccessorySampleRateCaps, _iap_next_trans_id(ctx), request.ptr), iap_false); + return iap_true; +} + +static int32_t handle_in_auth(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_RetAccessoryAuthenticationInfo: { + read_request(IAPRetAccAuthInfoPayload2p0); + print("accessory cert %" PRIu8 "/%" PRIu8, request->cert_current_section_index, request->cert_max_section_index); + /* iap_platform_dump_hex(request->ptr, request->size); */ + if(request->cert_current_section_index < request->cert_max_section_index) { + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } else { + ctx->on_send_complete = send_auth_challenge_sig_cb; + alloc_response(IAPAckAccAuthInfoPayload); + response->status = IAPAckAccAuthInfoStatus_Supported; + return IAPGeneralCommandID_AckAccessoryAuthenticationInfo; + } + } break; + case IAPGeneralCommandID_RetAccessoryAuthenticationSignature: { + print("accessory signature"); + /* iap_platform_dump_hex(request->ptr, request->size); */ + + alloc_response(IAPAckAccAuthSigPayload); + response->status = IAPAckStatus_Success; + + ctx->phase = IAPPhase_Authed; + ctx->on_send_complete = send_sample_rate_caps_cb; + + return IAPGeneralCommandID_AckAccessoryAuthenticationStatus; + } break; + } + break; + } + return -IAPAckStatus_EUnknownID; +} + +static int32_t build_ipod_ack_response(uint8_t lingo, uint16_t command, uint8_t status, struct IAPSpan* response_span) { + static int32_t ack_commmand_ids[] = { + IAPGeneralCommandID_IPodAck, + -1, + IAPSimpleRemoteCommandID_IPodAck, + IAPDisplayRemoteCommandID_IPodAck, + IAPExtendedInterfaceCommandID_IPodAck, + -1, + IAPUSBHostModeCommandID_IPodAck, + -1, + -1, + IAPSportsCommandID_IPodAck, + IAPDigitalAudioCommandID_IPodAck, + -1, + IAPStorageCommandID_IPodAck, + IAPIPodOutCommandID_IPodAck, + IAPLocationCommandID_IPodAck, + }; + check_ret(lingo < array_size(ack_commmand_ids) && ack_commmand_ids[lingo] >= 0, -1); + switch(lingo) { + case IAPLingoID_General: + case IAPLingoID_SimpleRemote: + case IAPLingoID_DisplayRemote: + case IAPLingoID_USBHostMode: + case IAPLingoID_Sports: + case IAPLingoID_DigitalAudio: + case IAPLingoID_IPodOut: + case IAPLingoID_Location: { + alloc_response(IAPIPodAckPayload); + response->id = command; + response->status = status; + } break; + case IAPLingoID_ExtendedInterface: { + alloc_response(IAPExtendedIPodAckPayload); + response->id = swap_16(command); + response->status = status; + } break; + case IAPLingoID_Storage: { + alloc_response(IAPStorageIPodAckPayload); + response->id = command; + response->status = status; + response->handle = -1; /* TODO: set proper handle */ + } break; + } + return ack_commmand_ids[lingo]; +} + +IAPBool _iap_feed_packet(struct IAPContext* ctx, const uint8_t* const data, const size_t size) { + union { + uint8_t u8; + uint16_t u16; + } buf; + struct IAPSpan span = {(uint8_t*)data, size}; + + /* read sof byte */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + if(buf.u8 == IAP_SYNC_BYTE) { + /* skip sync byte */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + } + check_ret(buf.u8 == IAP_SOF_BYTE, iap_false, "%x != %x", buf.u8, IAP_SOF_BYTE); + const uint8_t* const checksum_range_begin = span.ptr; + /* read size */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + size_t length; + if(buf.u8 == 0) { + /* long packet */ + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + length = buf.u16; + } else { + length = buf.u8; + } + /* we have length, strip span so that it contains lingo,command,payload */ + check_ret(span.size >= length + 1 /* checksum */, iap_false); + span.size = length; + /* verify checksum */ + const uint8_t* const checksum_range_end = span.ptr + span.size + 1 /* checksum */; + uint8_t checksum = 0; + for(const uint8_t* ptr = checksum_range_begin; ptr < checksum_range_end; ptr += 1) { + checksum += *ptr; + } + check_ret(checksum == 0, iap_false); + /* read lingo id */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + uint8_t lingo = buf.u8; + /* read command id */ + uint16_t command; + if(lingo == IAPLingoID_ExtendedInterface) { + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + command = buf.u16; + } else { + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + command = buf.u8; + } + + /* now span contains only payload */ + + /* request handling */ + if(ctx->trans_id_support == TransIDUnknown) { + check_ret(lingo == IAPLingoID_General, iap_false); + if(command == IAPGeneralCommandID_StartIDPS) { + ctx->trans_id_support = TransIDSupported; + } else if(command == IAPGeneralCommandID_IdentifyDeviceLingoes) { + ctx->trans_id_support = TransIDNotSupported; + ctx->handling_trans_id = -1; + } else { + warn("the first command(%02X:%04X) must be StartIDPS or IdentifyDeviceLingoes", lingo, command); + return iap_false; + } + } + if(ctx->trans_id_support == TransIDSupported) { + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + ctx->handling_trans_id = buf.u16; + } + + if(ctx->opts.enable_packet_dump) { + IAP_LOGF("==== acc ===="); + _iap_dump_packet(lingo, command, ctx->handling_trans_id, span); + } + + struct IAPSpan response = _iap_get_buffer_for_send_payload(ctx); + int32_t ret = handle_command(ctx, lingo, command, &span, &response); + if(response.ptr == NULL) { + /* handler disabled response */ + return iap_true; + } + if(ret >= 0) { + /* handled successfully */ + goto respond; + } + if(ret != -IAPAckStatus_EUnknownID) { + /* handled, but error */ + goto error_ack; + } + + /* not a standard request, try authentication handlers */ + ret = -IAPAckStatus_EBadParameter; + response = _iap_get_buffer_for_send_payload(ctx); + switch(ctx->phase) { + case IAPPhase_Connected: + ret = handle_in_connected(ctx, lingo, command, &span, &response); + break; + case IAPPhase_IDPS: + ret = handle_in_idps(ctx, lingo, command, &span, &response); + break; + case IAPPhase_Auth: + ret = handle_in_auth(ctx, lingo, command, &span, &response); + break; + } + if(response.ptr == NULL) { + /* handler disabled response */ + return iap_true; + } + if(ret >= 0) { + /* handled successfully */ + goto respond; + } +error_ack: + /* handling failed, replace response with ipod ack */ + warn("command handling failed 0x%02X(%s):0x%04X", lingo, _iap_lingo_str(lingo), command); + response = _iap_get_buffer_for_send_payload(ctx); + ret = build_ipod_ack_response(lingo, command, -ret, &response); + check_ret(ret >= 0, iap_false); +respond: + check_ret(_iap_send_packet(ctx, lingo, ret, ctx->handling_trans_id, response.ptr), iap_false); + return iap_true; +} + +struct IAPSpan _iap_get_buffer_for_send_payload(struct IAPContext* ctx) { + const size_t header_size = 1 /* sof */ + + 3 /* long format length */ + + 1 /* lingo */ + + 2 /* largest command id */ + + 2 /* trans id */; + const size_t footer_size = 1 /* checksum */; + + struct IAPSpan buf = {ctx->send_buf + header_size, SEND_BUFFER_SIZE - header_size - footer_size}; + return buf; +} + +int32_t _iap_next_trans_id(struct IAPContext* ctx) { + if(ctx->trans_id_support == TransIDSupported) { + return ctx->trans_id += 1; + } else { + return -1; + } +} + +IAPBool _iap_send_packet(struct IAPContext* ctx, uint8_t lingo, uint16_t command, int32_t trans_id, uint8_t* final_ptr) { + uint8_t* ptr = _iap_get_buffer_for_send_payload(ctx).ptr; + size_t payload_size = final_ptr - ptr; + + if(ctx->opts.enable_packet_dump) { + IAP_LOGF("==== dev ===="); + struct IAPSpan payload_span = { + .ptr = _iap_get_buffer_for_send_payload(ctx).ptr, + .size = payload_size, + }; + _iap_dump_packet(lingo, command, trans_id, payload_span); + } + +#define pack_8(val) \ + ptr -= 1; \ + *(uint8_t*)ptr = val; +#define pack_16(val) \ + ptr -= 2; \ + *(uu16*)ptr = swap_16(val); + + /* fill header in reverse order */ + /* trans id */ + if(trans_id >= 0) { + pack_16(trans_id); + } + /* command id */ + if(lingo == IAPLingoID_ExtendedInterface) { + pack_16(command); + } else { + pack_8(command); + } + /* lingo */ + pack_8(lingo); + /* length */ + const uint16_t length = 1 /*lingo*/ + (lingo == IAPLingoID_ExtendedInterface ? 2 : 1) /*command*/ + (trans_id >= 0 ? 2 : 0) + payload_size; + if(length <= 0xFC) { + pack_8(length); + } else { + pack_16(length); + pack_8(0); + } + /* sof */ + pack_8(IAP_SOF_BYTE); + + /* set checksum */ + uint8_t checksum = 0; + for(uint8_t* p = ptr + 1 /* exclude sof byte */; p < final_ptr; p += 1) { + checksum += *p; + } + checksum *= -1; + *final_ptr = checksum; + + check_ret(_iap_send_hid_reports(ctx, ptr - ctx->send_buf, final_ptr - ctx->send_buf + 1 /* include checksum */), iap_false); + return iap_true; +} + +static IAPBool push_active_event(struct IAPContext* ctx, struct IAPActiveEvent event) { + if(!ctx->send_busy) { + check_ret(event.callback(ctx, &event), iap_false); + return iap_true; + } + + for(size_t i = 0; i < array_size(ctx->active_events); i += 1) { + if(ctx->active_events[i].callback == NULL) { + ctx->active_events[i] = event; + return iap_true; + } + } + return iap_false; +} + +static IAPBool process_select_sampr(struct IAPContext* ctx, struct IAPActiveEvent* event) { + if(ctx->waiting_for_audio_attrs_ack) { + IAP_ERRORF("another sampr request pending"); + } + ctx->waiting_for_audio_attrs_ack = iap_true; + + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + struct IAPTrackNewAudioAttributesPayload* request = iap_span_alloc(&request_span, sizeof(*request)); + request->sample_rate = swap_32(event->sampr); + request->sound_check = 0; + request->volume_adjustment = 0; + check_ret(_iap_send_packet(ctx, IAPLingoID_DigitalAudio, IAPDigitalAudioCommandID_TrackNewAudioAttributes, _iap_next_trans_id(ctx), request_span.ptr), iap_false); + return iap_true; +} + +IAPBool iap_select_sampr(struct IAPContext* ctx, uint32_t sampr) { + struct IAPActiveEvent event = { + .callback = process_select_sampr, + .sampr = sampr, + }; + check_ret(push_active_event(ctx, event), iap_false); + return iap_true; +} + +static IAPBool process_control_response(struct IAPContext* ctx, struct IAPActiveEvent* event) { + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + const struct IAPPlatformPendingControl* ctrl = &event->control_response.control; + const uint8_t status = event->control_response.result ? IAPAckStatus_Success : IAPAckStatus_ECommandFailed; + if(ctrl->lingo == IAPLingoID_ExtendedInterface) { + ipod_ack_ext(ctrl->req_command, status, &request_span); + } else { + ipod_ack(ctrl->req_command, status, &request_span, ctrl->ack_command); + } + check_ret(_iap_send_packet(ctx, ctrl->lingo, ctrl->ack_command, ctrl->trans_id, request_span.ptr), iap_false); + return iap_true; +} + +IAPBool iap_control_response(struct IAPContext* ctx, struct IAPPlatformPendingControl pending, IAPBool result) { + struct IAPActiveEvent event = { + .callback = process_control_response, + .control_response = {pending, result}, + }; + if(pending.lingo == IAPLingoID_SimpleRemote && pending.req_command == IAPSimpleRemoteCommandID_ContextButtonStatus) { + /* no response */ + return iap_true; + } + check_ret(push_active_event(ctx, event), iap_false); + + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/iap.h b/firmware/usbstack/iap/libiap/iap.h new file mode 100644 index 0000000000..86ad0412e1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/iap.h @@ -0,0 +1,55 @@ +#pragma once +#include "context.h" +#include "span.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* iap.c */ +IAPBool iap_init_ctx(struct IAPContext* ctx, struct IAPOpts opts, void* platform); +IAPBool iap_deinit_ctx(struct IAPContext* ctx); +IAPBool _iap_feed_packet(struct IAPContext* ctx, const uint8_t* data, size_t size); +struct IAPSpan _iap_get_buffer_for_send_payload(struct IAPContext* ctx); +int32_t _iap_next_trans_id(struct IAPContext* ctx); +IAPBool _iap_send_packet(struct IAPContext* ctx, uint8_t lingo, uint16_t command, int32_t trans_id, uint8_t* final_ptr); +/* must be called after iap_platform_on_acc_samprs_received */ +IAPBool iap_select_sampr(struct IAPContext* ctx, uint32_t sampr); +IAPBool iap_control_response(struct IAPContext* ctx, struct IAPPlatformPendingControl pending, IAPBool result); + +/* hid.c */ +IAPBool iap_feed_hid_report(struct IAPContext* ctx, const uint8_t* data, size_t size); +IAPBool iap_notify_send_complete(struct IAPContext* ctx); +IAPBool _iap_send_hid_reports(struct IAPContext* ctx, size_t begin, size_t end); /* data is passed by ctx->send_buf */ +IAPBool _iap_send_next_report(struct IAPContext* ctx); + +/* fid-token-values.c */ +int _iap_hanlde_set_fid_token_values(struct IAPSpan* request, struct IAPSpan* response); + +/* notification.c */ +void iap_notify_track_time_position(struct IAPContext* ctx, uint32_t pos_ms); +void iap_notify_track_playback_index(struct IAPContext* ctx, uint32_t index); +void iap_notify_track_caps(struct IAPContext* ctx, uint32_t caps); +void iap_notify_tracks_count(struct IAPContext* ctx, uint32_t count); +void iap_notify_play_status(struct IAPContext* ctx, uint8_t status /* IAPIPodStatePlayStatus */); +void iap_notify_volume(struct IAPContext* ctx, uint8_t volume, IAPBool muted); +void iap_notify_power_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStatePowerState */, uint8_t battery_level); +void iap_notify_shuffle_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStateShuffleSettingState */); +void iap_notify_repeat_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStateRepeatSettingState */); +void iap_notify_time_setting(struct IAPContext* ctx, const struct IAPDateTime* time); +void iap_notify_hold_switch_state(struct IAPContext* ctx, uint8_t state); + +IAPBool iap_periodic_tick(struct IAPContext* ctx); /* call every 100ms */ + +IAPBool _iap_flush_notification(struct IAPContext* ctx); + +/* debug.c */ +const char* _iap_lingo_str(uint8_t lingo); +const char* _iap_command_str(uint8_t lingo, uint16_t command); +IAPBool _iap_span_is_str(const struct IAPSpan* span); +const char* _iap_span_as_str(const struct IAPSpan* span); +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/macros.h b/firmware/usbstack/iap/libiap/macros.h new file mode 100644 index 0000000000..f2cab9bb67 --- /dev/null +++ b/firmware/usbstack/iap/libiap/macros.h @@ -0,0 +1,27 @@ +#pragma once +#include "../platform-macros.h" + +#if !defined(IAP_LOGF) +#define IAP_LOGF_MUTED +#define IAP_LOGF(...) +#endif +#define print(fmt, ...) IAP_LOGF("%s:%d: " fmt, __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); + +#if !defined(IAP_ERRORF) +#define IAP_ERRORF_MUTED +#define IAP_ERRORF(...) +#endif +#define warn(fmt, ...) IAP_ERRORF("%s:%d: " fmt, __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); + +#define check_act(cond, act, ...) \ + if(!(cond)) { \ + warn("assertion failed" __VA_OPT__(": " __VA_ARGS__)); \ + act; \ + } + +#define check_ret(cond, ret, ...) check_act(cond, return ret, __VA_ARGS__) + +#define array_size(arr) (sizeof(arr) / sizeof(arr[0])) + +#define max(a, b) (a > b ? a : b) +#define min(a, b) (a < b ? a : b) diff --git a/firmware/usbstack/iap/libiap/notification.c b/firmware/usbstack/iap/libiap/notification.c new file mode 100644 index 0000000000..2d12f4ca35 --- /dev/null +++ b/firmware/usbstack/iap/libiap/notification.c @@ -0,0 +1,200 @@ +#include "context.h" +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "spec/iap.h" + +void iap_notify_track_time_position(struct IAPContext* ctx, uint32_t pos_ms) { + ctx->notification_data.track_time_position_ms = pos_ms; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackTimePositionMSec; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackTimePositionSec; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetSec; +} + +void iap_notify_track_playback_index(struct IAPContext* ctx, uint32_t index) { + ctx->notification_data.track_playback_index = index; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackPlaybackIndex; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackIndex; +} + +void iap_notify_track_caps(struct IAPContext* ctx, uint32_t caps) { + ctx->notification_data.track_caps = caps; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackCaps; +} + +void iap_notify_tracks_count(struct IAPContext* ctx, uint32_t count) { + ctx->notification_data.tracks_count = count; + ctx->notifications_3 |= 1 << IAPIPodStateType_PlaybackEngineContents; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackEngineContentsChanged; +} + +void iap_notify_play_status(struct IAPContext* ctx, uint8_t status) { + ctx->notification_data.play_status = status; + ctx->notifications_3 |= 1 << IAPIPodStateType_PlayStatus; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackStatusExtended; + if(status == IAPIPodStatePlayStatus_PlaybackStopped) { + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackStopped; + } + /* should set Playback{FEW,REW}SeekStop, but no way to find which from EndFastForwardRewind */ +} + +void iap_notify_volume(struct IAPContext* ctx, uint8_t volume, IAPBool muted) { + ctx->notification_data.volume = volume; + ctx->notification_data.mute_state = muted; + ctx->notifications_3 |= 1 << IAPIPodStateType_Volume; +} + +void iap_notify_power_state(struct IAPContext* ctx, uint8_t state, uint8_t battery_level) { + ctx->notification_data.power_state = state; + ctx->notification_data.battery_level = battery_level; + ctx->notifications_3 |= 1 << IAPIPodStateType_Power; +} + +void iap_notify_shuffle_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.shuffle_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_ShuffleSetting; +} + +void iap_notify_repeat_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.repeat_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_RepeatSetting; +} + +void iap_notify_time_setting(struct IAPContext* ctx, const struct IAPDateTime* time) { + ctx->notification_data.time_setting = *time; + ctx->notifications_3 |= 1 << IAPIPodStateType_DateTimeSetting; +} + +void iap_notify_hold_switch_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.hold_switch_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_HoldSwitchState; +} + +IAPBool iap_periodic_tick(struct IAPContext* ctx) { + if(ctx->waiting_for_audio_attrs_ack) { + return iap_true; + } + + ctx->flushing_notifications = iap_true; + ctx->notification_tick += 1; + if(!ctx->send_busy) { + check_ret(_iap_flush_notification(ctx), iap_false); + } + return iap_true; +} + +static uint8_t play_status_to_extended(uint8_t status) { + switch(status) { + case IAPIPodStatePlayStatus_PlaybackStopped: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped; + case IAPIPodStatePlayStatus_Playing: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Playing; + case IAPIPodStatePlayStatus_PlaybackPaused: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Paused; + case IAPIPodStatePlayStatus_FastForward: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWSeekStarted; + case IAPIPodStatePlayStatus_FastRewind: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_REWSeekStarted; + case IAPIPodStatePlayStatus_EndFastForwardRewind: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWREWSeekStopped; + } + /* unreachable */ + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped; +} + +#define send_notify_3(PayloadType, StateType, set) \ + if(ctx->enabled_notifications_3 & ctx->notifications_3 & (1 << StateType)) { \ + struct PayloadType* payload = iap_span_alloc(&request, sizeof(*payload)); \ + check_ret(payload != NULL, iap_false); \ + payload->type = StateType; \ + set; \ + check_ret(_iap_send_packet(ctx, IAPLingoID_DisplayRemote, IAPDisplayRemoteCommandID_RemoteEventNotification, _iap_next_trans_id(ctx), request.ptr), iap_false); \ + ctx->notifications_3 &= ~(1 << StateType); \ + return iap_true; \ + } + +#define send_notify_4(PayloadType, StateType, set) \ + if(ctx->enabled_notifications_4 & ctx->notifications_4 & (1 << StateType)) { \ + print("notification " #StateType); \ + struct PayloadType* payload = iap_span_alloc(&request, sizeof(*payload)); \ + check_ret(payload != NULL, iap_false); \ + payload->type = StateType; \ + set; \ + check_ret(_iap_send_packet(ctx, IAPLingoID_ExtendedInterface, IAPExtendedInterfaceCommandID_PlayStatusChangeNotification, _iap_next_trans_id(ctx), request.ptr), iap_false); \ + ctx->notifications_4 &= ~(1 << StateType); \ + return iap_true; \ + } + +IAPBool _iap_flush_notification(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + + /* [1] P.257: + * Notifications for enabled events are sent every 500 ms, + * with the exception of volume change notifications, which are sent every 100 ms. + */ + if(ctx->notification_tick % 5 != 0) { + goto freq_events; + } + + send_notify_3(IAPIPodStateTrackTimePositionMSecPayload, + IAPIPodStateType_TrackTimePositionMSec, + payload->position_ms = swap_32(ctx->notification_data.track_time_position_ms)); + send_notify_3(IAPIPodStateTrackPlaybackIndexPayload, + IAPIPodStateType_TrackPlaybackIndex, + payload->index = swap_32(ctx->notification_data.track_playback_index)); + send_notify_3(IAPIPodStateTrackCapsPayload, + IAPIPodStateType_TrackCaps, + payload->caps = swap_32(ctx->notification_data.track_caps)); + send_notify_3(IAPIPodStateTrackTimePositionSecPayload, + IAPIPodStateType_TrackTimePositionSec, + payload->position_s = swap_16(ctx->notification_data.track_time_position_ms / 1000)); + send_notify_3(IAPIPodStatePlaybackEngineContentsPayload, + IAPIPodStateType_PlaybackEngineContents, + payload->count = swap_32(ctx->notification_data.tracks_count)); + send_notify_3(IAPIPodStatePlayStatusPayload, + IAPIPodStateType_PlayStatus, + payload->status = ctx->notification_data.play_status); + send_notify_3(IAPIPodStatePowerPayload, + IAPIPodStateType_Power, + payload->power_state = ctx->notification_data.power_state; + payload->battery_level = ctx->notification_data.battery_level); + send_notify_3(IAPIPodStateShuffleSettingPayload, + IAPIPodStateType_ShuffleSetting, + payload->shuffle_state = ctx->notification_data.shuffle_state); + send_notify_3(IAPIPodStateRepeatSettingPayload, + IAPIPodStateType_RepeatSetting, + payload->repeat_state = ctx->notification_data.repeat_state); + send_notify_3(IAPIPodStateDateTimeSettingPayload, + IAPIPodStateType_DateTimeSetting, + payload->year = swap_16(ctx->notification_data.time_setting.year); + payload->month = ctx->notification_data.time_setting.month; + payload->day = ctx->notification_data.time_setting.day; + payload->hour = ctx->notification_data.time_setting.hour; + payload->minute = ctx->notification_data.time_setting.minute); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackStoppedPayload, + IAPStatusChangeNotificationType_PlaybackStopped, ); + send_notify_4(IAPPlayStatusChangeNotificationTrackIndexPayload, + IAPStatusChangeNotificationType_TrackIndex, + payload->index = swap_32(ctx->notification_data.track_playback_index)); + send_notify_4(IAPPlayStatusChangeNotificationTrackTimeOffsetMSecPayload, + IAPStatusChangeNotificationType_TrackTimeOffsetMSec, + payload->offset_ms = swap_32(ctx->notification_data.track_time_position_ms)); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackStatusExtendedPayload, + IAPStatusChangeNotificationType_PlaybackStatusExtended, + payload->state = play_status_to_extended(ctx->notification_data.play_status)); + send_notify_4(IAPPlayStatusChangeNotificationTrackTimeOffsetSecPayload, + IAPStatusChangeNotificationType_TrackTimeOffsetSec, + payload->offset_s = swap_32(ctx->notification_data.track_time_position_ms / 1000)); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackEngineContentsChangedPayload, + IAPStatusChangeNotificationType_PlaybackEngineContentsChanged, + payload->count = swap_32(ctx->notification_data.tracks_count)); +freq_events: + send_notify_3(IAPIPodStateVolumePayload, + IAPIPodStateType_Volume, + payload->mute_state = ctx->notification_data.mute_state; + payload->ui_volume = ctx->notification_data.volume); + + ctx->flushing_notifications = iap_false; + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/notification.h b/firmware/usbstack/iap/libiap/notification.h new file mode 100644 index 0000000000..977f380b09 --- /dev/null +++ b/firmware/usbstack/iap/libiap/notification.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#include "datetime.h" + +struct _IAPNotifyState { + uint32_t track_time_position_ms; + uint32_t track_playback_index; + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t tracks_count; + uint8_t play_status; /* IAPIPodStatePlayStatus */ + uint8_t mute_state; + uint8_t volume; + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; + uint8_t shuffle_state; /* IAPIPodStateShuffleSettingState */ + uint8_t repeat_state; /* IAPIPodStateRepeatSettingState */ + struct IAPDateTime time_setting; + uint8_t hold_switch_state; +}; diff --git a/firmware/usbstack/iap/libiap/pack-util.h b/firmware/usbstack/iap/libiap/pack-util.h new file mode 100644 index 0000000000..bc5dae8fc8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/pack-util.h @@ -0,0 +1,41 @@ +#include +#include + +#include "bool.h" +#include "macros.h" + +__attribute__((unused)) static IAPBool pack_u8(uint8_t** data, size_t* size, uint8_t value) { + check_ret(*size >= 1, iap_false); + (*data)[0] = value; + *data += 1; + *size -= 1; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_u16(uint8_t** data, size_t* size, uint16_t value) { + check_ret(*size >= 2, iap_false); + (*data)[0] = value >> 8; + (*data)[1] = value; + *data += 2; + *size -= 2; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_u32(uint8_t** data, size_t* size, uint32_t value) { + check_ret(*size >= 2, iap_false); + (*data)[0] = value >> 24; + (*data)[1] = value >> 16; + (*data)[2] = value >> 8; + (*data)[3] = value; + *data += 4; + *size -= 4; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_data(uint8_t** data, size_t* size, const void* payload, size_t payload_size) { + check_ret(*size >= payload_size, iap_false); + memcpy(*data, payload, payload_size); + *data += payload_size; + *size -= payload_size; + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/platform.h b/firmware/usbstack/iap/libiap/platform.h new file mode 100644 index 0000000000..880f6f4c58 --- /dev/null +++ b/firmware/usbstack/iap/libiap/platform.h @@ -0,0 +1,116 @@ +#pragma once +#include +#include + +#include "datetime.h" +#include "span.h" +#include "spec/iap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct IAPContext; + +/* iap_platform_malloc */ +enum IAPPlatformMallocFlags { + IAPPlatformMallocFlags_Uncached = 1 << 0, +}; + +/* iap_platform_get_play_status */ +struct IAPPlatformPlayStatus { + uint32_t track_total_ms; + uint32_t track_pos_ms; + uint32_t track_index; + uint32_t track_count; + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint8_t state; /* IAPIPodStatePlayStatus */ +}; + +/* iap_platform_control */ +enum IAPPlatformControl { + IAPPlatformControl_TogglePlayPause, + IAPPlatformControl_Play, + IAPPlatformControl_Pause, + IAPPlatformControl_Stop, + IAPPlatformControl_Next, + IAPPlatformControl_Prev, + IAPPlatformControl_VolumeUp, + IAPPlatformControl_VolumeDown, + IAPPlatformControl_ToggleMute, +}; + +struct IAPPlatformPendingControl { + uint16_t req_command; + uint16_t ack_command; + int32_t trans_id; + uint8_t lingo; +}; + +/* iap_platform_get_volume */ +struct IAPPlatformVolumeStatus { + uint8_t volume; + IAPBool muted; +}; + +/* iap_platform_get_power_status */ +struct IAPPlatformPowerStatus { + uint8_t state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +}; + +/* iap_platform_get_indexed_track_info */ +struct IAPPlatformTrackInfo { + uint32_t* total_ms; + uint32_t* caps; /* IAPIPodStateTrackCapBits */ + struct IAPDateTime* release_date; + struct IAPSpan* artist; + struct IAPSpan* composer; + struct IAPSpan* album; + struct IAPSpan* title; +}; + +/* iap_platform_open_artwork */ +struct IAPPlatformArtwork { + uint16_t width; /* user */ + uint16_t height; /* user */ + IAPBool color; /* user */ + IAPBool valid; /* library */ + uintptr_t opaque; /* user */ +}; + +/* library routines */ +void* iap_platform_malloc(struct IAPContext* iap_ctx, size_t size, int flags); +void iap_platform_free(struct IAPContext* iap_ctx, void* ptr); +int iap_platform_send_hid_report(struct IAPContext* iap_ctx, const void* ptr, size_t size); + +/* system info */ +IAPBool iap_platform_get_ipod_serial_num(struct IAPContext* iap_ctx, struct IAPSpan* serial); + +/* audio controls */ +IAPBool iap_platform_get_play_status(struct IAPContext* iap_ctx, struct IAPPlatformPlayStatus* status); +void iap_platform_control(struct IAPContext* iap_ctx, enum IAPPlatformControl control, struct IAPPlatformPendingControl pending); +IAPBool iap_platform_get_volume(struct IAPContext* iap_ctx, struct IAPPlatformVolumeStatus* status); +IAPBool iap_platform_get_power_status(struct IAPContext* iap_ctx, struct IAPPlatformPowerStatus* status); +IAPBool iap_platform_get_shuffle_setting(struct IAPContext* iap_ctx, uint8_t* status /* IAPIPodStateShuffleSettingState */); +IAPBool iap_platform_set_shuffle_setting(struct IAPContext* iap_ctx, uint8_t status /* IAPIPodStateShuffleSettingState */); +IAPBool iap_platform_get_repeat_setting(struct IAPContext* iap_ctx, uint8_t* status /* IAPIPodStateRepeatSettingState */); +IAPBool iap_platform_set_repeat_setting(struct IAPContext* iap_ctx, uint8_t status /* IAPIPodStateRepeatSettingState */); +IAPBool iap_platform_get_date_time(struct IAPContext* iap_ctx, struct IAPDateTime* time); +IAPBool iap_platform_get_backlight_level(struct IAPContext* iap_ctx, uint8_t* level); +IAPBool iap_platform_get_hold_switch_state(struct IAPContext* iap_ctx, IAPBool* state); +IAPBool iap_platform_get_indexed_track_info(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformTrackInfo* info); +IAPBool iap_platform_set_playing_track(struct IAPContext* iap_ctx, uint32_t index); +IAPBool iap_platform_open_artwork(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformArtwork* artwork); +IAPBool iap_platform_get_artwork_ptr(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork, struct IAPSpan* span); +IAPBool iap_platform_close_artwork(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork); + +/* other callbacks */ +IAPBool iap_platform_on_acc_samprs_received(struct IAPContext* iap_ctx, struct IAPSpan* samprs); + +/* debugging */ +void iap_platform_dump_hex(const void* ptr, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/span.c b/firmware/usbstack/iap/libiap/span.c new file mode 100644 index 0000000000..98c16989ac --- /dev/null +++ b/firmware/usbstack/iap/libiap/span.c @@ -0,0 +1,46 @@ +#include + +#include "endian.h" +#include "macros.h" +#include "span.h" +#include "unaligned.h" + +const void* iap_span_read(struct IAPSpan* span, size_t count) { + check_ret(span->size >= count, NULL); + span->ptr += count; + span->size -= count; + return span->ptr - count; +} + +void* iap_span_alloc(struct IAPSpan* span, size_t count) __attribute__((alias("iap_span_read"))); + +IAPBool iap_span_append(struct IAPSpan* span, const void* ptr, size_t size) { + void* dest = iap_span_alloc(span, size); + check_ret(dest != NULL, iap_false); + memcpy(dest, ptr, size); + return iap_true; +} + +#define iap_span_template(width) \ + IAPBool iap_span_peek_##width(struct IAPSpan* span, uint##width##_t* value) { \ + check_ret(span->size >= width / 8, iap_false); \ + *value = swap_##width(*(uu##width*)span->ptr); \ + return iap_true; \ + } \ + IAPBool iap_span_read_##width(struct IAPSpan* span, uint##width##_t* value) { \ + const uu##width* ptr = iap_span_read(span, width / 8); \ + check_ret(ptr != NULL, iap_false); \ + *value = swap_##width(*ptr); \ + return iap_true; \ + } \ + IAPBool iap_span_write_##width(struct IAPSpan* span, uint##width##_t value) { \ + uint##width##_t* ptr = iap_span_alloc(span, width / 8); \ + check_ret(ptr != NULL, iap_false); \ + *ptr = swap_##width(value); \ + return iap_true; \ + } +iap_span_template(8); +iap_span_template(16); +iap_span_template(32); +iap_span_template(64); +#undef iap_span_template diff --git a/firmware/usbstack/iap/libiap/span.h b/firmware/usbstack/iap/libiap/span.h new file mode 100644 index 0000000000..ddb5b084f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/span.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bool.h" + +struct IAPSpan { + uint8_t* ptr; + size_t size; +}; + +const void* iap_span_read(struct IAPSpan* span, size_t count); +void* iap_span_alloc(struct IAPSpan* span, size_t count); +IAPBool iap_span_append(struct IAPSpan* span, const void* ptr, size_t size); + +#define iap_span_template(width) \ + IAPBool iap_span_peek_##width(struct IAPSpan* span, uint##width##_t* value); \ + IAPBool iap_span_read_##width(struct IAPSpan* span, uint##width##_t* value); \ + IAPBool iap_span_write_##width(struct IAPSpan* span, uint##width##_t value) +iap_span_template(8); +iap_span_template(16); +iap_span_template(32); +iap_span_template(64); +#undef iap_span_template + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/spec/hid.h b/firmware/usbstack/iap/libiap/spec/hid.h new file mode 100644 index 0000000000..a2c79b0fba --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/hid.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [2] P.56 Table 3-2 Link control byte usage */ +enum IAPHIDReportLinkControlBits { + IAPHIDReportLinkControlBits_Continue = 1 << 0, + IAPHIDReportLinkControlBits_MoreToFollow = 1 << 1, +}; + +struct IAPHIDReport { + uint8_t report_id; + uint8_t link_control; /* IAPHIDReportLinkControlBits */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/iap.h b/firmware/usbstack/iap/libiap/spec/iap.h new file mode 100644 index 0000000000..fe1d51ebb8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/iap.h @@ -0,0 +1,54 @@ +#pragma once +/* References: + * [1]: MFi Accessory Firmware Specification R46 + * [2]: MFI Accessory Hardware Specification R9 + * [3]: MFi Accessory Interface Specification For Apple Devices R2 + */ + +#define IAP_SYNC_BYTE 0xFF +#define IAP_SOF_BYTE 0x55 + +/* [1] P.109 Table 2-10 iAP command packet format + * | name | size | description | + * | sync | 0 or 1 | IAP_SYNC_BYTE, exists if UART transport is used | + * | sof | 1 | IAP_SOF_BYTE | + * | length | 1 or 3 | 0xNN or 0x00NNNN. sum of length of {lingo,command,trans}_id,payload | + * | lingo_id | 1 | IAP_LINGO_ID, lingo identifier | + * | command_id | 1 or 2 | 2 bytes long if lingo == 4 | + * | trans_id | 0 or 2 | exists for some commands | + * | payload | N | data | + * | checksum | 1 | crc checksum | + */ + +/* [1] P.211 Table 4-1 Additional iAP lingoes */ +enum IAPLingoID { + IAPLingoID_General = 0x00, + IAPLingoID_Microphone = 0x01, + IAPLingoID_SimpleRemote = 0x02, + IAPLingoID_DisplayRemote = 0x03, + IAPLingoID_ExtendedInterface = 0x04, + IAPLingoID_AccessoryPower = 0x05, + IAPLingoID_USBHostMode = 0x06, + IAPLingoID_RFTuner = 0x07, + IAPLingoID_AccessoryEqualizer = 0x08, + IAPLingoID_Sports = 0x09, + IAPLingoID_DigitalAudio = 0x0A, + IAPLingoID_Storage = 0x0C, + IAPLingoID_IPodOut = 0x0D, + IAPLingoID_Location = 0x0E, +}; + +#include "lingoes/accessory-equalizer.h" +#include "lingoes/accessory-power.h" +#include "lingoes/digital-audio.h" +#include "lingoes/display-remote.h" +#include "lingoes/extended-interface.h" +#include "lingoes/general.h" +#include "lingoes/ipod-out.h" +#include "lingoes/location.h" +#include "lingoes/microphone.h" +#include "lingoes/rf-tuner.h" +#include "lingoes/simple-remote.h" +#include "lingoes/sports.h" +#include "lingoes/storage.h" +#include "lingoes/usb-host-mode.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h new file mode 100644 index 0000000000..f34386a5ae --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.331 Table 4-200 Accessory Equalizer lingo command summary */ +enum IAPAccessoryEqualizerCommandID { + IAPAccessoryEqualizerCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPAccessoryEqualizerCommandID_GetCurrentEQIndex = 0x01, /* from dev, no payload */ + IAPAccessoryEqualizerCommandID_RetCurrentEQIndex = 0x02, /* from acc, current-eq-index.h */ + IAPAccessoryEqualizerCommandID_SetCurrentEQIndex = 0x03, /* from dev, current-eq-index.h */ + IAPAccessoryEqualizerCommandID_GetEQSettingCount = 0x04, /* from dev, no payload */ + IAPAccessoryEqualizerCommandID_RetEQSettingCount = 0x05, /* from acc, eq-setting-count.h */ + IAPAccessoryEqualizerCommandID_GetEQIndexName = 0x06, /* from dev, */ + IAPAccessoryEqualizerCommandID_RetEQIndexName = 0x07, /* from acc, */ +}; + +#include "accessory-equalizer/current-eq-index.h" +#include "accessory-equalizer/eq-index-name.h" +#include "accessory-equalizer/eq-setting-count.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h new file mode 100644 index 0000000000..4cd2fc0845 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h @@ -0,0 +1,10 @@ +#pragma once +#include + +struct IAPRetCurrentEQIndexPayload { + uint8_t index; +} __attribute__((packed)); + +struct IAPSetCurrentEQIndexPayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h new file mode 100644 index 0000000000..4d7f0a4cc5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAGetEQIndexNamePayload { + uint8_t index; +} __attribute__((packed)); + +struct IARetEQIndexNamePayload { + uint8_t index; + char name[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h new file mode 100644 index 0000000000..b798803c6e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetEQSettingCountPayload { + uint8_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h new file mode 100644 index 0000000000..040040a9ee --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.548 Table C-37 Accessory Power lingo command summary */ +enum IAPAccessoryPowerCommandID { + IAPAccessoryPowerCommandID_BeginHighPower = 0x02, + IAPAccessoryPowerCommandID_EndHighPower = 0x03, +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h new file mode 100644 index 0000000000..804c7db444 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.346 Table 4-232 Digital Audio lingo command summary */ +enum IAPDigitalAudioCommandID { + IAPDigitalAudioCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPDigitalAudioCommandID_IPodAck = 0x01, /* from dev, general/ipod-ack.h */ + IAPDigitalAudioCommandID_GetAccessorySampleRateCaps = 0x02, /* from dev, no payload */ + IAPDigitalAudioCommandID_RetAccessorySampleRateCaps = 0x03, /* from acc, accessory-sample-rate-caps.h */ + IAPDigitalAudioCommandID_TrackNewAudioAttributes = 0x04, /* from acc, track-new-audio-attributes.h */ + IAPDigitalAudioCommandID_SetVideoDelay = 0x05, /* from acc, set-video-delay.h */ +}; + +#include "digital-audio/accessory-sample-rate-caps.h" +#include "digital-audio/set-video-delay.h" +#include "digital-audio/track-new-audio-attributes.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h new file mode 100644 index 0000000000..4cda0456b2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPRetAccessorySampleRateCapsPayload { + uint32_t sample_rates[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h new file mode 100644 index 0000000000..851040d3cd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetVideoDelayPayload { + uint32_t delay_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h new file mode 100644 index 0000000000..e43638c016 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPTrackNewAudioAttributesPayload { + uint32_t sample_rate; + uint32_t sound_check; + uint32_t volume_adjustment; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h new file mode 100644 index 0000000000..62185f3b8f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h @@ -0,0 +1,56 @@ +#pragma once +#include + +/* [1] P.249 Table 4-48 Display Remote lingo command summary */ +enum IAPDisplayRemoteCommandID { + IAPDisplayRemoteCommandID_IPodAck = 0x00, /* from acc, general/ipod-ack.h */ + IAPDisplayRemoteCommandID_GetCurrentEQProfileIndex = 0x01, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetCurrentEQProfileIndex = 0x02, /* from dev, current-eq-profile-index.h */ + IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex = 0x03, /* from acc, current-eq-profile-index.h */ + IAPDisplayRemoteCommandID_GetNumEQProfiles = 0x04, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetNumEQProfiles = 0x05, /* from dev, num-eq-profiles.h */ + IAPDisplayRemoteCommandID_GetIndexedEQProfileName = 0x06, /* from acc, indexed-eq-profile-name.h */ + IAPDisplayRemoteCommandID_RetIndexedEQProfileName = 0x07, /* from dev, indexed-eq-profile-name.h */ + IAPDisplayRemoteCommandID_SetRemoteEventNotification = 0x08, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_RemoteEventNotification = 0x09, /* from dev, ipod-state.h */ + IAPDisplayRemoteCommandID_GetRemoteEventStatus = 0x0A, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetRemoteEventStatus = 0x0B, /* from dev, remote-event-status.h */ + IAPDisplayRemoteCommandID_GetIPodStateInfo = 0x0C, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_RetIPodStateInfo = 0x0D, /* from dev, ipod-state.h */ + IAPDisplayRemoteCommandID_SetIPodStateInfo = 0x0E, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_GetPlayStatus = 0x0F, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetPlayStatus = 0x10, /* from dev, play-status.h */ + IAPDisplayRemoteCommandID_SetCurrentPlayingTrack = 0x11, /* from acc, set-current-playing-track.h */ + IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo = 0x12, /* from acc, indexed-playing-track-info.h */ + IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo = 0x13, /* from dev, indexed-playing-track-info.h */ + IAPDisplayRemoteCommandID_GetNumPlayingTracks = 0x14, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetNumPlayingTracks = 0x15, /* from dev, num-playing-tracks.h */ + IAPDisplayRemoteCommandID_GetArtworkFormats = 0x16, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetArtworkFormats = 0x17, /* from dev, artwork-formats.h */ + IAPDisplayRemoteCommandID_GetTrackArtworkData = 0x18, /* from acc, track-artwork-data.h */ + IAPDisplayRemoteCommandID_RetTrackArtworkData = 0x19, /* from dev, track-artwork-data.h */ + IAPDisplayRemoteCommandID_GetPowerBatteryState = 0x1A, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetPowerBatteryState = 0x1B, /* from dev, power-battery-state.h */ + IAPDisplayRemoteCommandID_GetSoundCheckState = 0x1C, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetSoundCheckState = 0x1D, /* from dev, sound-check-status.h */ + IAPDisplayRemoteCommandID_SetSoundCheckState = 0x1E, /* from acc, sound-check-status.h */ + IAPDisplayRemoteCommandID_GetTrackArtworkTimes = 0x1F, /* from acc, track-artwork-times.h */ + IAPDisplayRemoteCommandID_RetTrackArtworkTimes = 0x20, /* from dev, track-artwork-times.h */ + IAPDisplayRemoteCommandID_CreateGeniusPlaylist = 0x21, /* from acc, genius_playlist.h */ + IAPDisplayRemoteCommandID_IsGeniusAvailableForTrack = 0x22, /* from acc, genius_playlist.h */ +}; + +#include "display-remote/artwork-formats.h" +#include "display-remote/current-eq-profile-index.h" +#include "display-remote/genius-playlist.h" +#include "display-remote/indexed-eq-profile-name.h" +#include "display-remote/indexed-playing-track-info.h" +#include "display-remote/ipod-state.h" +#include "display-remote/num-eq-profiles.h" +#include "display-remote/num-playing-tracks.h" +#include "display-remote/play-status.h" +#include "display-remote/power-battery-state.h" +#include "display-remote/remote-event-status.h" +#include "display-remote/set-current-playing-track.h" +#include "display-remote/track-artwork-data.h" +#include "display-remote/track-artwork-times.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h new file mode 100644 index 0000000000..5c4b072532 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h @@ -0,0 +1,21 @@ +#pragma once +#include + +enum IAPArtworkPixelFormats { + IAPArtworkPixelFormats_Mono = 0x01, + IAPArtworkPixelFormats_RGB565LE = 0x02, + IAPArtworkPixelFormats_RGB565BE = 0x03, +}; + +struct IAPArtworkFormat { + uint16_t format_id; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t image_width; + uint16_t image_height; +} __attribute__((packed)); + +/* +struct IAPRetArtworkFormatsPayload { + struct IAPArtworkFormat formats[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h new file mode 100644 index 0000000000..3c00f06f28 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetCurrentEQProfileIndexPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPSetCurrentEQProfileIndexPayload { + uint32_t index; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h new file mode 100644 index 0000000000..b1da0f9f2f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h @@ -0,0 +1,10 @@ +#pragma once +#include + +struct IAPCreateGeniusPlaylistPayload { + uint32_t playback_index; +} __attribute__((packed)); + +struct IAPIsGeniusAvailableForTrackPayload { + uint32_t playback_index; +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h new file mode 100644 index 0000000000..d526e1df10 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPGetIndexedEQProfileNamePayload { + uint32_t index; +} __attribute__((packed)); + +/* +struct IAPRetIndexedEQProfileNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h new file mode 100644 index 0000000000..4baeeed66c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h @@ -0,0 +1,85 @@ +#pragma once +#include + +enum IAPIndexedPlayingTrackInfoType { + IAPIndexedPlayingTrackInfoType_TrackCapsInfo = 0x00, + IAPIndexedPlayingTrackInfoType_ChapterTimeName = 0x01, + IAPIndexedPlayingTrackInfoType_ArtistName = 0x02, + IAPIndexedPlayingTrackInfoType_AlbumName = 0x03, + IAPIndexedPlayingTrackInfoType_GenreName = 0x04, + IAPIndexedPlayingTrackInfoType_TrackTitle = 0x05, + IAPIndexedPlayingTrackInfoType_ComposerName = 0x06, + IAPIndexedPlayingTrackInfoType_Lyrics = 0x07, + IAPIndexedPlayingTrackInfoType_ArtworkCount = 0x08, +}; + +struct IAPGetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPIndexedPlayingTrackInfoType */ + uint32_t track_index; + uint16_t chapter_index; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPIndexedPlayingTrackInfoType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_TrackCapsInfo */ + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t track_total_ms; + uint16_t chapter_count; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoChapterTimeNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ChapterTimeName */ + uint32_t offset_ms; + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoArtistNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ArtistName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoAlbumNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_AlbumName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoGenreNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_GenreName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoTrackTitlePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_TrackTitle */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoComposerNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ComposerName */ + char name[]; +} __attribute__((packed)); + +enum IAPIndexedPlayingTrackInfoLyricsInfoBits { + IAPIndexedPlayingTrackInfoLyricsInfoBits_Series = 1 << 0, + IAPIndexedPlayingTrackInfoLyricsInfoBits_Last = 1 << 1, +}; + +struct IAPRetIndexedPlayingTrackInfoLyricsPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_Lyrics */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char lyrics[]; +} __attribute__((packed)); + +struct IAPArtworkCount { + uint16_t format; /* IAPArtworkPixelFormats */ + uint16_t count; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoArtworkCountPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ArtworkCount */ + struct IAPArtworkCount data[]; /* or 0x08 to indicate no counts */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h new file mode 100644 index 0000000000..a861ec3667 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h @@ -0,0 +1,204 @@ +#pragma once +#include + +/* common state types */ + +/* [1] P.266 Table 4-74 Apple device state data */ +enum IAPIPodStateType { + IAPIPodStateType_TrackTimePositionMSec = 0x00, + IAPIPodStateType_TrackPlaybackIndex = 0x01, + IAPIPodStateType_ChapterIndex = 0x02, + IAPIPodStateType_PlayStatus = 0x03, + IAPIPodStateType_Volume = 0x04, + IAPIPodStateType_Power = 0x05, + IAPIPodStateType_EQSetting = 0x06, + IAPIPodStateType_ShuffleSetting = 0x07, + IAPIPodStateType_RepeatSetting = 0x08, + IAPIPodStateType_DateTimeSetting = 0x09, + IAPIPodStateType_AlarmSetting = 0x0A, + IAPIPodStateType_BacklightLevel = 0x0B, + IAPIPodStateType_HoldSwitchState = 0x0C, + IAPIPodStateType_SoundCheckState = 0x0D, + IAPIPodStateType_AudiobookSpeeed = 0x0E, + IAPIPodStateType_TrackTimePositionSec = 0x0F, + IAPIPodStateType_AbsoluteVolume = 0x10, + IAPIPodStateType_TrackCaps = 0x11, + IAPIPodStateType_PlaybackEngineContents = 0x12, +}; + +struct IAPIPodStatePayload { + uint8_t type; /* IAPIPodStateType */ + uint8_t data[]; +} __attribute__((packed)); + +/* [1] P.257 Table 4-61 Event notification data */ + +struct IAPIPodStateTrackTimePositionMSecPayload { + uint8_t type; /* = IAPIPodStateType_TrackTimePositionMSec */ + uint32_t position_ms; +} __attribute__((packed)); + +struct IAPIPodStateTrackPlaybackIndexPayload { + uint8_t type; /* = IAPIPodStateType_TrackPlaybackIndex */ + uint32_t index; +} __attribute__((packed)); + +struct IAPIPodStateChapterIndexPayload { + uint8_t type; /* = IAPIPodStateType_ChapterIndex */ + uint32_t index; + uint16_t chapter_count; + uint16_t chapter_index; +} __attribute__((packed)); + +enum IAPIPodStatePlayStatus { + IAPIPodStatePlayStatus_PlaybackStopped = 0x00, + IAPIPodStatePlayStatus_Playing = 0x01, + IAPIPodStatePlayStatus_PlaybackPaused = 0x02, + IAPIPodStatePlayStatus_FastForward = 0x03, + IAPIPodStatePlayStatus_FastRewind = 0x04, + IAPIPodStatePlayStatus_EndFastForwardRewind = 0x05, +}; + +struct IAPIPodStatePlayStatusPayload { + uint8_t type; /* = IAPIPodStateType_PlayStatus */ + uint8_t status; /* IAPIPodStatePlayStatus */ +} __attribute__((packed)); + +struct IAPIPodStateVolumePayload { + uint8_t type; /* = IAPIPodStateType_Volume */ + uint8_t mute_state; + uint8_t ui_volume; +} __attribute__((packed)); + +enum IAPIPodStatePowerState { + IAPIPodStatePowerState_InternalLow = 0x00, + IAPIPodStatePowerState_Internal = 0x01, + IAPIPodStatePowerState_ExternalBattery = 0x02, + IAPIPodStatePowerState_External = 0x03, + IAPIPodStatePowerState_ExternalCharging = 0x04, + IAPIPodStatePowerState_ExternalCharged = 0x05, +}; + +struct IAPIPodStatePowerPayload { + uint8_t type; /* = IAPIPodStateType_Power */ + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +} __attribute__((packed)); + +struct IAPIPodStateEQSettingPayload { + uint8_t type; /* = IAPIPodStateType_EQSetting */ + uint32_t eq_index; +} __attribute__((packed)); + +enum IAPIPodStateShuffleSettingState { + IAPIPodStateShuffleSettingState_Off = 0x00, + IAPIPodStateShuffleSettingState_Tracks = 0x01, + IAPIPodStateShuffleSettingState_Albums = 0x02, +}; + +struct IAPIPodStateShuffleSettingPayload { + uint8_t type; /* = IAPIPodStateType_ShuffleSetting */ + uint8_t shuffle_state; /* IAPIPodStateShuffleSettingState */ +} __attribute__((packed)); + +enum IAPIPodStateRepeatSettingState { + IAPIPodStateRepeatSettingState_Off = 0x00, + IAPIPodStateRepeatSettingState_One = 0x01, + IAPIPodStateRepeatSettingState_All = 0x02, +}; + +struct IAPIPodStateRepeatSettingPayload { + uint8_t type; /* = IAPIPodStateType_RepeatSetting */ + uint8_t repeat_state; /* IAPIPodStateRepeatSettingState */ +} __attribute__((packed)); + +struct IAPIPodStateDateTimeSettingPayload { + uint8_t type; /* = IAPIPodStateType_DateTimeSetting */ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; +} __attribute__((packed)); + +struct IAPIPodStateAlarmSettingPayload { + uint8_t type; /* = IAPIPodStateType_AlarmSetting */ + uint8_t deprecated[3]; +} __attribute__((packed)); + +struct IAPIPodStateBacklightLevelPayload { + uint8_t type; /* = IAPIPodStateType_BacklightLevel */ + uint8_t level; +} __attribute__((packed)); + +struct IAPIPodStateHoldSwitchStatePayload { + uint8_t type; /* = IAPIPodStateType_HoldSwitchState */ + uint8_t state; +} __attribute__((packed)); + +struct IAPIPodStateSoundCheckStatePayload { + uint8_t type; /* = IAPIPodStateType_SoundCheckState */ + uint8_t state; +} __attribute__((packed)); + +enum IAPIPodStateAudiobookSpeeed { + IAPIPodStateAudiobookSpeeed_Slower = 0xFF, + IAPIPodStateAudiobookSpeeed_Normal = 0x00, + IAPIPodStateAudiobookSpeeed_Faster = 0x01, +}; + +struct IAPIPodStateAudiobookSpeeedPayload { + uint8_t type; /* = IAPIPodStateType_AudiobookSpeeed */ + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ +} __attribute__((packed)); + +struct IAPIPodStateTrackTimePositionSecPayload { + uint8_t type; /* = IAPIPodStateType_TrackTimePositionSec */ + uint16_t position_s; +} __attribute__((packed)); + +struct IAPIPodStateAbsoluteVolumePayload { + uint8_t type; /* = IAPIPodStateType_AbsoluteVolume */ + uint8_t mute_state; + uint8_t ui_volume; + uint8_t absolute_volume; +} __attribute__((packed)); + +enum IAPIPodStateTrackCapBits { + IAPIPodStateTrackCapBits_IsAudiobook = 1 << 0, + IAPIPodStateTrackCapBits_HasChapters = 1 << 1, + IAPIPodStateTrackCapBits_HasAlbumArts = 1 << 2, + IAPIPodStateTrackCapBits_HasLyrics = 1 << 3, + IAPIPodStateTrackCapBits_IsPodcast = 1 << 4, + IAPIPodStateTrackCapBits_HasReleaseDate = 1 << 5, + IAPIPodStateTrackCapBits_HasDescription = 1 << 6, + IAPIPodStateTrackCapBits_HasVideo = 1 << 7, + IAPIPodStateTrackCapBits_IsQueued = 1 << 8, + IAPIPodStateTrackCapBits_GenerateGeniusPlaylist = 1 << 13, + IAPIPodStateTrackCapBits_IsITunesUEpisode = 1 << 14, +}; + +struct IAPIPodStateTrackCapsPayload { + uint8_t type; /* = IAPIPodStateType_TrackCaps */ + uint32_t caps; /* IAPIPodStateTrackCapBits */ +} __attribute__((packed)); + +struct IAPIPodStatePlaybackEngineContentsPayload { + uint8_t type; /* = IAPIPodStateType_PlaybackEngineContents */ + uint32_t count; +} __attribute__((packed)); + +/* event notification */ +struct IAPSetRemoteEventNotificationPayload { + uint32_t mask; /* (1 << IAPIPodStateType) | ... */ +} __attribute__((packed)); + +/* IAPRemoteEventNotificationPayload = IAPIPodStatePayload */ + +/* get/ret state info */ +struct IAPGetIPodStateInfoPayload { + uint8_t type; /* = IAPIPodStateType */ +} __attribute__((packed)); + +/* RetIPodStateInfoPayload = IAPIPodStatePayload */ +/* SetIPodStateInfoPayload = IAPIPodStatePayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h new file mode 100644 index 0000000000..bb40c6dacf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetNumEQProfilesPayload { + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h new file mode 100644 index 0000000000..b8d7a59d6a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetNumPlayingTracksPayload { + uint32_t num_playing_tracks; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h new file mode 100644 index 0000000000..6ddfd38d74 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h @@ -0,0 +1,9 @@ +#pragma once +#include + +struct IAPRetPlayStatusPayload { + uint8_t state; /* IAPIPodStatePlayStatus */ + uint32_t track_index; + uint32_t track_total_ms; + uint32_t track_pos_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h new file mode 100644 index 0000000000..43f03f5e9f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPRetPowerBatteryStatePayload { + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h new file mode 100644 index 0000000000..7b7e6a4a39 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetRemoteEventStatusPayload { + uint32_t mask; /* (1 << IAPRemoteEventNotifications) | ... */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h new file mode 100644 index 0000000000..b2522186a8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetCurrentPlayingTrackPayload { + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h new file mode 100644 index 0000000000..f540c63f26 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h @@ -0,0 +1,26 @@ +#pragma once +#include + +struct IAPGetTrackArtworkDataPayload { + uint32_t track_index; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPRetTrackArtworkDataFirstPayload { + uint16_t index; /* = 0x0000 */ + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; + uint16_t inset_top_left_x; + uint16_t inset_top_left_y; + uint16_t inset_bottom_right_x; + uint16_t inset_bottom_right_y; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetTrackArtworkDataSubsequenctPayload { + uint16_t index; /* > 0x0000 */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h new file mode 100644 index 0000000000..670b4b5732 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h @@ -0,0 +1,15 @@ +#pragma once +#include + +struct IAPGetTrackArtworkTimesPayload { + uint32_t track_index; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +/* +struct IAPRetTrackArtworkTimesPayload { + uint32_t offsets_ms[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h new file mode 100644 index 0000000000..b40c4342ea --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h @@ -0,0 +1,109 @@ +#pragma once +#include + +/* [1] P.398 Table 5-1 Extended Interface lingo command summary */ +enum IAPExtendedInterfaceCommandID { + IAPExtendedInterfaceCommandID_IPodAck = 0x0001, /* 1.00, NoAuth, from acc, ipod-ack.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterInfo = 0x0002, /* 1.06, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo = 0x0003, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_SetCurrentPlayingTrackChapter = 0x0004, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterPlayStatus = 0x0005, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterPlayStatus = 0x0006, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterName = 0x0007, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterName = 0x0008, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetAudiobookSpeed = 0x0009, /* 1.06, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_RetAudiobookSpeed = 0x000A, /* 1.06, NoAuth, from dev, audiobook-speed.h */ + IAPExtendedInterfaceCommandID_SetAudiobookSpeed = 0x000B, /* 1.06, NoAuth, from acc, audiobook-speed.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo = 0x000C, /* 1.08, NoAuth, from acc, indexed-playing-track-info.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo = 0x000D, /* 1.08, NoAuth, from dev, indexed-playing-track-info.h */ + IAPExtendedInterfaceCommandID_GetArtworkFormats = 0x000E, /* 1.10, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_RetArtworkFormats = 0x000F, /* 1.10, NoAuth, from dev, display-remote/artwork-formats.h*/ + IAPExtendedInterfaceCommandID_GetTrackArtworkData = 0x0010, /* 1.10, NoAuth, from acc, display-remote/track-artwork-data.h*/ + IAPExtendedInterfaceCommandID_RetTrackArtworkData = 0x0011, /* 1.10, NoAuth, from dev, display-remote/track-artwork-data.h*/ + IAPExtendedInterfaceCommandID_RequestProtocolVersion = 0x0012, /* deprecated */ + IAPExtendedInterfaceCommandID_ReturnProtocolVersion = 0x0013, /* deprecated */ + IAPExtendedInterfaceCommandID_RequestIPodName = 0x0014, /* deprecated */ + IAPExtendedInterfaceCommandID_ReturnIPodName = 0x0015, /* deprecated */ + IAPExtendedInterfaceCommandID_ResetDBSelection = 0x0016, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_SelectDBRecord = 0x0017, /* 1.00, NoAuth, from dev, database.h*/ + IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords = 0x0018, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords = 0x0019, /* 1.00, NoAuth, from dev, database.h */ + IAPExtendedInterfaceCommandID_RetrieveCategorizedDatabaseRecords = 0x001A, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_ReturnCategorizedDatabaseRecords = 0x001B, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_GetPlayStatus = 0x001C, /* 1.00, NoAuth, no payload */ + IAPExtendedInterfaceCommandID_ReturnPlayStatus = 0x001D, /* 1.00, NoAuth, play-status.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackIndex = 0x001E, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex = 0x001F, /* 1.00, NoAuth, from dev, current-playing-index.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle = 0x0020, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle = 0x0021, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName = 0x0022, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName = 0x0023, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName = 0x0024, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName = 0x0025, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification = 0x0026, /* 1.00, NoAuth, from acc, play-status-change-notification.h */ + IAPExtendedInterfaceCommandID_PlayStatusChangeNotification = 0x0027, /* 1.00, NoAuth, from dev, play-status-change-notification.h */ + IAPExtendedInterfaceCommandID_PlayCurrentSelection = 0x0028, /* 1.00, NoAuth, from acc, play-current-selection.h */ + IAPExtendedInterfaceCommandID_PlayControl = 0x0029, /* 1.00, NoAuth, from acc, play-control.h */ + IAPExtendedInterfaceCommandID_GetTrackArtworkTimes = 0x002A, /* 1.10, NoAuth, from acc, display-remote/track-artwork-times.h */ + IAPExtendedInterfaceCommandID_RetTrackArtworkTimes = 0x002B, /* 1.10, NoAuth, from dev, display-remote/track-artwork-times.h */ + IAPExtendedInterfaceCommandID_GetShuffle = 0x002C, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnShuffle = 0x002D, /* 1.00, NoAuth, from dev, shuffle.h */ + IAPExtendedInterfaceCommandID_SetShuffle = 0x002E, /* 1.00, NoAuth, from acc, shuffle.h */ + IAPExtendedInterfaceCommandID_GetRepeat = 0x002F, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnRepeat = 0x0030, /* 1.00, NoAuth, from dev, repeat.h */ + IAPExtendedInterfaceCommandID_SetRepeat = 0x0031, /* 1.00, NoAuth, from acc, repeat.h */ + IAPExtendedInterfaceCommandID_SetDisplayImage = 0x0032, /* 1.01, NoAuth, from acc, set-display-image.h */ + IAPExtendedInterfaceCommandID_GetMonoDisplayImageLimits = 0x0033, /* 1.01, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnMonoDisplayImageLimits = 0x0034, /* 1.01, NoAuth, from dev, mono-display-image-limits.h */ + IAPExtendedInterfaceCommandID_GetNumPlayingTracks = 0x0035, /* 1.01, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks = 0x0036, /* 1.01, NoAuth, from dev, display-remote/num-playing-tracks.h */ + IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack = 0x0037, /* 1.01, NoAuth, from acc, display-remote/set-current-playing-track.h */ + IAPExtendedInterfaceCommandID_SelectSortDBRecord = 0x0038, /* deprecated */ + IAPExtendedInterfaceCommandID_GetColorDisplayImageLimits = 0x0039, /* 1.09, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits = 0x003A, /* 1.09, NoAuth, from dev, color-display-image-limits.h */ + IAPExtendedInterfaceCommandID_ResetDBSelectionHierarchy = 0x003B, /* 1.11, Auth, from acc, db-selection-hierarchy.h */ + IAPExtendedInterfaceCommandID_GetDBITunesInfo = 0x003C, /* 1.13, Auth, from acc, db-itunes-info.h */ + IAPExtendedInterfaceCommandID_RetDBITunesInfo = 0x003D, /* 1.13, Auth, from dev, db-itunes-info.h */ + IAPExtendedInterfaceCommandID_GetUIDTrackInfo = 0x003E, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetUIDTrackInfo = 0x003F, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_GetDBTrackInfo = 0x0040, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetDBTrackInfo = 0x0041, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_GetPBTrackInfo = 0x0042, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetPBTrackInfo = 0x0043, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_CreateGeniusPlaylist = 0x0044, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_RefreshGeniusPlaylist = 0x0045, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_IsGeniusAvailableForTrack = 0x0047, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_GetPlaylistInfo = 0x0048, /* 1.13, Auth, from acc, playlist-info.h */ + IAPExtendedInterfaceCommandID_RetPlaylistInfo = 0x0049, /* 1.13, Auth, from dev, playlist-info.h */ + IAPExtendedInterfaceCommandID_PrepareUIDList = 0x004A, /* 1.14, Auth, from dev, uid-list.h */ + IAPExtendedInterfaceCommandID_PlayPreparedUIDList = 0x004B, /* 1.14, Auth, from dev, uid-list.h */ + IAPExtendedInterfaceCommandID_GetArtworkTimes = 0x004C, /* 1.14, Auth, from acc, artwork-times.h */ + IAPExtendedInterfaceCommandID_RetArtworkTimes = 0x004D, /* 1.14, Auth, from dev, artwork-times.h */ + IAPExtendedInterfaceCommandID_GetArtworkData = 0x004E, /* 1.14, Auth, from acc, artwork-data.h */ + IAPExtendedInterfaceCommandID_RetArtworkData = 0x004F, /* 1.14, Auth, from dev, artwork-data.h */ +}; + +#include "extended-interface/artwork-data.h" +#include "extended-interface/artwork-times.h" +#include "extended-interface/audiobook-speed.h" +#include "extended-interface/color-display-image-limits.h" +#include "extended-interface/current-playing-track-chapter.h" +#include "extended-interface/current-playing-track-index.h" +#include "extended-interface/database.h" +#include "extended-interface/db-itunes-info.h" +#include "extended-interface/db-selection-hierarchy.h" +#include "extended-interface/genius-playlist.h" +#include "extended-interface/indexed-playing-track-info.h" +#include "extended-interface/indexed-playing-track-string.h" +#include "extended-interface/ipod-ack.h" +#include "extended-interface/mono-display-image-limits.h" +#include "extended-interface/play-control.h" +#include "extended-interface/play-current-selection.h" +#include "extended-interface/play-status-change-notification.h" +#include "extended-interface/play-status.h" +#include "extended-interface/playlist-info.h" +#include "extended-interface/repeat.h" +#include "extended-interface/set-display-image.h" +#include "extended-interface/shuffle.h" +#include "extended-interface/track-info.h" +#include "extended-interface/uid-list.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h new file mode 100644 index 0000000000..67c0569750 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h @@ -0,0 +1,58 @@ +#pragma once +#include + +/* [1] P.459 5.1.72 Command 0x004E: GetArtworkData */ + +struct IAPGetArtworkDataPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPGetArtworkDataUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPGetArtworkDataIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPRetArtworkDataPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkDataBody { + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; /* [1] P.461 "Table 5-104 imageDescriptionAndData format" misses this field, but should be here */ + uint16_t inset_top_left_x; + uint16_t inset_top_left_y; + uint16_t inset_bottom_right_x; + uint16_t inset_bottom_right_y; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkDataUIDPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + struct IAPRetArtworkDataBody body; +} __attribute__((packed)); + +struct IAPRetArtworkDataIndexPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + struct IAPRetArtworkDataBody body; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h new file mode 100644 index 0000000000..41207efe5e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h @@ -0,0 +1,48 @@ +#pragma once +#include + +/* [1] P.458 5.1.70 Command 0x004C: GetArtworkTimes */ + +enum IAPArtworkTrackIDType { + IAPGetArtworkTrackIDType_UID = 0x00, + IAPGetArtworkTrackIDType_PlaybackListIndex = 0x01, + IAPGetArtworkTrackIDType_DatabaseIndex = 0x02, +}; + +struct IAPGetArtworkTimesPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPGetArtworkTimesUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +struct IAPGetArtworkTimesIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +struct IAPRetArtworkTimesPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkTimesUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint32_t offsets_ms[]; +} __attribute__((packed)); + +struct IAPRetArtworkTimesIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint32_t offsets_ms[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h new file mode 100644 index 0000000000..770cf06039 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetAudiobookSpeedPayload { + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ +} __attribute__((packed)); + +struct IAPSetAudiobookSpeedPayload { + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ + uint8_t restore_on_exit; /* optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h new file mode 100644 index 0000000000..c5e75fd3b7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.443 5.1.53 Command 0x003A: ReturnColorDisplayImageLimits */ + +struct IAPColorDisplayImageLimit { + uint16_t max_width; + uint16_t max_height; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ +} __attribute__((packed)); + +/* +struct IAPReturnColorDisplayImageLimitsPayload { + struct IAPColorDisplayImageLimit limits[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h new file mode 100644 index 0000000000..f059b53c49 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h @@ -0,0 +1,30 @@ +#pragma once +#include + +struct IAPReturnCurrentPlayingTrackChapterInfoPayload { + uint32_t index; + uint32_t count; +} __attribute__((packed)); + +struct IAPSetCurrentPlayingTrackChapterPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPGetCurrentPlayingTrackChapterPlayStatusPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPReturnCurrentPlayingTrackChapterPlayStatusPayload { + uint32_t length_ms; + uint32_t elapsed_ms; +} __attribute__((packed)); + +struct IAPGetCurrentPlayingTrackChapterNamePayload { + uint32_t index; +} __attribute__((packed)); + +/* +struct IAPReturnCurrentPlayingTrackChapterNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h new file mode 100644 index 0000000000..1f86a18e83 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnCurrentPlayingTrackIndexPayload { + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h new file mode 100644 index 0000000000..55a548ba7e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h @@ -0,0 +1,42 @@ +#pragma once +#include + +enum IAPDatabaseType { + IAPDatabaseType_TopLevel = 0x00, /* 1.14 */ + IAPDatabaseType_Playlist = 0x01, /* 1.00 */ + IAPDatabaseType_Artist = 0x02, /* 1.00 */ + IAPDatabaseType_Album = 0x03, /* 1.00 */ + IAPDatabaseType_Genre = 0x04, /* 1.00 */ + IAPDatabaseType_Track = 0x05, /* 1.00 */ + IAPDatabaseType_Composer = 0x06, /* 1.00 */ + IAPDatabaseType_Audiobook = 0x07, /* 1.06 */ + IAPDatabaseType_Podcast = 0x08, /* 1.08 */ + IAPDatabaseType_NestedPlaylist = 0x09, /* 1.13 */ + IAPDatabaseType_GeniusMixes = 0x0A, /* 1.14 */ + IAPDatabaseType_ITunesU = 0x0B, /* 1.14 */ + +}; + +struct IAPSelectDBRecord { + uint8_t type; /* IAPDatabaseType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPGetNumberCategorizedDBRecordsPayload { + uint8_t type; /* IAPDatabaseType */ +} __attribute__((packed)); + +struct IAPReturnNumberCategorizedDBRecordsPayload { + uint32_t count; +} __attribute__((packed)); + +struct IAPRetrieveCategorizedDatabaseRecordsPayload { + uint8_t type; /* IAPDatabaseType */ + uint32_t index; + uint32_t count; +} __attribute__((packed)); + +struct IAPReturnCategorizedDatabaseRecordsPayload { + uint32_t index; + char record[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h new file mode 100644 index 0000000000..11618be6eb --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.444 5.1.55 Command 0x003C: GetDBiTunesInfo */ + +enum IAPITunesMetadataType { + IAPITunesMetadataType_UID = 0x00, + IAPITunesMetadataType_LastSyncDate = 0x01, + IAPITunesMetadataType_AudioTrackCount = 0x02, + IAPITunesMetadataType_VideoTrackCount = 0x03, + IAPITunesMetadataType_AudiobookCount = 0x04, + IAPITunesMetadataType_PhotoCount = 0x05, +}; + +struct IAPGetDBITunesInfoPayload { + uint8_t metadata_type; /* IAPITunesMetadataType */ +} __attribute__((packed)); + +struct IAPRetDBITunesInfoPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoUIDPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_UID */ + uint8_t uid[8]; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoLastSyncDatePayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_LastSyncDate */ + uint8_t seconds; + uint8_t minute; + uint8_t hour; + uint8_t day; + uint8_t month; + uint16_t year; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoCountPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_{AudioTrack,VideoTrack,Audiobook,Photo}Count */ + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h new file mode 100644 index 0000000000..434b21bab3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.443 5.1.54 Command 0x003B: ResetDBSelectionHierarchy */ + +enum IAPResetDBSelectionHierarchySelection { + IAPResetDBSelectionHierarchySelection_Audio = 0x01, + IAPResetDBSelectionHierarchySelection_Video = 0x02, +}; + +struct IAPResetDBSelectionHierarchyPayload { + uint8_t selection; /* IAPResetDBSelectionHierarchySelection */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h new file mode 100644 index 0000000000..81601886e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h @@ -0,0 +1,23 @@ +#pragma once +#include + +/* [1] P.454 5.1.63 Command 0x0044: CreateGeniusPlaylist */ + +enum IAPCreateGeniusPlaylistIndexType { + IAPCreateGeniusPlaylistIndexType_DatabaseEngine = 0x00, + IAPCreateGeniusPlaylistIndexType_PlaybackEngine = 0x01, +}; + +struct IAPExtendedCreateGeniusPlaylistPayload { + uint8_t index_type; /* IAPCreateGeniusPlaylistIndexType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPExtendedRefreshGeniusPlaylistPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPExtendedIsGeniusAvailableForTrackPayload { + uint8_t index_type; /* IAPCreateGeniusPlaylistIndexType */ + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h new file mode 100644 index 0000000000..ed7615d1f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h @@ -0,0 +1,81 @@ +#pragma once +#include + +/* [1] P.408 5.1.13 Command 0x000C: GetIndexedPlayingTrackInfo */ + +enum IAPExtendedIndexedPlayingTrackInfoType { + IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo = 0x00, + IAPExtendedIndexedPlayingTrackInfoType_PodcastName = 0x01, + IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate = 0x02, + IAPExtendedIndexedPlayingTrackInfoType_TrackDescription = 0x03, + IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics = 0x04, + IAPExtendedIndexedPlayingTrackInfoType_TrackGenre = 0x05, + IAPExtendedIndexedPlayingTrackInfoType_TrackComposer = 0x06, + IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount = 0x07, +}; + +struct IAPExtendedGetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPExtendedIndexedPlayingTrackInfoType */ + uint32_t track_index; + uint16_t chapter_index; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPExtendedIndexedPlayingTrackInfoType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackCapsInfo */ + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t track_total_ms; + uint16_t chapter_count; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoPodcastNamePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_PodcastName */ + char name[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackReleaseDate */ + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t weekday; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackDescription */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char description[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackSongLyrics */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char lyrics[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackGenrePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackGenre */ + char genre[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackComposerPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackComposer */ + char composer[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackArtworkCountPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackArtworkCount */ + struct { + uint16_t format; /* IAPArtworkPixelFormats */ + uint16_t count; + } __attribute__((packed)) data[]; /* or 0x08 to indicate no counts */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h new file mode 100644 index 0000000000..895fa2eba3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h @@ -0,0 +1,20 @@ +#pragma once +#include + +struct IAPGetIndexedPlayingTrackStringPayload { + uint32_t index; +} __attribute__((packed)); + +/* IAPGetIndexedPlayingTrackTitlePayload = IAPGetIndexedPlayingTrackStringPayload */ +/* IAPGetIndexedPlayingTrackArtistNamePayload = IAPGetIndexedPlayingTrackStringPayload */ +/* IAPGetIndexedPlayingTrackAlbumNamePayload = IAPGetIndexedPlayingTrackStringPayload */ + +/* +struct IAPReturnIndexedPlayingTrackStringPayload { + char string[]; +} __attribute__((packed)); +*/ + +/* IAPReturnIndexedPlayingTrackTitlePayload = IAPReturnIndexedPlayingTrackStringPayload */ +/* IAPReturnIndexedPlayingTrackArtistNamePayload = IAPReturnIndexedPlayingTrackStringPayload */ +/* IAPReturnIndexedPlayingTrackAlbumNamePayload = IAPReturnIndexedPlayingTrackStringPayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h new file mode 100644 index 0000000000..bcaf1e2203 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPExtendedIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint16_t id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h new file mode 100644 index 0000000000..6f8d8ac375 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.440 5.1.48 Command 0x0034: ReturnMonoDisplayImageLimits */ + +struct IAPReturnMonoDisplayImageLimitsPayload { + uint16_t max_width; + uint16_t max_height; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h new file mode 100644 index 0000000000..0360026daa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h @@ -0,0 +1,25 @@ +#pragma once +#include + +/* [1] P.428 5.1.37 Command 0x0029: PlayControl */ + +enum IAPPlayControlCode { + IAPPlayControlCode_TogglePlayPause = 0x01, /* 1.00 */ + IAPPlayControlCode_Stop = 0x02, /* 1.00 */ + IAPPlayControlCode_NextTrack = 0x03, /* 1.00 */ + IAPPlayControlCode_PrevTrack = 0x04, /* 1.00 */ + IAPPlayControlCode_StartFF = 0x05, /* 1.00 */ + IAPPlayControlCode_StartRew = 0x06, /* 1.00 */ + IAPPlayControlCode_EndFFRew = 0x07, /* 1.00 */ + IAPPlayControlCode_Next = 0x08, /* 1.06 */ + IAPPlayControlCode_Prev = 0x09, /* 1.06 */ + IAPPlayControlCode_Play = 0x0A, /* 1.13 */ + IAPPlayControlCode_Pause = 0x0B, /* 1.13 */ + IAPPlayControlCode_NextChapter = 0x0C, /* 1.14 */ + IAPPlayControlCode_PrevChapter = 0x0D, /* 1.14 */ + IAPPlayControlCode_ResumeIPod = 0x0E, /* 1.14 */ +}; + +struct IAPPlayControlPayload { + uint8_t code; /* IAPPlayControlCode */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h new file mode 100644 index 0000000000..a543ae7b86 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.546 Table C-34 PlayCurrentSelection packet */ + +struct IAPPlayCurrentSelectionPayload { + uint32_t track_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h new file mode 100644 index 0000000000..a7ce5095f5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h @@ -0,0 +1,137 @@ +#pragma once +#include + +struct IAPSetPlayStatusChangeNotification1BytePayload { + uint8_t enable; +} __attribute__((packed)); + +enum IAPStatusChangeNotificationBits { + IAPStatusChangeNotificationBits_Basic = 1 << 0, + IAPStatusChangeNotificationBits_Extended = 1 << 1, + IAPStatusChangeNotificationBits_TrackIndex = 1 << 2, + IAPStatusChangeNotificationBits_TrackTimeOffsetMSec = 1 << 3, + IAPStatusChangeNotificationBits_TrackTimeOffsetSec = 1 << 4, + IAPStatusChangeNotificationBits_ChapterIndex = 1 << 5, + IAPStatusChangeNotificationBits_ChapterTimeOffsetMSec = 1 << 6, + IAPStatusChangeNotificationBits_ChapterTimeOffsetSec = 1 << 7, + IAPStatusChangeNotificationBits_TrackUniqueID = 1 << 8, + IAPStatusChangeNotificationBits_TrackMediaType = 1 << 9, + IAPStatusChangeNotificationBits_TrackLyricsReady = 1 << 10, + IAPStatusChangeNotificationBits_TrackCapsChanged = 1 << 11, + IAPStatusChangeNotificationBits_PlaybackEngineContentsChanged = 1 << 12, + +}; + +struct IAPSetPlayStatusChangeNotification4BytesPayload { + uint32_t mask; /* IAPStatusChangeNotificationBits */ +} __attribute__((packed)); + +/* [1] P.426 5.1.36 Command 0x0027: PlayStatusChangeNotification */ + +enum IAPStatusChangeNotificationType { + IAPStatusChangeNotificationType_PlaybackStopped = 0x00, + IAPStatusChangeNotificationType_TrackIndex = 0x01, + IAPStatusChangeNotificationType_PlaybackFEWSeekStop = 0x02, + IAPStatusChangeNotificationType_PlaybackREWSeekStop = 0x03, + IAPStatusChangeNotificationType_TrackTimeOffsetMSec = 0x04, + IAPStatusChangeNotificationType_ChapterIndex = 0x05, + IAPStatusChangeNotificationType_PlaybackStatusExtended = 0x06, + IAPStatusChangeNotificationType_TrackTimeOffsetSec = 0x07, + IAPStatusChangeNotificationType_ChapterTimeOffsetMSec = 0x08, + IAPStatusChangeNotificationType_ChapterTimeOffsetSec = 0x09, + IAPStatusChangeNotificationType_TrackUniqueID = 0x0A, + IAPStatusChangeNotificationType_TrackPlaybackMode = 0x0B, + IAPStatusChangeNotificationType_TrackLyricsReady = 0x0C, + IAPStatusChangeNotificationType_TrackCapsChanged = 0x0D, + IAPStatusChangeNotificationType_PlaybackEngineContentsChanged = 0x0E, +}; + +struct IAPPlayStatusChangeNotificationPayload { + uint8_t type; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackStoppedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackStopped */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackIndexPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackIndex */ + uint32_t index; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackFEWSeekStopPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackFEWSeekStop */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackREWSeekStopPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackREWSeekStop */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackTimeOffsetMSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackTimeOffsetMSec */ + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterIndexPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterIndex */ + uint32_t index; +} __attribute__((packed)); + +enum IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates { + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped = 0x02, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWSeekStarted = 0x05, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_REWSeekStarted = 0x06, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWREWSeekStopped = 0x07, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Playing = 0x0A, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Paused = 0x0B, +}; + +struct IAPPlayStatusChangeNotificationPlaybackStatusExtendedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackStatusExtended */ + uint8_t state; /* IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackTimeOffsetSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackTimeOffsetSec */ + uint32_t offset_s; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterTimeOffsetMSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterTimeOffsetMSec */ + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterTimeOffsetSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterTimeOffsetSec */ + uint32_t offset_s; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackUniqueIDPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackUniqueID */ + uint8_t id[8]; +} __attribute__((packed)); + +enum IAPPlayStatusChangeNotificationTrackMediaTypePlayMode { + IAPPlayStatusChangeNotificationTrackMediaTypePlayMode_Audio = 0x00, + IAPPlayStatusChangeNotificationTrackMediaTypePlayMode_Video = 0x01, +}; + +struct IAPPlayStatusChangeNotificationTrackPlaybackModePayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackPlaybackMode */ + uint8_t play_mode; /* IAPPlayStatusChangeNotificationTrackMediaTypePlayMode */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackLyricsReadyPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackLyricsReady */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackCapsChangedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackCapsChanged */ + uint32_t caps; /* IAPIPodStateTrackCapBits */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackEngineContentsChangedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackEngineContentsChanged */ + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h new file mode 100644 index 0000000000..66d4e00a79 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h @@ -0,0 +1,15 @@ +#pragma once +#include + +enum IAPPlayStatus { + IAPPlayStatus_Stopped = 0x00, + IAPPlayStatus_Playing = 0x01, + IAPPlayStatus_Paused = 0x02, + IAPPlayStatus_Error = 0xFF, +}; + +struct IAPExtendedRetPlayStatusPayload { + uint32_t track_total_ms; + uint32_t track_pos_ms; + uint8_t state; /* IAPPlayStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h new file mode 100644 index 0000000000..7693e5a002 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.456 5.1.66 Command 0x0048: GetPlaylistInfo */ + +enum IAPPlaylistInfoType { + PlaylistInfo = 0x00, +}; + +struct IAPGetPlaylistInfoPayload { + uint8_t info_type; /* IAPPlaylistInfoType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPRetPlaylistInfoPayload { + uint8_t info_type; /* IAPPlaylistInfoType */ + uint8_t data; /* TODO: add definitions */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h new file mode 100644 index 0000000000..619e5dacef --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.434 5.1.44 Command 0x0030: ReturnRepeat */ + +struct IAPReturnRepeatPayload { + uint8_t mode; /* IAPIPodStateRepeaetSettingState */ +}; + +struct IAPSetRepeatPayload { + uint8_t mode; /* IAPIPodStateRepeatSettingState */ + uint8_t restore_on_exit; /* optional */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h new file mode 100644 index 0000000000..4687fe8f35 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.435 5.1.46 Command 0x0032: SetDisplayImage */ + +struct IAPSetDisplayImageFirstPayload { + uint16_t index; /* = 0x0000 */ + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPSetDisplayImageSubsequenctPayload { + uint16_t index; /* > 0x0000 */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h new file mode 100644 index 0000000000..f94a0e6e1e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.431 5.1.41 Command 0x002D: ReturnShuffle */ + +struct IAPReturnShufflePayload { + uint8_t mode; /* IAPIPodStateShuffleSettingState */ +}; + +struct IAPSetShufflePayload { + uint8_t mode; /* IAPIPodStateShuffleSettingState */ + uint8_t restore_on_exit; /* optional */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h new file mode 100644 index 0000000000..2eab33ed67 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h @@ -0,0 +1,59 @@ +#pragma once +#include + +/* [1] P.446 5.1.57 Command 0x003E: GetUIDTrackInfo */ + +enum IAPTrakcInfoTypeBits { + /* mask[0] */ + IAPTrakcInfoTypeBits_Caps = 1 << 0, + IAPTrakcInfoTypeBits_TrackName = 1 << 1, + IAPTrakcInfoTypeBits_ArtistName = 1 << 2, + IAPTrakcInfoTypeBits_AlbumName = 1 << 3, + IAPTrakcInfoTypeBits_GenreName = 1 << 4, + IAPTrakcInfoTypeBits_ComposerName = 1 << 5, + IAPTrakcInfoTypeBits_TotalTrackTimeDuration = 1 << 6, + IAPTrakcInfoTypeBits_UniqueTrackID = 1 << 7, + /* mask[1] */ + IAPTrakcInfoTypeBits_ChapterCount = 1 << 0, + IAPTrakcInfoTypeBits_ChapterTimes = 1 << 1, + IAPTrakcInfoTypeBits_ChapterNames = 1 << 2, + IAPTrakcInfoTypeBits_Lyrics = 1 << 3, + IAPTrakcInfoTypeBits_Description = 1 << 4, + IAPTrakcInfoTypeBits_AlbumTrackIndex = 1 << 5, + IAPTrakcInfoTypeBits_DiscSetAlbumIndex = 1 << 6, + IAPTrakcInfoTypeBits_PlayCount = 1 << 7, + /* mask[2] */ + IAPTrakcInfoTypeBits_SkipCount = 1 << 0, + IAPTrakcInfoTypeBits_PodcastReleaseDate = 1 << 1, + IAPTrakcInfoTypeBits_LastPlayedDate = 1 << 2, + IAPTrakcInfoTypeBits_Year = 1 << 3, + IAPTrakcInfoTypeBits_StarRating = 1 << 4, + IAPTrakcInfoTypeBits_SeriesName = 1 << 5, + IAPTrakcInfoTypeBits_SeasonNumber = 1 << 6, + IAPTrakcInfoTypeBits_TrackVolumeAdjust = 1 << 7, + /* mask[3] */ + IAPTrakcInfoTypeBits_TrackEQPreset = 1 << 0, + IAPTrakcInfoTypeBits_TrackDataRate = 1 << 1, + IAPTrakcInfoTypeBits_BookmarkOffset = 1 << 2, + IAPTrakcInfoTypeBits_StartStopTimeOffset = 1 << 3, + +}; + +struct IAPGetUIDTrackInfoPayload { + uint8_t uid[8]; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +struct IAPGetDBTrackInfoPayload { + uint8_t database_index; + uint8_t track_count; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +struct IAPRetDBTrackInfoPayload { + uint8_t playing_index; + uint8_t track_count; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +/* TODO: define IAPRet{UID,DB,PB}TrackInfoPayload, but who really needs this? */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h new file mode 100644 index 0000000000..4947e70f56 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h @@ -0,0 +1,15 @@ +#pragma once +#include + +/* [1] P.457 5.1.68 Command 0x004A: PrepareUIDList */ + +struct IAPPrepareUIDListPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t uids[][8]; +} __attribute__((packed)); + +struct IAPPlayPreparedUIDListPayload { + uint8_t reserved; + uint8_t uid[8]; +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general.h b/firmware/usbstack/iap/libiap/spec/lingoes/general.h new file mode 100644 index 0000000000..5bcae5276d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general.h @@ -0,0 +1,112 @@ +#pragma once +#include + +/* [1] P.119 Table 3-1 General lingo commands */ +enum IAPGeneralCommandID { + IAPGeneralCommandID_RequestIdentify = 0x00, /* 0.00, from dev, no payload */ + IAPGeneralCommandID_Identify = 0x01, /* 0.00, from acc, identify.h, deprecated */ + IAPGeneralCommandID_IPodAck = 0x02, /* 1.00, from dev, ipod-ack.h */ + IAPGeneralCommandID_RequestExtendedInterfaceMode = 0x03, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnExtendedInterfaceMode = 0x04, /* 1.00, from dev, extended-interface-mode.h */ + IAPGeneralCommandID_EnterExtendedInterfaceMode = 0x05, /* 1.00, from acc, no payload, deprecated */ + IAPGeneralCommandID_ExitExtendedInterfaceMode = 0x06, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_RequestIPodName = 0x07, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodName = 0x08, /* 1.00, from dev, ipod-name.h */ + IAPGeneralCommandID_RequestIPodSoftwareVersion = 0x09, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodSoftwareVersion = 0x0A, /* 1.00, from dev, ipod-software-version.h */ + IAPGeneralCommandID_RequestIPodSerialNum = 0x0B, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodSerialNum = 0x0C, /* 1.00, from dev, ipod-serial-num.h */ + IAPGeneralCommandID_RequestIPodModelNum = 0x0D, /* 1.00, from acc, deprecated */ + IAPGeneralCommandID_ReturnIPodModelNum = 0x0E, /* 1.00, from dev, deprecated */ + IAPGeneralCommandID_RequestLingoProtocolVersion = 0x0F, /* 1.00, from acc, lingo-protocol-version.h */ + IAPGeneralCommandID_ReturnLingoProtocolVersion = 0x10, /* 1.00, from dev, lingo-protocol-version.h */ + IAPGeneralCommandID_RequestTransportMaxPayloadSize = 0x11, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_ReturnTransportMaxPayloadSize = 0x12, /* 1.09, from dev, transport-max-payload-size.h */ + IAPGeneralCommandID_IdentifyDeviceLingoes = 0x13, /* 1.01, from acc, identify-device-lingoes.h */ + IAPGeneralCommandID_GetAccessoryAuthenticationInfo = 0x14, /* 1.01, from dev, no payload */ + IAPGeneralCommandID_RetAccessoryAuthenticationInfo = 0x15, /* 1.01, from acc, acc-auth-info.h */ + IAPGeneralCommandID_AckAccessoryAuthenticationInfo = 0x16, /* 1.01, from dev, acc-auth-info.h */ + IAPGeneralCommandID_GetAccessoryAuthenticationSignature = 0x17, /* 1.01, from dev, acc-auth-sig.h */ + IAPGeneralCommandID_RetAccessoryAuthenticationSignature = 0x18, /* 1.01, from acc, acc-auth-sig.h */ + IAPGeneralCommandID_AckAccessoryAuthenticationStatus = 0x19, /* 1.01, from dev, acc-auth-sig.h*/ + IAPGeneralCommandID_GetIPodAuthenticationInfo = 0x1A, /* 1.01, from acc, no payload */ + IAPGeneralCommandID_RetIPodAuthenticationInfo = 0x1B, /* 1.01, from dev, ipod-auth-info.h */ + IAPGeneralCommandID_AckIPodAuthenticationInfo = 0x1C, /* 1.01, from acc, ipod-auth-info.h */ + IAPGeneralCommandID_GetIPodAuthenticationSignature = 0x1D, /* 1.01, from acc, ipod-auth-sig.h */ + IAPGeneralCommandID_RetIPodAuthenticationSignature = 0x1E, /* 1.01, from dev, ipod-auth-sig.h */ + IAPGeneralCommandID_AckIPodAuthenticationStatus = 0x1F, /* 1.01, from acc, ipod-auth-sig.h */ + IAPGeneralCommandID_NotifyIPodStateChange = 0x23, /* 1.02, from dev, notify-ipod-state-change.h */ + IAPGeneralCommandID_GetIPodOptions = 0x24, /* 1.05, from acc, no payload */ + IAPGeneralCommandID_RetIPodOptions = 0x25, /* 1.05, from dev, ipod-options.h */ + IAPGeneralCommandID_GetAccessoryInfo = 0x27, /* 1.04, from dev, acc-info.h */ + IAPGeneralCommandID_RetAccessoryInfo = 0x28, /* 1.04, from acc, acc-info.h */ + IAPGeneralCommandID_GetIPodPreferences = 0x29, /* 1.05, from acc, ipod-preferences.h */ + IAPGeneralCommandID_RetIPodPreferences = 0x2A, /* 1.05, from dev, ipod-preferences.h */ + IAPGeneralCommandID_SetIPodPreferences = 0x2B, /* 1.05, from acc, ipod-preferences.h */ + IAPGeneralCommandID_GetUIMode = 0x35, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetUIMode = 0x36, /* 1.09, from dev, ui-mode.h */ + IAPGeneralCommandID_SetUIMode = 0x37, /* 1.09, from acc, ui-mode.h */ + IAPGeneralCommandID_StartIDPS = 0x38, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_SetFIDTokenValues = 0x39, /* 1.09, from acc, fid-token-values.h */ + IAPGeneralCommandID_AckFIDTokenValues = 0x3A, /* 1.09, from dev, fid-token-values.h */ + IAPGeneralCommandID_EndIDPS = 0x3B, /* 1.09, from acc, end-idps.h */ + IAPGeneralCommandID_IDPSStatus = 0x3C, /* 1.09, from dev, end-idps.h */ + IAPGeneralCommandID_OpenDataSessionForProtocol = 0x3F, /* 1.09, from dev, data-session.h */ + IAPGeneralCommandID_CloseDataSession = 0x40, /* 1.09, from dev, data-session.h */ + IAPGeneralCommandID_AccessoryAck = 0x41, /* 1.09, from acc, acc-ack.h */ + IAPGeneralCommandID_AccessoryDataTransfer = 0x42, /* 1.09, from acc, data-transfer.h */ + IAPGeneralCommandID_IPodDataTransfer = 0x43, /* 1.09, from dev, data-transfer.h */ + IAPGeneralCommandID_SetAccessoryStatusNotification = 0x46, /* 1.09, from dev, acc-status-notification.h */ + IAPGeneralCommandID_RetAccessoryStatusNotification = 0x47, /* 1.09, from acc, acc-status-notification.h */ + IAPGeneralCommandID_AccessoryStatusNotification = 0x48, /* 1.09, from acc, acc-status-notification.h */ + IAPGeneralCommandID_SetEventNotification = 0x49, /* 1.09, from acc, event-notification.h */ + IAPGeneralCommandID_IPodNotification = 0x4A, /* 1.09, from dev, ipod-notifiction.h */ + IAPGeneralCommandID_GetIPodOptionsForLingo = 0x4B, /* 1.09, from acc, ipod-options-for-lingo.h */ + IAPGeneralCommandID_RetIPodOptionsForLingo = 0x4C, /* 1.09, from dev, ipod-options-for-lingo.h */ + IAPGeneralCommandID_GetEventNotification = 0x4D, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetEventNotification = 0x4E, /* 1.09, from dev, event-notification.h */ + IAPGeneralCommandID_GetSupportedEventNotification = 0x4F, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_CancelCommand = 0x50, /* 1.09, from acc, cancel-command.h */ + IAPGeneralCommandID_RetSupportedEventNotification = 0x51, /* 1.09, from dev, event-notification.h */ + IAPGeneralCommandID_SetAvailableCurrent = 0x54, /* 1.09, from acc, set-available-current.h */ + IAPGeneralCommandID_SetInternalBatteryChargingState = 0x56, /* 1.09, from acc, set-internal-battery-charging-state.h */ + IAPGeneralCommandID_RequestApplicationLaunch = 0x64, /* 1.09, from acc, request-application-launch.h */ + IAPGeneralCommandID_GetNowPlayingApplicationBundleName = 0x65, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetNowPlayingApplicationBundleName = 0x66, /* 1.09, from dev, now-playing-app-bundle-name.h */ + IAPGeneralCommandID_GetLocalizationInfo = 0x67, /* 1.09, from acc, localization-info.h */ + IAPGeneralCommandID_RetLocalizationInfo = 0x68, /* 1.09, from dev, localization-info.h */ + IAPGeneralCommandID_RequestWiFiConnectionInfo = 0x69, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_WiFiConnectionInfo = 0x6A, /* 1.09, from dev, wifi-connection-info.h */ +}; + +#include "general/acc-ack.h" +#include "general/acc-auth-info.h" +#include "general/acc-auth-sig.h" +#include "general/acc-info.h" +#include "general/acc-status-notification.h" +#include "general/cancel-command.h" +#include "general/data-session.h" +#include "general/data-transfer.h" +#include "general/end-idps.h" +#include "general/event-notification.h" +#include "general/extended-interface-mode.h" +#include "general/fid-token-values.h" +#include "general/identify-device-lingoes.h" +#include "general/identify.h" +#include "general/ipod-ack.h" +#include "general/ipod-auth-info.h" +#include "general/ipod-auth-sig.h" +#include "general/ipod-name.h" +#include "general/ipod-notification.h" +#include "general/ipod-options-for-lingo.h" +#include "general/ipod-options.h" +#include "general/ipod-preferences.h" +#include "general/ipod-software-version.h" +#include "general/lingo-protocol-version.h" +#include "general/localization-info.h" +#include "general/now-playing-app-bundle-name.h" +#include "general/request-application-launch.h" +#include "general/set-available-current.h" +#include "general/transport-max-payload-size.h" +#include "general/ui-mode.h" +#include "general/wifi-connection-info.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h new file mode 100644 index 0000000000..3d9a4d91ef --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPAccAckPayload { + uint8_t status; /* = IAPAckStatus_{Success,EBadParameter} */ + uint8_t id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h new file mode 100644 index 0000000000..2dd764e93e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h @@ -0,0 +1,27 @@ +#pragma once +#include + +struct IAPRetAccAuthInfoPayload { + uint8_t protocol_major; + uint8_t protocol_minor; +} __attribute__((packed)); + +struct IAPRetAccAuthInfoPayload2p0 { + uint8_t protocol_major; /* = 0x02 */ + uint8_t protocol_minor; /* = 0x00 */ + uint8_t cert_current_section_index; + uint8_t cert_max_section_index; + uint8_t cert_data[]; +} __attribute__((packed)); + +enum IAPAckAccAuthInfoStatus { + IAPAckAccAuthInfoStatus_Supported = 0x00, + IAPAckAccAuthInfoStatus_CertTooLong = 0x04, + IAPAckAccAuthInfoStatus_Unsupported = 0x08, + IAPAckAccAuthInfoStatus_InvalidCert = 0x0A, + IAPAckAccAuthInfoStatus_InvalidCertPerm = 0x0B, +}; + +struct IAPAckAccAuthInfoPayload { + uint8_t status; /* IAPAckAccAuthInfoStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h new file mode 100644 index 0000000000..9fd22d2598 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h @@ -0,0 +1,22 @@ +#pragma once +#include + +struct IAPGetAccAuthSigPayload1p0 { + uint8_t challenge[16]; + uint8_t retry; +} __attribute__((packed)); + +struct IAPGetAccAuthSigPayload2p0 { + uint8_t challenge[20]; + uint8_t retry; +} __attribute__((packed)); + +/* +struct IAPRetAccAuthSigPayload { + uint8_t sig[]; +} __attribute__((packed)); +*/ + +struct IAPAckAccAuthSigPayload { + uint8_t status; /* IAPAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h new file mode 100644 index 0000000000..6f91bee72e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h @@ -0,0 +1,83 @@ +#pragma once +#include + +enum IAPAccInfoType { + IAPAccInfoType_AccInfoCaps = 0x00, /* G1 -> R2 */ + IAPAccInfoType_AccName = 0x01, /* G1 -> R1 */ + IAPAccInfoType_MinDeviceFirmwareVersion = 0x02, /* G2 -> R3 */ + IAPAccInfoType_MinLingoVersion = 0x03, /* G3 -> R4 */ + IAPAccInfoType_FirmwareVersion = 0x04, /* G1 -> R5 */ + IAPAccInfoType_HardwareVersion = 0x05, /* G1 -> R5 */ + IAPAccInfoType_Manufacture = 0x06, /* G1 -> R2 */ + IAPAccInfoType_ModelNumber = 0x07, /* G1 -> R2 */ + IAPAccInfoType_SerialNumber = 0x08, /* G1 -> R2 */ + IAPAccInfoType_MaxPayloadSize = 0x09, /* G1 -> R6 */ + IAPAccInfoType_SupportedStatusTypes = 0x0B, /* G1 -> R7 */ +}; + +/* G1 */ +struct IAPGetAccInfoPayload { + uint8_t type; /* IAPAccInfoType */ +} __attribute__((packed)); + +/* G2 */ +struct IAPGetAccInfoMinDeviceFirmwareVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinDeviceFirmwareVersion */ + uint32_t model_id; +} __attribute__((packed)); + +/* G3 */ +struct IAPGetAccInfoMinLingoVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinLingoVersion */ + uint8_t lingo_id; +}; + +/* R1 */ +struct IAPRetAccInfoPayload { + uint8_t type; /* IAPAccInfoType */ + char value[]; +}; + +/* R2 */ +struct IAPRetAccInfoCapsPayload { + uint8_t type; /* = IAPAccInfoType_AccInfoCaps */ + uint32_t caps; +} __attribute__((packed)); + +/* R3 */ +struct IAPRetAccInfoMinDeviceFirmwareVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinDeviceFirmwareVersion */ +}; + +/* R4 */ +struct IAPRetAccInfoMinLingoVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinLingoVersion */ + uint8_t lingo_id; + uint8_t major; + uint8_t minor; +}; + +/* R5 */ +struct IAPRetAccInfoFirmHardVersionPayload { + uint8_t type; /* = IAPAccInfoType_{Firmware,Hardware}Version */ + uint8_t major; + uint8_t minor; + uint8_t revision; +} __attribute__((packed)); + +/* R6 */ +struct IAPRetAccInfoMaxPayloadSizePayload { + uint8_t type; /* = IAPAccInfoType_MaxPayloadSize */ + uint16_t max_payload_size; +} __attribute__((packed)); + +enum IAPAccInfoStatusTypes { + IAPAccInfoStatusTypes_Bluetooth = 0b0010, + IAPAccInfoStatusTypes_FaultCondition = 0b0100, +}; + +/* R7 */ +struct IAPRetAccInfoSupportedStatusTypes { + uint8_t type; /* = IAPAccInfoType_SupportedStatusTypes */ + uint32_t status_types; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h new file mode 100644 index 0000000000..501d6d2ce6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h @@ -0,0 +1,18 @@ +#pragma once +#include + +struct IAPSetAccStatusNotificationPayload { + uint32_t mask; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); + +struct IAPRetAccStatusNotificationPayload { + uint32_t mask; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); + +struct IAPAccStatusNotificationPayload { + uint32_t type; /* IAPAccInfoStatusTypes */ + uint8_t params[]; +} __attribute__((packed)); + +/* [1] P.181 Table 3-108 AccessoryStatusNotification parameters */ +/* TODO: add definitions */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h new file mode 100644 index 0000000000..af10443935 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPCancelCommandPayload { + uint8_t lingo_id; + uint16_t command_id; + uint16_t transaction_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h new file mode 100644 index 0000000000..1367987cd6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPOpenDataSessionForProtocolPayload { + uint16_t session_id; + uint8_t protocol_index; +} __attribute__((packed)); + +struct IAPCloseDataSessionPayload { + uint16_t session_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h new file mode 100644 index 0000000000..73cfa522a1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPAccDataTransferPayload { + uint16_t session_id; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPIPodDataTransferPayload { + uint16_t session_id; + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h new file mode 100644 index 0000000000..c9c7fe41e1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h @@ -0,0 +1,33 @@ +#pragma once +#include + +enum IAPEndIDPSStatus { + IAPEndIDPSStatus_Success = 0x00, + IAPEndIDPSStatus_Reset = 0x01, + IAPEndIDPSStatus_Abort = 0x02, + IAPEndIDPSStatus_AnotherTransport = 0x03, +}; + +struct IAPEndIDPSPayload { + uint8_t status; /* IAPEndIDPSStatus */ +}; + +enum IAPIDPSStatus { + /* IAPEndIDPSStatus_Success */ + IAPIDPSStatus_Success = 0x00, + IAPIDPSStatus_RequiredTokenRejected = 0x01, + IAPIDPSStatus_RequiredTokenMissing = 0x02, + IAPIDPSStatus_RequiredTokenRejectedMissing = 0x03, + /* IAPEndIDPSStatus_Reset */ + IAPIDPSStatus_NotTimedOut = 0x04, + IAPIDPSStatus_TimedOut = 0x05, + /* IAPEndIDPSStatus_Abort */ + IAPIDPSStatus_Aborted = 0x06, + /* IAPEndIDPSStatus_AnotherTransport */ + IAPIDPSStatus_Failed = 0x07, + +}; + +struct IAPIDPSStatusPayload { + uint8_t status; /* IAPIDPSStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h new file mode 100644 index 0000000000..d6ac7f1505 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h @@ -0,0 +1,31 @@ +#pragma once +#include + +/* [1] P.184 Table 3-112 Notification bitmask bits */ + +enum IAPEventNotificationEvents { + IAPSetEventNotificationEvents_FlowControl = 1 << 2, + IAPSetEventNotificationEvents_RadioTagging = 1 << 3, + IAPSetEventNotificationEvents_Camera = 1 << 4, + IAPSetEventNotificationEvents_ChargingInfo = 1 << 5, + IAPSetEventNotificationEvents_DatabaseChanged = 1 << 9, + IAPSetEventNotificationEvents_AppBundleName = 1 << 10, + IAPSetEventNotificationEvents_SessionSpaceAvail = 1 << 11, + IAPSetEventNotificationEvents_CommandComplete = 1 << 13, + IAPSetEventNotificationEvents_IPodOutMode = 1 << 15, + IAPSetEventNotificationEvents_BluetoothConnection = 1 << 17, + IAPSetEventNotificationEvents_AppDisplayName = 1 << 19, + IAPSetEventNotificationEvents_AssistiveTouch = 1 << 20, +}; + +struct IAPSetEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); + +struct IAPRetEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); + +struct IAPRetSupportedEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h new file mode 100644 index 0000000000..5a19804e26 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.127 3.3.4 Command 0x04: ReturnExtendedInterfaceMode */ + +struct IAPReturnExtendedInterfaceModePayload { + uint8_t is_ext_mode; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h new file mode 100644 index 0000000000..01bbf6ffbd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h @@ -0,0 +1,58 @@ +#pragma once +#include + +/* [1] P.161 Table 3-67 FIDTokenValues tokens */ +/* = 0x{type}{subtype} */ +enum IAPFIDTokenTypes { + IAPFIDTokenTypes_Identify = 0x0000, + IAPFIDTokenTypes_AccCaps = 0x0001, + IAPFIDTokenTypes_AccInfo = 0x0002, + IAPFIDTokenTypes_IPodPreference = 0x0003, + IAPFIDTokenTypes_EAProtocol = 0x0004, + IAPFIDTokenTypes_BundleSeedIDPref = 0x0005, + IAPFIDTokenTypes_ScreenInfo = 0x0007, + IAPFIDTokenTypes_EAProtocolMetadata = 0x0008, + IAPFIDTokenTypes_AccDigitalAudioSampleRates = 0x000E, + IAPFIDTokenTypes_AccDigitalAudioVideoDelay = 0x000F, + IAPFIDTokenTypes_MicrophoneCaps = 0x0100, +}; + +/* [1] P.161 Table 3-66 FIDTokenValues field format */ +struct IAPFIDTokenValuesToken { + uint8_t length; + uint8_t type; + uint8_t subtype; + uint8_t data[]; +} __attribute__((packed)); + +/* [1] P.170 Table 3-86 Acknowledgment status codes */ +enum IAPFIDTokenValuesAckStatus { + IAPFIDTokenValuesIdentifyAckStatus_Accepted = 0x00, + IAPFIDTokenValuesIdentifyAckStatus_RequiredFailed = 0x01, + IAPFIDTokenValuesIdentifyAckStatus_OptionalFailed = 0x02, + IAPFIDTokenValuesIdentifyAckStatus_NotSupported = 0x03, + IAPFIDTokenValuesIdentifyAckStatus_LingoBusy = 0x04, + IAPFIDTokenValuesIdentifyAckStatus_MaxConnections = 0x05, +}; + +#include "fid-token-values/acc-caps.h" +#include "fid-token-values/acc-digital-audio-sample-rates.h" +#include "fid-token-values/acc-digital-audio-video-delay.h" +#include "fid-token-values/acc-info.h" +#include "fid-token-values/bundle-seed-id-pref.h" +#include "fid-token-values/ea-protocol-metadata.h" +#include "fid-token-values/ea-protocol.h" +#include "fid-token-values/identify.h" +#include "fid-token-values/ipod-preference.h" +#include "fid-token-values/microphone-caps.h" +#include "fid-token-values/screen-info.h" + +struct IAPSetFIDTokenValuesPayload { + uint8_t num_token_values; + uint8_t data[]; /* packed token values */ +}; + +struct IAPAckFIDTokenValuesPayload { + uint8_t num_token_value_acks; + uint8_t data[]; /* packed token value acks */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h new file mode 100644 index 0000000000..3a86700594 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h @@ -0,0 +1,31 @@ +#pragma once +#include + +/* [1] P.164 Table 3-71 Accessory capabilities bit values */ +enum IAPFIDTokenValuesAccCapsBits { + IAPFIDTokenValuesAccCapsBits_LineOut = 1 << 0, + IAPFIDTokenValuesAccCapsBits_LineIn = 1 << 1, + IAPFIDTokenValuesAccCapsBits_VideoOut = 1 << 2, + IAPFIDTokenValuesAccCapsBits_USBAudioOut = 1 << 4, + IAPFIDTokenValuesAccCapsBits_AppsCommunication = 1 << 9, + IAPFIDTokenValuesAccCapsBits_CheckVolume = 1 << 11, + IAPFIDTokenValuesAccCapsBits_VoiceOver = 1 << 17, + IAPFIDTokenValuesAccCapsBits_AsyncPlaybackChange = 1 << 18, + IAPFIDTokenValuesAccCapsBits_MixedResponse = 1 << 19, + IAPFIDTokenValuesAccCapsBits_AudioRoutingSwitch = 1 << 21, + IAPFIDTokenValuesAccCapsBits_AssistiveTouch = 1 << 23, +}; + +struct IAPFIDTokenValuesAccCapsToken { + uint8_t length; /* = 0x0A */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x01 */ + uint64_t caps_bits; /* IAPFIDTokenValuesAccCapsBits */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccCapsAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x01 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h new file mode 100644 index 0000000000..e9ee239aaa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0E */ + uint32_t sample_rates[]; +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccDigitalAudioSampleRatesAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0E */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h new file mode 100644 index 0000000000..b658d55c5f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken { + uint8_t length; /* = 0x06 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0F */ + uint32_t delay; +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccDigitalAudioVideoDelayAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0F */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h new file mode 100644 index 0000000000..94926ae7d9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h @@ -0,0 +1,33 @@ +#pragma once +#include + +/* [1] P.165 Table 3-73 Accessory Info Type values */ +enum IAPFIDTokenValuesAccInfoTypes { + IAPFIDTokenValuesAccInfoTypes_AccName = 0x01, + IAPFIDTokenValuesAccInfoTypes_FirmwareVersion = 0x04, + IAPFIDTokenValuesAccInfoTypes_HardwareVersion = 0x05, + IAPFIDTokenValuesAccInfoTypes_Manufacture = 0x06, + IAPFIDTokenValuesAccInfoTypes_ModelNumber = 0x07, + IAPFIDTokenValuesAccInfoTypes_SerialNumber = 0x08, + IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize = 0x09, + IAPFIDTokenValuesAccInfoTypes_AccStatus = 0x0B, + IAPFIDTokenValuesAccInfoTypes_RFCerts = 0x0C, +}; + +struct IAPFIDTokenValuesAccInfoToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x02 */ + uint8_t info_type; /* IAPFIDTokenValuesAccInfoTypes */ + uint8_t info[]; +} __attribute__((packed)); + +/* TODO: add info definitions */ + +struct IAPFIDTokenValuesAccInfoAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x02 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t info_type; /* IAPFIDTokenValuesAccInfoTypes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h new file mode 100644 index 0000000000..144ebc3ac8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesBundleSeedIDPrefToken { + uint8_t length; /* = 0x0D */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x05 */ + char bundle_seed_id_string[11]; +} __attribute__((packed)); + +struct IAPFIDTokenValuesBundleSeedIDPrefAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x05 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h new file mode 100644 index 0000000000..7ec88e97b7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h @@ -0,0 +1,23 @@ +#pragma once +#include + +enum IAPFIDTokenValuesEAProtocolMetadataTypes { + IAPFIDTokenValuesEAProtocolMetadataTypes_DontFindApp = 0x00, + IAPFIDTokenValuesEAProtocolMetadataTypes_FindApp = 0x01, + IAPFIDTokenValuesEAProtocolMetadataTypes_DontFindButDisplayMessage = 0x03, +}; + +struct IAPFIDTokenValuesEAProtocolMetadataToken { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x08 */ + uint8_t protocol_index; + uint8_t metadata_type; /* IAPFIDTokenValuesEAProtocolMetadataTypes */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesEAProtocolMetadataAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x08 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h new file mode 100644 index 0000000000..20558ea7d3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h @@ -0,0 +1,20 @@ +#pragma once +#include + +/* [1] P.167 Table 3-76 EAProtocolToken format */ +struct IAPFIDTokenValuesEAProtocolToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x04 */ + uint8_t protocol_index; + char protocol_string[]; +} __attribute__((packed)); + +/* [1] P.171 Table 3-90 Acknowledgment format for EAProtocolToken */ +struct IAPFIDTokenValuesEAProtocolAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x04 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t protocol_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h new file mode 100644 index 0000000000..8779774b28 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h @@ -0,0 +1,28 @@ +#pragma once +#include + +struct IAPFIDTokenValuesIdentifyTokenHead { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x00 */ + uint8_t num_lingoes; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyTokenTail { + uint32_t device_option; /* = 0b10 */ + uint32_t device_id; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyToken { + struct IAPFIDTokenValuesIdentifyTokenHead head; + /*uint8_t lingoes[num_lingoes]; */ + struct IAPFIDTokenValuesIdentifyTokenHead tail; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x00 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t lingo_ids[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h new file mode 100644 index 0000000000..722cc1db11 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h @@ -0,0 +1,21 @@ +#pragma once +#include + +/* [1] P.166 Table 3-75 iPodPreferenceToken format */ +struct IAPFIDTokenValuesIPodPreferenceToken { + uint8_t length; /* = 0x05 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x03 */ + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; + uint8_t restore_on_exit; /* = 0x01 */ +} __attribute__((packed)); + +/* [1] P.171 Table 3-89 Acknowledgment format for ipodPreferenceToken */ +struct IAPFIDTokenValuesIPodPreferenceAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x03 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t class_id; /* IAPIPodPereferenceClassID */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h new file mode 100644 index 0000000000..947f7852b6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h @@ -0,0 +1,24 @@ +#pragma once +#include + +enum IAPFIDTokenValuesMicrophoneCaps { + IAPFIDTokenValuesMicrophoneCaps_StereoInput = 1 << 0, + IAPFIDTokenValuesMicrophoneCaps_StereoMonoInputSwitch = 1 << 1, + IAPFIDTokenValuesMicrophoneCaps_VariableRecordLevel = 1 << 2, + IAPFIDTokenValuesMicrophoneCaps_RecordLevelLimit = 1 << 3, + IAPFIDTokenValuesMicrophoneCaps_DuplexAudio = 1 << 4, +}; + +struct IAPFIDTokenValuesMicrophoneCapsToken { + uint8_t length; /* = 0x06 */ + uint8_t type; /* = 0x01 */ + uint8_t subtype; /* = 0x00 */ + uint32_t caps_bits; /* IAPFIDTokenValuesMicrophoneCaps */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesMicrophoneCapsAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x01 */ + uint8_t subtype; /* = 0x00 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h new file mode 100644 index 0000000000..a05c8b0faf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h @@ -0,0 +1,27 @@ +#pragma once +#include + +enum IAPFIDTokenValuesScreenInfoFeatureBits { + IAPFIDTokenValuesScreenInfoFeatureBits_ColorDisplay = 1 << 0, +}; + +struct IAPFIDTokenValuesScreenInfoToken { + uint8_t length; /* = 0x10 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x07 */ + uint16_t total_screen_width_inches; + uint16_t total_screen_height_inches; + uint16_t total_screen_width_pixels; + uint16_t total_screen_height_pixels; + uint16_t ipod_out_screen_width_pixels; + uint16_t ipod_out_screen_height_pixels; + uint8_t screen_feature_mask; /* IAPFIDTokenValuesScreenInfoFeatureBits */ + uint8_t screen_gamma_value; +} __attribute__((packed)); + +struct IAPFIDTokenValuesScreenInfoAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x07 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h new file mode 100644 index 0000000000..d2c36955e3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h @@ -0,0 +1,30 @@ +#pragma once +#include + +/* [1] P.134 Table 3-24 Device Lingoes Spoken bits */ +enum IAPIdentifyDeviceLingoesLingoBits { + IAPIdentifyDeviceLingoesLingoBits_General = 1 << 0, + IAPIdentifyDeviceLingoesLingoBits_SimpleRemote = 1 << 2, + IAPIdentifyDeviceLingoesLingoBits_DisplayRemote = 1 << 3, + IAPIdentifyDeviceLingoesLingoBits_ExtendedInterface = 1 << 4, + IAPIdentifyDeviceLingoesLingoBits_AccessoryPower = 1 << 5, + IAPIdentifyDeviceLingoesLingoBits_USBHostMode = 1 << 6, + IAPIdentifyDeviceLingoesLingoBits_RFTuner = 1 << 7, + IAPIdentifyDeviceLingoesLingoBits_AccessoryEqualizer = 1 << 8, + IAPIdentifyDeviceLingoesLingoBits_Sports = 1 << 9, + IAPIdentifyDeviceLingoesLingoBits_DigitalAudio = 1 << 10, + IAPIdentifyDeviceLingoesLingoBits_Storage = 1 << 12, +}; + +/* [1] P.135 Table 3-25 IdentifyDeviceLingoes Options bits */ +enum IAPIdentifyDeviceLingoesOptions { + IAPIdentifyDeviceLingoesOptions_NoAuth = 0b00, + IAPIdentifyDeviceLingoesOptions_DeferAuth = 0b01, + IAPIdentifyDeviceLingoesOptions_ImmediateAuth = 0b10, +}; + +struct IAPIdentifyDeviceLingoesPayload { + uint32_t lingoes_bits; /* IAPIdentifyDeviceLingoesLingoBits */ + uint32_t options; /* IAPIdentifyDeviceLingoesOptions */ + uint32_t device_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h new file mode 100644 index 0000000000..ef469fffa6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h @@ -0,0 +1,15 @@ +#pragma once +#include + +/* [1] P.531 Table C-8 Identify packet */ +struct IAPIdentifyPayload { + uint8_t supported_lingo; +}; + +/* [1] P.531 Table C-9 Identify packet for high-power accessories */ +struct IAPIdentifyHighPowerPayload { + uint8_t supported_lingo; /* = IAPLingoID_AccessoryPower */ + uint8_t reserved; /* = 0x00 */ + uint8_t num_valid_bits; /* = 0x02 */ + uint8_t option_flags; /* TODO: add bitfield definition */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h new file mode 100644 index 0000000000..3dfe657a64 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h @@ -0,0 +1,57 @@ +#pragma once +#include + +/* [1] P.124 3.3.2 Command 0x02: iPodAck */ + +enum IAPAckStatus { + IAPAckStatus_Success = 0x00, + IAPAckStatus_EUnknownDatabaseOrSessionID = 0x01, + IAPAckStatus_ECommandFailed = 0x02, + IAPAckStatus_EOutOfResource = 0x03, + IAPAckStatus_EBadParameter = 0x04, + IAPAckStatus_EUnknownID = 0x05, + IAPAckStatus_CommandPending = 0x06, + IAPAckStatus_ENotAuthenticated = 0x07, + IAPAckStatus_EBadAuthVersion = 0x08, + IAPAckStatus_EAccPowerModeRequestFailed = 0x09, + IAPAckStatus_EInvalidCert = 0x0A, + IAPAckStatus_EInvalidCertPerm = 0x0B, + IAPAckStatus_EFileInUse = 0x0C, + IAPAckStatus_EInvalidFileHandle = 0x0D, + IAPAckStatus_EDirectoryNotEmpty = 0x0E, + IAPAckStatus_EOperationTimedOut = 0x0F, + IAPAckStatus_ECommandUnavailable = 0x10, + IAPAckStatus_EBadWire = 0x11, + IAPAckStatus_ESelectionNotGenius = 0x12, + IAPAckStatus_MultiDataRecvSuccess = 0x13, + IAPAckStatus_ELingoBusy = 0x14, + IAPAckStatus_EMaxNumAcc = 0x15, + IAPAckStatus_HIDIndexInUse = 0x16, + IAPAckStatus_EDropped = 0x17, + IAPAckStatus_EInvalidVideoSettings = 0x18, +}; + +struct IAPIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; +} __attribute__((packed)); + +struct IAPIPodAckCommandPendingPayload { + uint8_t status; /* = IAPAckStatus_CommandPending */ + uint8_t id; + uint32_t time_ms; +} __attribute__((packed)); + +/* [1] P.389 Table 4-292 Multisection iPodAck packet */ +struct IAPIPodAckMultiSectPayload { + uint8_t status; /* IAPAckStatus_IAPAckStatus_MultiDataRecvSuccess */ + uint8_t id; + uint16_t sect_cur; +}; + +struct IAPIPodAckEDroppedPayload { + uint8_t status; /* = IAPAckStatus_EDropped */ + uint8_t id; /* = IAPCommandIDGeneral_AccessoryDataTransfer */ + uint16_t session_id; + uint32_t num_dropped_bytes; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h new file mode 100644 index 0000000000..de03274418 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h @@ -0,0 +1,14 @@ +#pragma once +#include + +struct IAPRetIPodAuthInfoPayload2p0 { + uint8_t protocol_major; /* = 0x02 */ + uint8_t protocol_minor; /* = 0x00 */ + uint8_t cert_current_section_index; + uint8_t cert_max_section_index; + uint8_t cert_data[]; +} __attribute__((packed)); + +struct IAPAckIPodAuthInfoPayload { + uint8_t status; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h new file mode 100644 index 0000000000..977b299790 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h @@ -0,0 +1,17 @@ +#pragma once +#include + +struct IAPGetIPodAuthSigPayload2p0 { + uint8_t challenge[20]; + uint8_t retry; +} __attribute__((packed)); + +/* +struct IAPRetIPodAuthSigPayload { + uint8_t sig[]; +} __attribute__((packed)); +*/ + +struct IAPAckIPodAuthSigPayload { + uint8_t status; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h new file mode 100644 index 0000000000..7cedcedeff --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPReturnIPodNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h new file mode 100644 index 0000000000..2674c4867e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h @@ -0,0 +1,120 @@ +#pragma once +#include + +enum IAPIPodNotificationType { + IAPIPodNotificationType_FlowControl = 2, + IAPIPodNotificationType_RadioTagging = 3, + IAPIPodNotificationType_Camera = 4, + IAPIPodNotificationType_ChargingInfo = 5, + IAPIPodNotificationType_DatabaseChanged = 9, + IAPIPodNotificationType_AppBundleName = 10, + IAPIPodNotificationType_SessionSpaceAvail = 11, + IAPIPodNotificationType_CommandComplete = 13, + IAPIPodNotificationType_IPodOutMode = 15, + IAPIPodNotificationType_BluetoothConnection = 17, + IAPIPodNotificationType_AppDisplayName = 19, + IAPIPodNotificationType_AssistiveTouch = 20, +}; + +struct IAPIPodNotificationPayload { + uint8_t type; /* IAPIPodNotificationType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPIPodNotificationFlowControlPayload { + uint8_t type; /* = IAPIPodNotificationType_FlowControl */ + uint32_t wait_time_ms; + uint16_t overflow_transaction_id; +} __attribute__((packed)); + +enum IAPIPodNotificationRadioTaggingStatus { + IAPIPodNotificationRadioTaggingStatus_Success = 0x00, + IAPIPodNotificationRadioTaggingStatus_Failed = 0x01, + IAPIPodNotificationRadioTaggingStatus_InfoAvail = 0x02, + IAPIPodNotificationRadioTaggingStatus_InfoNotAvail = 0x03, +}; + +struct IAPIPodNotificationRadioTaggingPayload { + uint8_t type; /* = IAPIPodNotificationType_RadioTagging */ + uint8_t status; /* IAPIPodNotificationRadioTaggingStatus */ +} __attribute__((packed)); + +enum IAPIPodNotificationCameraStatus { + IAPIPodNotificationCameraStatus_AppOff = 0x00, + IAPIPodNotificationCameraStatus_Preview = 0x03, + IAPIPodNotificationCameraStatus_Recording = 0x04, +}; + +struct IAPIPodNotificationCameraPayload { + uint8_t type; /* = IAPIPodNotificationType_Camera */ + uint8_t status; /* IAPIPodNotificationCameraStatus */ +} __attribute__((packed)); + +enum IAPIPodNotificationChargingInfoType { + IAPIPodNotificationChargingInfoType_AvailCurrent = 0x00, +}; + +struct IAPIPodNotificationChargingInfoPayload { + uint8_t type; /* = IAPIPodNotificationType_ChargingInfo */ + uint8_t info_type; /* IAPIPodNotificationChargingInfoType */ + uint16_t info_value; +} __attribute__((packed)); + +struct IAPIPodNotificationDatabaseChangedPayload { + uint8_t type; /* = IAPIPodNotificationType_DatabaseChanged */ +} __attribute__((packed)); + +struct IAPIPodNotificationAppBundleNamePayload { + uint8_t type; /* = IAPIPodNotificationType_AppBundleName */ + char name[]; +} __attribute__((packed)); + +struct IAPIPodNotificationSessionSpaceAvailPayload { + uint8_t type; /* = IAPIPodNotificationType_SessionSpaceAvail */ + uint16_t session_id; +} __attribute__((packed)); + +enum IAPIPodNotificationCommandCompleteStatus { + IAPIPodNotificationCommandCompleteStatus_Success = 0x00, + IAPIPodNotificationCommandCompleteStatus_Failed = 0x01, + IAPIPodNotificationCommandCompleteStatus_Cancelled = 0x02, +}; + +struct IAPIPodNotificationCommandCompletePayload { + uint8_t type; /* = IAPIPodNotificationType_CommandComplete */ + uint8_t lingo_id; + uint16_t command_id; + uint8_t status; /* IAPIPodNotificationCommandCompleteStatus */ +} __attribute__((packed)); + +struct IAPIPodNotificationIPodOutModePayload { + uint8_t type; /* = IAPIPodNotificationType_IPodOutMode */ + uint8_t is_active; +} __attribute__((packed)); + +enum IAPIPodNotificationBluetoothConnectionStatusBits { + IAPIPodNotificationBluetoothConnectionStatusBits_HFP = 1 << 0, + IAPIPodNotificationBluetoothConnectionStatusBits_PBAP = 1 << 1, + IAPIPodNotificationBluetoothConnectionStatusBits_AVRCP = 1 << 3, + IAPIPodNotificationBluetoothConnectionStatusBits_A2DP = 1 << 4, + IAPIPodNotificationBluetoothConnectionStatusBits_HID = 1 << 5, + IAPIPodNotificationBluetoothConnectionStatusBits_IAP = 1 << 7, + IAPIPodNotificationBluetoothConnectionStatusBits_PAN_NAP = 1 << 8, + IAPIPodNotificationBluetoothConnectionStatusBits_PAN_U = 1 << 12, +}; + +struct IAPIPodNotificationBluetoothConnectionPayload { + uint8_t type; /* = IAPIPodNotificationType_BluetoothConnection */ + uint8_t mac_addr[6]; + uint64_t status; /* IAPIPodNotificationBluetoothConnectionStatusBits */ +} __attribute__((packed)); + +struct IAPIPodNotificationAppDisplayNamePayload { + uint8_t type; /* = IAPIPodNotificationType_AppDisplayName */ + char name[]; +} __attribute__((packed)); + +struct IAPIPodNotificationAssistiveTouchPayload { + uint8_t type; /* = IAPIPodNotificationType_AssistiveTouch */ + uint8_t is_on; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h new file mode 100644 index 0000000000..aeeca1a793 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h @@ -0,0 +1,114 @@ +#pragma once +#include + +struct IAPGetIPodOptionsForLingoPayload { + uint8_t lingo_id; +} __attribute__((packed)); + +/* [1] P.192 Table 3-132 RetiPodOptionsForLingo option bits */ + +enum IAPRetIPodOptionsForLingoGeneralBits { + IAPRetIPodOptionsForLingoGeneralBits_LineOutUsage = 1 << 0, + IAPRetIPodOptionsForLingoGeneralBits_VideOutput = 1 << 1, + IAPRetIPodOptionsForLingoGeneralBits_VideoNTSCFormat = 1 << 2, + IAPRetIPodOptionsForLingoGeneralBits_VideoPALFormat = 1 << 3, + IAPRetIPodOptionsForLingoGeneralBits_VideoCompositeConnection = 1 << 4, + IAPRetIPodOptionsForLingoGeneralBits_VideoSVideoConnection = 1 << 5, + IAPRetIPodOptionsForLingoGeneralBits_VideoComponentConnection = 1 << 6, + IAPRetIPodOptionsForLingoGeneralBits_VideoClosedCaptioning = 1 << 7, + IAPRetIPodOptionsForLingoGeneralBits_VideoAspectRatio4x3 = 1 << 8, + IAPRetIPodOptionsForLingoGeneralBits_VideoAspectRatio16x9 = 1 << 9, + IAPRetIPodOptionsForLingoGeneralBits_VideoSubtitle = 1 << 10, + IAPRetIPodOptionsForLingoGeneralBits_VideoAltAudioChannel = 1 << 11, + IAPRetIPodOptionsForLingoGeneralBits_AppCommnunication = 1 << 13, + IAPRetIPodOptionsForLingoGeneralBits_DeviceNotification = 1 << 14, + IAPRetIPodOptionsForLingoGeneralBits_PauseOnRemovalControl = 1 << 19, + IAPRetIPodOptionsForLingoGeneralBits_RestrictedIDPS = 1 << 23, + IAPRetIPodOptionsForLingoGeneralBits_AppLaunchRequest = 1 << 24, + IAPRetIPodOptionsForLingoGeneralBits_AudioRouteSwitch = 1 << 26, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeHardwareInvoke = 1 << 27, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeAudioOutput = 1 << 28, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeAudioInput = 1 << 29, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeNoMaxCurrent = 1 << 30, +}; + +enum IAPRetIPodOptionsForLingoSimpleRemoteBits { + IAPRetIPodOptionsForLingoSimpleRemoteBits_ContextSpecificControls = 1 << 0, + IAPRetIPodOptionsForLingoSimpleRemoteBits_AudioMediaControls = 1 << 1, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VideoMediaControls = 1 << 2, + IAPRetIPodOptionsForLingoSimpleRemoteBits_ImageMediaControls = 1 << 3, + IAPRetIPodOptionsForLingoSimpleRemoteBits_SportsMediaControls = 1 << 4, + IAPRetIPodOptionsForLingoSimpleRemoteBits_CameraMediaControls = 1 << 8, + IAPRetIPodOptionsForLingoSimpleRemoteBits_USBHIDCommands = 1 << 9, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VoiceOverControls = 1 << 10, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VoiceOverPreferences = 1 << 11, + IAPRetIPodOptionsForLingoSimpleRemoteBits_AssistiveTouchCursor = 1 << 12, +}; + +enum IAPRetIPodOptionsForLingoDisplayRemoteBits { + IAPRetIPodOptionsForLingoDisplayRemoteBits_UIVolumeControl = 1 << 0, + IAPRetIPodOptionsForLingoDisplayRemoteBits_AbsoluteVolumeControl = 1 << 1, + IAPRetIPodOptionsForLingoDisplayRemoteBits_GeniusPlaylistCreation = 1 << 2, +}; + +enum IAPRetIPodOptionsForLingoExtendedInterfaceBits { + IAPRetIPodOptionsForLingoExtendedInterfaceBits_VideoBrowsing = 1 << 0, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_InfoCommands = 1 << 1, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_NestedPlaylists = 1 << 2, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_GeniusPlaylistCreationAndRefresh = 1 << 3, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_DisplayImages = 1 << 4, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_CategoryListAccess = 1 << 5, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_PlayControl = 1 << 6, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_UIDBasedCommands = 1 << 7, +}; + +/* +enum IAPRetIPodOptionsForLingoAccessoryPowerBits { +}; +*/ + +enum IAPRetIPodOptionsForLingoUSBHostModeBits { + IAPRetIPodOptionsForLingoUSBHostModeBits_USBHostModeHardwareInvoke = 1 << 0, /* deprecated */ + IAPRetIPodOptionsForLingoUSBHostModeBits_USBHostModeFirmwareInvoke = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoRFTunerBits { + IAPRetIPodOptionsForLingoRFTunerBits_RDSRawMode = 1 << 0, + IAPRetIPodOptionsForLingoRFTunerBits_HDRadioTuning = 1 << 1, + IAPRetIPodOptionsForLingoRFTunerBits_AMRadioTuning = 1 << 2, +}; + +/* +enum IAPRetIPodOptionsForLingoAccessoryEqualizerBits { +}; +*/ + +enum IAPRetIPodOptionsForLingoSportsBits { + IAPRetIPodOptionsForLingoSportsBits_NikePlusEquipment = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoDigitalAudioBits { + IAPRetIPodOptionsForLingoDigitalAudioBits_SetVideoDelayEnabled = 1 << 0, + IAPRetIPodOptionsForLingoDigitalAudioBits_SampleRateCapsFIDToken = 1 << 2, + IAPRetIPodOptionsForLingoDigitalAudioBits_VideoDelayFIDToken = 1 << 3, +}; + +enum IAPRetIPodOptionsForLingoStorageBits { + IAPRetIPodOptionsForLingoStorageBits_ITunesTagging = 1 << 0, + IAPRetIPodOptionsForLingoStorageBits_NikePlusEquipment = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoIPodOutBits { + IAPRetIPodOptionsForLingoIPodOutBits_WheelUIModeAvail = 1 << 0, + IAPRetIPodOptionsForLingoIPodOutBits_PALVideoEnabled = 1 << 2, +}; + +enum IAPRetIPodOptionsForLingoLocationBits { + IAPRetIPodOptionsForLingoLocationBits_AcceptNMEAData = 1 << 0, + IAPRetIPodOptionsForLingoLocationBits_SendLocationAssistanceData = 1 << 1, +}; + +struct IAPRetIPodOptionsForLingoPayload { + uint8_t lingo_id; + uint64_t bits; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h new file mode 100644 index 0000000000..14377c04d9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPRetIPodOptionsState { + IAPRetIPodOptionsState_VideoOutputSupport = 1 << 0, + IAPRetIPodOptionsState_SetiPodPreferencesSupport = 1 << 1, +}; + +struct IAPRetIPodOptionsPayload { + uint64_t state; /* IAPRetIPodOptionsState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h new file mode 100644 index 0000000000..ec6d168440 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h @@ -0,0 +1,95 @@ +#pragma once +#include + +/* [1] P.152 Table 3-57 Apple device preference class and setting IDs */ +enum IAPIPodPereferenceClassID { + IAPIPodPereferenceClassID_VideoOut = 0x00, + IAPIPodPereferenceClassID_Screen = 0x01, + IAPIPodPereferenceClassID_VideoFormat = 0x02, + IAPIPodPereferenceClassID_LineOutUsage = 0x03, + IAPIPodPereferenceClassID_VideoOutConnection = 0x08, + IAPIPodPereferenceClassID_ClosedCaptioning = 0x09, + IAPIPodPereferenceClassID_VideoAspectRatio = 0x0A, + IAPIPodPereferenceClassID_Subtitle = 0x0C, + IAPIPodPereferenceClassID_VideoAltAudioChannel = 0x0D, + IAPIPodPereferenceClassID_PauseOnPowerRemoval = 0x0F, + IAPIPodPereferenceClassID_VoiceOver = 0x14, + IAPIPodPereferenceClassID_AssistiveTouch = 0x16, +}; + +enum IAPIPodPreferenceVideoOutSettingID { + IAPIPodPreferenceVideoOutSettingID_Off = 0x00, + IAPIPodPreferenceVideoOutSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceScreenSettingID { + IAPIPodPreferenceScreenSettingID_FillScreen = 0x00, + IAPIPodPreferenceScreenSettingID_FitToEdge = 0x01, +}; + +enum IAPIPodPreferenceVideoFormatSettingID { + IAPIPodPreferenceVideoFormatSettingID_NTSC = 0x00, + IAPIPodPreferenceVideoFormatSettingID_PAL = 0x01, +}; + +enum IAPIPodPreferenceLineOutUsageSettingID { + IAPIPodPreferenceLineOutUsageSettingID_NotUsed = 0x00, + IAPIPodPreferenceLineOutUsageSettingID_Used = 0x01, +}; + +enum IAPIPodPreferenceVideoOutConnectionSettingID { + IAPIPodPreferenceVideoOutConnectionSettingID_Composite = 0x01, + IAPIPodPreferenceVideoOutConnectionSettingID_SVideo = 0x02, + IAPIPodPreferenceVideoOutConnectionSettingID_Component = 0x03, +}; + +enum IAPIPodPreferenceClosedCaptioningSettingID { + IAPIPodPreferenceClosedCaptioningSettingID_Off = 0x00, + IAPIPodPreferenceClosedCaptioningSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceVideoAspectRatioSettingID { + IAPIPodPreferenceVideoAspectRatioSettingID_4x3 = 0x00, + IAPIPodPreferenceVideoAspectRatioSettingID_16x9 = 0x01, +}; + +enum IAPIPodPreferenceSubtitleSettingID { + IAPIPodPreferenceSubtitleSettingID_Off = 0x00, + IAPIPodPreferenceSubtitleSettingID_On = 0x01, + +}; + +enum IAPIPodPreferenceVideoAltAudioChannelSettingID { + IAPIPodPreferenceVideoAltAudioChannelSettingID_Off = 0x00, + IAPIPodPreferenceVideoAltAudioChannelSettingID_On = 0x01, +}; + +enum IAPIPodPreferencePauseOnPowerRemovalSettingID { + IAPIPodPreferencePauseOnPowerRemovalSettingID_Off = 0x00, + IAPIPodPreferencePauseOnPowerRemovalSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceVoiceOverSettingID { + IAPIPodPreferenceVoiceOverSettingID_Off = 0x00, + IAPIPodPreferenceVoiceOverSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceAssistiveTouchSettingID { + IAPIPodPreferenceAssistiveTouchSettingID_Off = 0x00, + IAPIPodPreferenceAssistiveTouchSettingID_On = 0x01, +}; + +struct IAPGetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ +} __attribute__((packed)); + +struct IAPRetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; +} __attribute__((packed)); + +struct IAPSetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h new file mode 100644 index 0000000000..3285007403 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnIPodSerialNumPayload { + char serial[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h new file mode 100644 index 0000000000..bb036dbbc9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPReturnIPodSoftwareVersionPayload { + uint8_t major; + uint8_t minor; + uint8_t revision; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h new file mode 100644 index 0000000000..44569c7c12 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPRequestLingoProtocolVersionPayload { + uint8_t lingo; +} __attribute__((packed)); + +struct IAPReturnLingoProtocolVersionPayload { + uint8_t lingo; + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h new file mode 100644 index 0000000000..f9a4235834 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h @@ -0,0 +1,16 @@ +#pragma once +#include + +enum IAPLocalicationInfoType { + IAPLocalicationInfoType_Language = 0x00, + IAPLocalicationInfoType_Region = 0x01, +}; + +struct IAPGetLocalizationInfoPayload { + uint8_t type; /* IAPLocalicationInfoType */ +} __attribute__((packed)); + +struct IAPRetLocalizationInfoPayload { + uint8_t type; /* IAPLocalicationInfoType */ + char value[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h new file mode 100644 index 0000000000..22b187832a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h @@ -0,0 +1,13 @@ +#pragma once +#include + +enum IAPNotifyIPodStateChangeState { + IAPNotifyIPodStateChangeState_LostContextHibernate = 0x01, + IAPNotifyIPodStateChangeState_SaveContextHibernate = 0x02, + IAPNotifyIPodStateChangeState_Sleep = 0x03, + IAPNotifyIPodStateChangeState_PowerOn = 0x04, +}; + +struct IAPNotifyIPodStateChangePayload { + uint8_t state; /* IAPNotifyIPodStateChangeState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h new file mode 100644 index 0000000000..b6401d555d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPRetNowPlayingApplicationBundleNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h new file mode 100644 index 0000000000..8a6ecc3cd7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h @@ -0,0 +1,9 @@ +#pragma once +#include + +struct IAPRequestApplicationLaunchPayload { + uint8_t reserved1; + uint8_t flag; + uint8_t reserved2; + char app_id[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h new file mode 100644 index 0000000000..ed9cf86f30 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetAvailableCurrentPayload { + uint16_t current_limit_ma; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h new file mode 100644 index 0000000000..b5804cc4cb --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPInternalBatteryChargingState { + IAPInternalBatteryChargingState_DontCharge = 0x00, + IAPInternalBatteryChargingState_MayCharge = 0x01, +}; + +struct IAPInternalBatteryChargingStatePayload { + uint8_t state; /* IAPInternalBatteryChargingState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h new file mode 100644 index 0000000000..bf83edd35c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnTransportMaxPayloadSizePayload { + uint16_t max_payload_size; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h new file mode 100644 index 0000000000..ed0410458a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h @@ -0,0 +1,13 @@ +#pragma once +#include + +enum IAPUIMode { + IAPUIMode_Standard = 0x00, + IAPUIMode_Extended = 0x01, + IAPUIMode_IPodOutFullscreen = 0x02, + IAPUIMode_IPodOutActionSafe = 0x03, +}; + +struct IAPSetUIModePayload { + uint8_t ui_mode; /* IAPUIMode */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h new file mode 100644 index 0000000000..701bbe5fa0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h @@ -0,0 +1,24 @@ +#pragma once +#include + +enum IAPWiFiConnectionInfoStatus { + IAPWiFiConnectionInfoStatus_Success = 0x00, + IAPWiFiConnectionInfoStatus_Unavailable = 0x01, + IAPWiFiConnectionInfoStatus_Declined = 0x02, + IAPWiFiConnectionInfoStatus_Failed = 0x03, +}; + +enum IAPWiFiConnectionInfoSecurityType { + IAPWiFiConnectionInfoSecurityType_Unsecured = 0x00, + IAPWiFiConnectionInfoSecurityType_WEP = 0x01, + IAPWiFiConnectionInfoSecurityType_WPA = 0x02, + IAPWiFiConnectionInfoSecurityType_WPA2 = 0x03, + IAPWiFiConnectionInfoSecurityType_MixedWPAWPA2 = 0x04, +}; + +struct IAPWiFiConnectionInfoPayload { + uint8_t status; /* IAPWiFiConnectionInfoStatus */ + uint8_t security_type; /* IAPWiFiConnectionInfoSecurityType */ + char ssid[32]; + char passphrase[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h new file mode 100644 index 0000000000..cbf6d0b329 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.366 Table 4-258 iPod Out lingo command summary */ +enum IAPIPodOutCommandID { + IAPIPodOutCommandID_IPodAck = 0x00, /* from dev, general/ipod-ack.h */ + IAPIPodOutCommandID_GetIPodOutOptions = 0x01, /* from acc, ipod-out-options.h */ + IAPIPodOutCommandID_RetIPodOutOptions = 0x02, /* from dev, ipod-out-options.h */ + IAPIPodOutCommandID_SetIPodOutOptions = 0x03, /* from acc, ipod-out-options.h */ + IAPIPodOutCommandID_AccessoryStateChangeEvent = 0x04, /* from acc, */ +}; + +#include "ipod-out/accessory-state-change-event.h" +#include "ipod-out/ipod-out-options.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h new file mode 100644 index 0000000000..4863e89554 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.368 4.12.7 Command 0x04: AccessoryStateChangeEvent */ + +enum IAPAccessoryStateChangeEvents { + IAPAccessoryStateChangeEvents_DisplaySwitchAwayFromIPodOut = 0x00, + IAPAccessoryStateChangeEvents_DisplaySwitchBackToIPodOut = 0x01, + IAPAccessoryStateChangeEvents_AudioSwitchAwayFromIPodOut = 0x02, + IAPAccessoryStateChangeEvents_AudioSwitchBackToIPodOut = 0x03, + IAPAccessoryStateChangeEvents_EnterDaytimeMode = 0x04, + IAPAccessoryStateChangeEvents_EnterNighttimeMode = 0x05, +}; + +struct AccessoryStateChangeEventPayload { + uint8_t state_change; /* IAPAccessoryStateChangeEvents */ +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h new file mode 100644 index 0000000000..0af8b99ce2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h @@ -0,0 +1,29 @@ +#pragma once +#include + +/* [1] P.366 4.12.4 Command 0x01: GetiPodOutOptions */ + +struct IAPGetIPodOutOptionsPayload { + uint8_t report_current; +} __attribute__((packed)); + +enum IAPIPodOutOptionBits { + IAPIPodOutOptionBits_DisplayAudio = 1 << 0, + IAPIPodOutOptionBits_DisplayPhoneCall = 1 << 1, + IAPIPodOutOptionBits_DisplaySMS = 1 << 2, + IAPIPodOutOptionBits_DisplayVoicemail = 1 << 4, + IAPIPodOutOptionBits_DisplayPushNotifications = 1 << 5, + IAPIPodOutOptionBits_DisplayClockAlarmNotifications = 1 << 6, + IAPIPodOutOptionBits_DisplayTestPattern = 1 << 8, + IAPIPodOutOptionBits_ShowMinimalIPodOut = 1 << 9, + IAPIPodOutOptionBits_ShowFullIPodOud = 1 << 10, +}; + +struct IAPRetIPodOutOptionsPayload { + uint8_t report_current; + uint32_t options; /* IAPIPodOutOptionBits */ +} __attribute__((packed)); + +struct IAPSetIPodOutOptionsPayload { + uint32_t options; /* IAPIPodOutOptionBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/location.h b/firmware/usbstack/iap/libiap/spec/lingoes/location.h new file mode 100644 index 0000000000..8e2a4aabae --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/location.h @@ -0,0 +1,19 @@ +#pragma once +#include + +/* [1] P.371 Table 4-266 Location lingo commands */ +enum IAPLocationCommandID { + IAPLocationCommandID_AccessoryAck = 0x00, /* from acc */ + IAPLocationCommandID_GetAccessoryCaps = 0x01, /* from dev */ + IAPLocationCommandID_AccessoryCaps = 0x02, /* from acc */ + IAPLocationCommandID_GetAccessoryControl = 0x03, /* from dev */ + IAPLocationCommandID_RetAccessoryControl = 0x04, /* from acc */ + IAPLocationCommandID_SetAccessoryControl = 0x05, /* from dev */ + IAPLocationCommandID_GetAccessoryData = 0x06, /* from dev */ + IAPLocationCommandID_RetAccessoryData = 0x07, /* from acc */ + IAPLocationCommandID_SetAccessoryData = 0x08, /* from dev */ + IAPLocationCommandID_AsyncAccessoryData = 0x09, /* from acc */ + IAPLocationCommandID_IPodAck = 0x80, /* from dev */ +}; + +/* TODO: add payload definitions */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h new file mode 100644 index 0000000000..3c90b3800c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.534 Table C-12 Microphone lingo command summary */ +enum IAPMicrophoneCommandID { + IAPMicrophoneCommandID_BeginRecord = 0x00, /* deprecated */ + IAPMicrophoneCommandID_EndRecord = 0x01, /* deprecated */ + IAPMicrophoneCommandID_BeginPlayback = 0x02, /* deprecated */ + IAPMicrophoneCommandID_EndPlayback = 0x03, /* deprecated */ + IAPMicrophoneCommandID_AccessoryAck = 0x04, /* from acc, general/acc-ack.h */ + IAPMicrophoneCommandID_GetAccessoryAck = 0x05, /* from dev, no payload */ + IAPMicrophoneCommandID_IPodModeChange = 0x06, /* from dev, ipod-mode-change.h */ + IAPMicrophoneCommandID_GetAccessoryCaps = 0x07, /* from dev, no payload */ + IAPMicrophoneCommandID_RetAccessoryCaps = 0x08, /* from acc, acc-caps.h */ + IAPMicrophoneCommandID_GetAccessoryCtrl = 0x09, /* from dev, acc-ctrl.h */ + IAPMicrophoneCommandID_RetAccessoryCtrl = 0x0A, /* from acc, acc-ctrl.h */ + IAPMicrophoneCommandID_SetAccessoryCtrl = 0x0B, /* from dev, acc-ctrl.h */ +}; + +#include "microphone/acc-caps.h" +#include "microphone/acc-ctrl.h" +#include "microphone/ipod-mode-change.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h new file mode 100644 index 0000000000..3a98aadb32 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h @@ -0,0 +1,14 @@ +#pragma once +#include + +enum IAPAccCapsBits { + IAPAccCapsBits_StereoInput = 1 << 0, + IAPAccCapsBits_StereoMonoInputSwitch = 1 << 1, + IAPAccCapsBits_VariableRecordLevel = 1 << 2, + IAPAccCapsBits_RecordLevelLimit = 1 << 3, + IAPAccCapsBits_DuplexAudio = 1 << 4, +}; + +struct IAPRetAccCapsPayload { + uint8_t bits; /* IAPAccCapsBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h new file mode 100644 index 0000000000..fe3506aad9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h @@ -0,0 +1,22 @@ +#pragma once +#include + +enum IAPAccCtrlType { + IAPAccCtrlType_LineInput = 0x01, + IAPAccCtrlType_RecordingLevelControl = 0x02, + IAPAccCtrlType_RecordingLevelLimiterControl = 0x03, +}; + +struct IAPGetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ +} __attribute__((packed)); + +struct IAPRetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ + uint8_t value; +} __attribute__((packed)); + +struct IAPSetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ + uint8_t value; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h new file mode 100644 index 0000000000..55a49be67a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.537 Table C-18 Mode values */ +enum IAPIPodModeChangeModes { + IAPIPodModeChangeModes_BeginAudioRecord = 0x00, + IAPIPodModeChangeModes_EndAudioRecord = 0x01, + IAPIPodModeChangeModes_BeginAudioPlayback = 0x02, + IAPIPodModeChangeModes_EndAudioPlayback = 0x03, +}; + +struct IAPIPodModeChangePayload { + uint8_t mode; /* IAPIPodModeChangeModes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h new file mode 100644 index 0000000000..11a540b6f6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h @@ -0,0 +1,69 @@ +#pragma once +#include + +/* [1] P.288 Table 4-111 RF Tuner lingo command summary */ +enum IAPRFTunerCommandID { + IAPRFTunerCommandID_AccessoryAck = 0x00, /* 1.00, from acc, acc-ack.h */ + IAPRFTunerCommandID_GetTunerCaps = 0x01, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerCaps = 0x02, /* 1.00, from acc, tuner-caps.h */ + IAPRFTunerCommandID_GetTunerCtrl = 0x03, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerCtrl = 0x04, /* 1.00, from acc, tuner-ctrl.h */ + IAPRFTunerCommandID_SetTunerCtrl = 0x05, /* 1.00, from dev, tuner-ctlr.h */ + IAPRFTunerCommandID_GetTunerBand = 0x06, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerBand = 0x07, /* 1.00, from acc, tuner-band.h */ + IAPRFTunerCommandID_SetTunerBand = 0x08, /* 1.00, from dev, tuner-band.h*/ + IAPRFTunerCommandID_GetTunerFreq = 0x09, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerFreq = 0x0A, /* 1.00, from acc, tuner-freq.h */ + IAPRFTunerCommandID_SetTunerFreq = 0x0B, /* 1.00, from dev, tuner-freq.h */ + IAPRFTunerCommandID_GetTunerMode = 0x0C, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerMode = 0x0D, /* 1.00, from acc, tuner-mode.h */ + IAPRFTunerCommandID_SetTunerMode = 0x0E, /* 1.00, from dev, tuner-mode.h */ + IAPRFTunerCommandID_GetTunerSeekRssi = 0x0F, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerSeekRssi = 0x10, /* 1.00, from acc, tuner-seek-rssi.h */ + IAPRFTunerCommandID_SetTunerSeekRssi = 0x11, /* 1.00, from dev, tuner-seek-rssi.h */ + IAPRFTunerCommandID_TunerSeekStart = 0x12, /* 1.00, from dev, tuner-seek.h */ + IAPRFTunerCommandID_TunerSeekDone = 0x13, /* 1.00, from acc, tuner-seek.h */ + IAPRFTunerCommandID_GetTunerStatus = 0x14, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerStatus = 0x15, /* 1.00, from acc, tuner-status.h */ + IAPRFTunerCommandID_GetStatusNotifyMask = 0x16, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetStatusNotifyMask = 0x17, /* 1.00, from acc, status-notification.h */ + IAPRFTunerCommandID_SetStatusNotifyMask = 0x18, /* 1.00, from dev, status-notification.h */ + IAPRFTunerCommandID_StatusChangeNotify = 0x19, /* 1.00, from acc, status-notification.h */ + IAPRFTunerCommandID_GetRdsReadyStatus = 0x1A, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetRdsReadyStatus = 0x1B, /* 1.00, from acc, rds-ready-status.h */ + IAPRFTunerCommandID_GetRdsData = 0x1C, /* 1.00, from dev, rds-data.h */ + IAPRFTunerCommandID_RetRdsData = 0x1D, /* 1.00, from acc, rds-data.h */ + IAPRFTunerCommandID_GetRdsNotifyMask = 0x1E, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetRdsNotifyMask = 0x1F, /* 1.00, from acc, rds-notify-mask.h */ + IAPRFTunerCommandID_SetRdsNotifyMask = 0x20, /* 1.00, from dev, rds-notify-mask.h */ + IAPRFTunerCommandID_RdsReadyNotify = 0x21, /* 1.00, from acc, rds-data.h */ + IAPRFTunerCommandID_GetHDProgramServiceCount = 0x25, /* 1.01, from dev, no paylaod */ + IAPRFTunerCommandID_RetHDProgramServiceCount = 0x26, /* 1.01, from acc, hd-program.h */ + IAPRFTunerCommandID_GetHDProgramService = 0x27, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDProgramService = 0x28, /* 1.01, from acc, hd-program.h */ + IAPRFTunerCommandID_SetHDProgramService = 0x29, /* 1.01, from dev, hd-program.h */ + IAPRFTunerCommandID_GetHDDataReadyStatus = 0x2A, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDDataReadyStatus = 0x2B, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_GetHDData = 0x2C, /* 1.01, from dev, hd-data.h */ + IAPRFTunerCommandID_RetHDData = 0x2D, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_GetHDDataNotifyMask = 0x2E, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDDataNotifyMask = 0x2F, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_SetHDDataNotifyMask = 0x30, /* 1.01, from dev, hd-data.h */ + IAPRFTunerCommandID_HDDataReadyNotify = 0x31, /* 1.01, from acc, hd-data.h */ +}; + +#include "rf-tuner/acc-ack.h" +#include "rf-tuner/hd-data.h" +#include "rf-tuner/hd-program.h" +#include "rf-tuner/rds-data.h" +#include "rf-tuner/rds-notify-mask.h" +#include "rf-tuner/rds-ready-status.h" +#include "rf-tuner/status-notification.h" +#include "rf-tuner/tuner-band.h" +#include "rf-tuner/tuner-caps.h" +#include "rf-tuner/tuner-ctrl.h" +#include "rf-tuner/tuner-freq.h" +#include "rf-tuner/tuner-mode.h" +#include "rf-tuner/tuner-seek-rssi.h" +#include "rf-tuner/tuner-seek.h" +#include "rf-tuner/tuner-status.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h new file mode 100644 index 0000000000..e16d3eab7d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h @@ -0,0 +1,13 @@ +#pragma once +#include + +struct IAPRFTunerAccAckPayload { + uint8_t status; /* = IAPAckStatus */ + uint8_t id; +} __attribute__((packed)); + +struct IAPRFTunerAccAckPendingPayload { + uint8_t status; /* = IAPAckStatus_CommandPending */ + uint8_t id; + uint32_t pending_time_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h new file mode 100644 index 0000000000..0e95dc46b2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.319 4.7.45 Command 0x2B: RetHDDataReadyStatus */ + +enum IAPHDDataType { + IAPHDDataType_PSD = 0x00, + IAPHDDataType_SISStationIDNumber = 0x02, + IAPHDDataType_SISStationNameShort = 0x03, + IAPHDDataType_SISStationNameLong = 0x04, + IAPHDDataType_SISALFN = 0x05, + IAPHDDataType_SISStationLocation = 0x06, + IAPHDDataType_SISStationMessage = 0x07, + IAPHDDataType_SISSlogan = 0x08, + IAPHDDataType_SISParameterMessage = 0x09, +}; + +struct IAPRetHDDataReadyStatusPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPGetHDDataPayload { + uint8_t type; /* IAPHDDataType */ +} __attribute__((packed)); + +struct IAPRetHDDataPayload { + uint8_t type; /* IAPHDDataType */ + uint8_t data[]; /* TODO: add difinitions */ +} __attribute__((packed)); + +struct IAPRetHDDataNotifyMaskPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPSetHDDataNotifyMaskPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPHDDataReadyNotifyPayload { + uint8_t type; /* IAPHDDataType */ + uint8_t data[]; /* TODO: add difinitions */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h new file mode 100644 index 0000000000..a02d8ba5d3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h @@ -0,0 +1,17 @@ +#pragma once +#include + +/* [1] P.317 4.7.40 Command 0x26: RetHDProgramServiceCount */ + +struct IAPRetHDProgramServiceCountPayload { + uint8_t count; + uint8_t analog_program_exists; +} __attribute__((packed)); + +struct IAPRetHDProgramServicePayload { + uint8_t index; +} __attribute__((packed)); + +struct IAPSetHDProgramServicePayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h new file mode 100644 index 0000000000..eebb783ea7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.311 4.7.33 Command 0x1C: GetRdsData */ + +struct IAPGetRdsDataPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ +}; + +struct IAPRetRdsDataPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ + uint8_t data[]; /* TODO: add definitions */ +}; + +struct IAPReadyNotifyPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ + uint8_t data[]; /* TODO: add definitions */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h new file mode 100644 index 0000000000..d3d67434e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/* [1] P.314 4.7.36 Command 0x1F: RetRdsNotifyMask */ + +struct IAPRetRdsNotifyMaskPayload { + uint32_t mask; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); + +struct IAPSetRdsNotifyMaskPayload { + uint32_t mask; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h new file mode 100644 index 0000000000..713ebec3bf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.310 4.7.32 Command 0x1B: RetRdsReadyStatus */ + +enum IAPRdsDataParsedType { + IAPRdsDataParsedType_RadioText = 0x04, + IAPRdsDataParsedType_ProgramServiceName = 0x1E, +}; + +enum IAPRdsDataRawType { + IAPRdsDataRawType_RdsGroupData = 0x05, +}; + +struct IAPRetRdsReadyStatusPayload { + uint32_t status; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h new file mode 100644 index 0000000000..4d304fb27f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.307 4.7.28 Command 0x17: RetStatusNotifyMask */ + +struct IAPRetStatusNotifyMaskPayload { + uint8_t mask; /* IAPTunerStatusBits */ +} __attribute__((packed)); + +struct IAPSetStatusNotifyMaskPayload { + uint8_t mask; /* IAPTunerStatusBits */ +} __attribute__((packed)); + +struct IAPStatusChangeNotifyPayload { + uint8_t mask; /* IAPTunerStatusBits */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h new file mode 100644 index 0000000000..5ec03a950d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h @@ -0,0 +1,19 @@ +#pragma once +#include + +/* [1] P.297 4.7.12 Command 0x07: RetTunerBand */ + +enum IAPTunerBandStateBits { + AMBandWorldwide = 0x00, + FMBandEuropeUS = 0x01, + FMBandJapan = 0x02, + FNBandWide = 0x03, +}; + +struct IAPRetTunerBandPayload { + uint8_t state; /* IAPTunerBandStateBits */ +} __attribute__((packed)); + +struct IAPSetTunerBandPayload { + uint8_t state; /* IAPTunerBandStateBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h new file mode 100644 index 0000000000..0b233f89fd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.292 4.7.7 Command 0x02: RetTunerCaps */ + +enum IAPFMTunerResolutionID { + _200kHz = 0b00, + _100kHz = 0b01, + _50kHz = 0b10, +}; + +enum IAPAMTunerResolutionID { + _10kHz = 0b0, + _9kHz = 0b1, +}; + +enum IAPTunerCapsBits { + IAPTunerCapsBits_AMWorldwide = 1 << 0, + IAPTunerCapsBits_FMEuropeUS = 1 << 1, + IAPTunerCapsBits_FMJapan = 1 << 2, + IAPTunerCapsBits_FMWide = 1 << 3, + IAPTunerCapsBits_HDRadio = 1 << 4, + IAPTunerCapsBits_PowerOnOff = 1 << 8, + IAPTunerCapsBits_StatusChangeNotification = 1 << 9, + IAPTunerCapsBits_MinFMResolution = 3 << 16, /* IAPFMTunerResolutionID */ + IAPTunerCapsBits_TunerSeekUpDown = 1 << 18, + IAPTunerCapsBits_TunerSeekRSSIThreshold = 1 << 19, + IAPTunerCapsBits_ForcemonophonicMode = 1 << 20, + IAPTunerCapsBits_StereoBlend = 1 << 21, + IAPTunerCapsBits_FMTunerDeemphasisSelect = 1 << 22, + IAPTunerCapsBits_AMTuner9kHzResoluton = 1 << 23, + IAPTunerCapsBits_RDSData = 1 << 24, + IAPTunerCapsBits_TunerChannelRSSIIndication = 1 << 25, + IAPTunerCapsBits_StereoSourceIndicator = 1 << 26, + IAPTunerCapsBits_RDSRawMode = 1 << 27, +}; + +struct IAPRetTunerCapsPayload { + uint32_t caps; /* IAPTunerCapsBits */ + uint8_t reserved1; /* = 0x01 */ + uint8_t reserved2; /* = 0x00 */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h new file mode 100644 index 0000000000..c09fd2465d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.294 4.7.9 Command 0x04: RetTunerCtrl */ + +enum IAPTunerCtrlStateBits { + PowerDraw = 1 << 0, + StateChangeNotification = 1 << 1, + RDSRawMode = 1 << 3, +}; + +struct IAPRetTunerCtrlPayload { + uint8_t state; /* IAPTunerCtrlStateBits */ +} __attribute__((packed)); + +struct IAPSetTunerCtrlPayload { + uint8_t state; /* IAPTunerCtrlStateBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h new file mode 100644 index 0000000000..ed70a8f0aa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.298 4.7.15 Command 0x0A: RetTunerFreq */ + +struct IAPRetTunerFreqPayload { + uint32_t freq_khz; + uint8_t rssi_level; +} __attribute__((packed)); + +struct IAPSetTunerFreqPayload { + uint32_t freq_khz; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h new file mode 100644 index 0000000000..9ed455875f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.300 4.7.18 Command 0x0D: RetTunerMode */ + +enum IAPTunerModeStatusBits { + FMTunerResolution = 3 << 0, /* IAPFMTunerResolutionID */ + TunerSeekingUp = 1 << 2, + TunerSeekingWithMinRSSIThreshold = 1 << 3, + ForceMonophonic = 1 << 4, + StereoBlend = 1 << 5, + FMTunerDeemphasis50usec = 1 << 6, + AMTunerResolution = 1 << 7, /* IAPAMTunerResolutionID */ +}; + +struct IAPRetTunerModePayload { + uint8_t status; /* IAPTunerModeStatusBits */ +} __attribute__((packed)); + +struct IAPSetTunerModePayload { + uint8_t status; /* IAPTunerModeStatusBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h new file mode 100644 index 0000000000..e3cf8fca8e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/* [1] P.302 4.7.21 Command 0x10: RetTunerSeekRssi */ + +struct IAPRetTunerSeekRssiPayload { + uint8_t rssi_threshold; +} __attribute__((packed)); + +struct IAPSetTunerSeekRssiPayload { + uint8_t rssi_threshold; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h new file mode 100644 index 0000000000..ff56682887 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h @@ -0,0 +1,33 @@ +#pragma once +#include + +/* [1] P.302 4.7.23 Command 0x12: TunerSeekStart */ + +enum IAPTunerSeekOperationID { + IAPTunerSeekOperationID_NoSeek = 0x00, + IAPTunerSeekOperationID_SeekUpFromBeginning = 0x01, + IAPTunerSeekOperationID_SeekDownFromEnd = 0x02, + IAPTunerSeekOperationID_SeekUpFromCurrent = 0x03, + IAPTunerSeekOperationID_SeekDownFromCurrent = 0x04, + IAPTunerSeekOperationID_SeekUpFromBeginningWithRssiThreshold = 0x05, + IAPTunerSeekOperationID_SeekDownFromEndWithRssiThreshold = 0x06, + IAPTunerSeekOperationID_SeekUpFromCurrentWithRssiThreshold = 0x07, + IAPTunerSeekOperationID_SeekDownFromCurrentWithRssiThreshold = 0x08, + IAPTunerSeekOperationID_SeekUpFromBeginningForHDSignal = 0x09, + IAPTunerSeekOperationID_SeekDownFromEndForHDSignal = 0x0A, + IAPTunerSeekOperationID_SeekUpFromCurrentForHDSignal = 0x0B, + IAPTunerSeekOperationID_SeekDownFromCurrentForHDSignal = 0x0C, + IAPTunerSeekOperationID_SeekUpFromBeginningForHDSignalWithRssiThreshold = 0x0D, + IAPTunerSeekOperationID_SeekDownFromEndForHDSignalWithRssiThreshold = 0x0E, + IAPTunerSeekOperationID_SeekUpFromCurrentForHDSignalWithRssiThreshold = 0x0F, + IAPTunerSeekOperationID_SeekDownFromCurrentForHDSignalWithRssiThreshold = 0x10, +}; + +struct IAPTunerSeekStartPayload { + uint8_t operation_id; /* IAPTunerSeekOperationID */ +} __attribute__((packed)); + +struct IAPTunerSeekDonePayload { + uint32_t freq_khz; + uint8_t signal_strength; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h new file mode 100644 index 0000000000..70ba8b01e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h @@ -0,0 +1,17 @@ +#pragma once +#include + +/* [1] P.306 4.7.26 Command 0x15: RetTunerStatus */ + +enum IAPTunerStatusBits { + IAPTunerStatusBits_DataReceived = 1 << 0, + IAPTunerStatusBits_RSSILevelChanged = 1 << 1, + IAPTunerStatusBits_StereoSourceIndicator = 1 << 2, + IAPTunerStatusBits_HDSignalPresent = 1 << 3, + IAPTunerStatusBits_HDDigitalAudioPresent = 1 << 4, + IAPTunerStatusBits_HDDataReceived = 1 << 5, +}; + +struct IAPRetTunerStatusPayload { + uint8_t status; /* IAPTunerStatusBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h new file mode 100644 index 0000000000..e96a25bde3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h @@ -0,0 +1,39 @@ +#pragma once +#include + +/* [1] P.213 Table 4-3 Simple remote lingo command summary */ +enum IAPSimpleRemoteCommandID { + IAPSimpleRemoteCommandID_ContextButtonStatus = 0x00, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_IPodAck = 0x01, /* from dev, general/ipod-ack.h */ + IAPSimpleRemoteCommandID_ImageButtonStatus = 0x02, /* from acc, button-status.h, deprecated */ + IAPSimpleRemoteCommandID_VideoButtonStatus = 0x03, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_AudioButtonStatus = 0x04, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_IPodOutButtonStatus = 0x0B, /* from acc, ipod-out-button-status.h */ + IAPSimpleRemoteCommandID_RotationInputStatus = 0x0C, /* from acc, rotation-input-status.h */ + IAPSimpleRemoteCommandID_RadioButtonStatus = 0x0D, /* from acc, radio-button-status.h */ + IAPSimpleRemoteCommandID_CameraButtonStatus = 0x0E, /* from acc, camera-button-status.h */ + IAPSimpleRemoteCommandID_RegisterDescriptor = 0x0F, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_IPodHIDReport = 0x10, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_AccessoryHIDReport = 0x11, /* from dev, hid.h */ + IAPSimpleRemoteCommandID_UnregisterDescriptor = 0x12, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_VoiceOverEvent = 0x13, /* from acc, voice-over-event.h */ + IAPSimpleRemoteCommandID_GetVoiceOverParameter = 0x14, /* from acc, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_RetVoiceOverParameter = 0x15, /* from dev, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_SetVoiceOverParameter = 0x16, /* from acc, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_GetCurrentVoiceOverItemProperty = 0x17, /* from acc, current-voice-over-item-property.h */ + IAPSimpleRemoteCommandID_RetCurrentVoiceOverItemProperty = 0x18, /* from dev, current-voice-over-item-property.h */ + IAPSimpleRemoteCommandID_SetVoiceOverContext = 0x19, /* from acc, set-voice-over-context.h */ + IAPSimpleRemoteCommandID_VoiceOverParameterChanged = 0x2A, /* from dev, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_AccessoryAck = 0x81, /* from acc, general/acc-ack.h */ +}; + +#include "simple-remote/button-status.h" +#include "simple-remote/camera-button-status.h" +#include "simple-remote/current-voice-over-item-property.h" +#include "simple-remote/hid.h" +#include "simple-remote/ipod-out-button-status.h" +#include "simple-remote/radio-button-status.h" +#include "simple-remote/rotation-input-status.h" +#include "simple-remote/set-voice-over-context.h" +#include "simple-remote/voice-over-event.h" +#include "simple-remote/voice-over-parameter.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h new file mode 100644 index 0000000000..779c2f540b --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h @@ -0,0 +1,98 @@ +#pragma once +#include + +enum IAPContextButtonBits { + /* bits1 */ + IAPContextButtonStatusButtons_PlayPause = 1 << 0, + IAPContextButtonStatusButtons_VolumeUp = 1 << 1, + IAPContextButtonStatusButtons_VolumeDown = 1 << 2, + IAPContextButtonStatusButtons_NextTrack = 1 << 3, + IAPContextButtonStatusButtons_PrevTrack = 1 << 4, + IAPContextButtonStatusButtons_NextAlbum = 1 << 5, + IAPContextButtonStatusButtons_PrevAlbum = 1 << 6, + IAPContextButtonStatusButtons_Stop = 1 << 7, + /* bits2 */ + IAPContextButtonStatusButtons_PlayResume = 1 << 0, + IAPContextButtonStatusButtons_Pause = 1 << 1, + IAPContextButtonStatusButtons_MuteToggle = 1 << 2, + IAPContextButtonStatusButtons_NextChapter = 1 << 3, + IAPContextButtonStatusButtons_PrevChapter = 1 << 4, + IAPContextButtonStatusButtons_NextPlaylist = 1 << 5, + IAPContextButtonStatusButtons_PrevPlaylist = 1 << 6, + IAPContextButtonStatusButtons_ShuffleSettingAdvance = 1 << 7, + /* bits3 */ + IAPContextButtonStatusButtons_RepeatSettingAdvance = 1 << 0, + IAPContextButtonStatusButtons_PowerOn = 1 << 1, + IAPContextButtonStatusButtons_PowerOff = 1 << 2, + IAPContextButtonStatusButtons_Backlight30Seconds = 1 << 3, + IAPContextButtonStatusButtons_BeginFastForward = 1 << 4, + IAPContextButtonStatusButtons_BeginRewind = 1 << 5, + IAPContextButtonStatusButtons_Menu = 1 << 6, + IAPContextButtonStatusButtons_Select = 1 << 7, + /* bits4 */ + IAPContextButtonStatusButtons_UpArrow = 1 << 0, + IAPContextButtonStatusButtons_DownArrow = 1 << 1, + IAPContextButtonStatusButtons_BacklightOff = 1 << 2, +}; + +enum IAPImageButtonBits { + /* bits1 */ + IAPImageButtonBits_PlayPause = 1 << 0, + IAPImageButtonBits_NextImage = 1 << 1, + IAPImageButtonBits_PrevImage = 1 << 2, + IAPImageButtonBits_Stop = 1 << 3, + IAPImageButtonBits_PlayResume = 1 << 4, + IAPImageButtonBits_Pause = 1 << 5, + IAPImageButtonBits_ShuffleAdvance = 1 << 6, + IAPImageButtonBits_RepeatAdvance = 1 << 7, +}; + +enum IAPVideoButtonBits { + /* bits1 */ + IAPVideoButtonBits_PlayPause = 1 << 0, + IAPVideoButtonBits_NextVideo = 1 << 1, + IAPVideoButtonBits_PrevVideo = 1 << 2, + IAPVideoButtonBits_Stop = 1 << 3, + IAPVideoButtonBits_PlayResume = 1 << 4, + IAPVideoButtonBits_Pause = 1 << 5, + IAPVideoButtonBits_BeginFF = 1 << 6, + IAPVideoButtonBits_BeginREW = 1 << 7, + /* bits1 */ + IAPVideoButtonBitsNextChapter = 1 << 0, + IAPVideoButtonBitsPrevChapter = 1 << 1, + IAPVideoButtonBitsNextFrame = 1 << 2, + IAPVideoButtonBitsPrevFrame = 1 << 3, +}; + +enum IAPAudioButtonBits { + /* bits1 */ + IAPAudioButtonBits_PlayPause = 1 << 0, + IAPAudioButtonBits_VolumeUp = 1 << 1, + IAPAudioButtonBits_VolumeDown = 1 << 2, + IAPAudioButtonBits_NextTrack = 1 << 3, + IAPAudioButtonBits_PrevTrack = 1 << 4, + IAPAudioButtonBits_NextAlbum = 1 << 5, + IAPAudioButtonBits_PrevAlbum = 1 << 6, + IAPAudioButtonBits_Stop = 1 << 7, + /* bits2 */ + IAPAudioButtonBits_PlayResume = 1 << 0, + IAPAudioButtonBits_Pause = 1 << 1, + IAPAudioButtonBits_MuteToggle = 1 << 2, + IAPAudioButtonBits_NextChapter = 1 << 3, + IAPAudioButtonBits_PrevChapter = 1 << 4, + IAPAudioButtonBits_NextPlaylist = 1 << 5, + IAPAudioButtonBits_PrevPlaylist = 1 << 6, + IAPAudioButtonBits_ShuffleSettingAdvance = 1 << 7, + /* bits3 */ + IAPAudioButtonBits_RepeatSettingAdvance = 1 << 0, + IAPAudioButtonBits_BeginFF = 1 << 1, + IAPAudioButtonBits_BeginREW = 1 << 2, + IAPAudioButtonBits_Record = 1 << 3, +}; + +struct IAPButtonStatusPayload { + uint8_t bits1; /* IAP{Context,Image,Video,Audio}ButtonBits(bits1) */ + uint8_t bits2; /* IAP{Context,Image,Video,Audio}ButtonBits(bits2), optional */ + uint8_t bits3; /* IAP{Context,Image,Video,Audio}ButtonBits(bits3), optional */ + uint8_t bits4; /* IAP{Context,Image,Video,Audio}ButtonBits(bits4), optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h new file mode 100644 index 0000000000..7e5fd17b8b --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPCameraButtonStatus { + IAPCameraButtonStatus_Up = 0x00, + IAPCameraButtonStatus_Down = 0x01, +}; + +struct IAPCameraButtonStatusPayload { + uint8_t status; /* IAPCameraButtonStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h new file mode 100644 index 0000000000..fbbf85fe79 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h @@ -0,0 +1,72 @@ +#pragma once +#include + +enum IAPCurrentVoiceOverItemPropertyType { + IAPCurrentVoiceOverItemPropertyType_Label, + IAPCurrentVoiceOverItemPropertyType_Value, + IAPCurrentVoiceOverItemPropertyType_Hint, + IAPCurrentVoiceOverItemPropertyType_Frame, + IAPCurrentVoiceOverItemPropertyType_Traits, +}; + +struct IAPGetCurrentVoiceOverItemPropertyPayload { + uint8_t type; /* IAPCurrentVoiceOverItemPropertyType */ +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyPayload { + uint8_t type; /* IAPCurrentVoiceOverItemPropertyType */ + uint8_t value[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyLabelPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Label */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyVolumePayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Volume */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyHintPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Hint */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyFramePayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Frame */ + uint16_t top_x; + uint16_t top_y; + uint16_t bottom_x; + uint16_t bottom_y; +} __attribute__((packed)); + +enum IAPRetCurrentVoiceOverItemPropertyTraits { + IAPRetCurrentVoiceOverItemPropertyTraits_Button = 0x0000, + IAPRetCurrentVoiceOverItemPropertyTraits_Link = 0x0001, + IAPRetCurrentVoiceOverItemPropertyTraits_SearchField = 0x0002, + IAPRetCurrentVoiceOverItemPropertyTraits_Image = 0x0003, + IAPRetCurrentVoiceOverItemPropertyTraits_Selected = 0x0004, + IAPRetCurrentVoiceOverItemPropertyTraits_Sound = 0x0005, + IAPRetCurrentVoiceOverItemPropertyTraits_KeyboardKey = 0x0006, + IAPRetCurrentVoiceOverItemPropertyTraits_StaticText = 0x0007, + IAPRetCurrentVoiceOverItemPropertyTraits_SummaryElement = 0x0008, + IAPRetCurrentVoiceOverItemPropertyTraits_NotEnabled = 0x0009, + IAPRetCurrentVoiceOverItemPropertyTraits_UpdatesFrequently = 0x000A, + IAPRetCurrentVoiceOverItemPropertyTraits_StartsMediaSession = 0x000B, + IAPRetCurrentVoiceOverItemPropertyTraits_Adjustable = 0x000C, + IAPRetCurrentVoiceOverItemPropertyTraits_BackButton = 0x000D, + IAPRetCurrentVoiceOverItemPropertyTraits_Map = 0x000E, + IAPRetCurrentVoiceOverItemPropertyTraits_DeleteKey = 0x000F, +}; + +struct IAPRetCurrentVoiceOverItemPropertyTraitsPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Traits */ + uint16_t traits[]; /* IAPRetCurrentVoiceOverItemPropertyTraits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h new file mode 100644 index 0000000000..70b42be1fd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h @@ -0,0 +1,75 @@ +#pragma once +#include + +enum IAPRegisterDescriptorCountryCodes { + IAPRegisterDescriptorCountryCodes_Undefined = 0x00, + IAPRegisterDescriptorCountryCodes_Arabic = 0x01, + IAPRegisterDescriptorCountryCodes_Belgian = 0x02, + IAPRegisterDescriptorCountryCodes_CanadianBilingual = 0x03, + IAPRegisterDescriptorCountryCodes_CanadianFrench = 0x04, + IAPRegisterDescriptorCountryCodes_CzechRepublic = 0x05, + IAPRegisterDescriptorCountryCodes_Danish = 0x06, + IAPRegisterDescriptorCountryCodes_Finnish = 0x07, + IAPRegisterDescriptorCountryCodes_French = 0x08, + IAPRegisterDescriptorCountryCodes_German = 0x09, + IAPRegisterDescriptorCountryCodes_Greek = 0x0A, + IAPRegisterDescriptorCountryCodes_Hebrew = 0x0B, + IAPRegisterDescriptorCountryCodes_Hungarian = 0x0C, + IAPRegisterDescriptorCountryCodes_International = 0x0D, + IAPRegisterDescriptorCountryCodes_Italian = 0x0E, + IAPRegisterDescriptorCountryCodes_Japan = 0x0F, + IAPRegisterDescriptorCountryCodes_Korean = 0x10, + IAPRegisterDescriptorCountryCodes_LatinAmerican = 0x11, + IAPRegisterDescriptorCountryCodes_Netherlands = 0x12, + IAPRegisterDescriptorCountryCodes_Norwegian = 0x13, + IAPRegisterDescriptorCountryCodes_Persian = 0x14, + IAPRegisterDescriptorCountryCodes_Poland = 0x15, + IAPRegisterDescriptorCountryCodes_Portuguese = 0x16, + IAPRegisterDescriptorCountryCodes_Russian = 0x17, + IAPRegisterDescriptorCountryCodes_Slovakia = 0x18, + IAPRegisterDescriptorCountryCodes_Spanish = 0x19, + IAPRegisterDescriptorCountryCodes_Swedish = 0x1A, + IAPRegisterDescriptorCountryCodes_SwissFrench = 0x1B, + IAPRegisterDescriptorCountryCodes_SwissGerman = 0x1C, + IAPRegisterDescriptorCountryCodes_Switzerland = 0x1D, + IAPRegisterDescriptorCountryCodes_Taiwan = 0x1E, + IAPRegisterDescriptorCountryCodes_TurkishQ = 0x1F, + IAPRegisterDescriptorCountryCodes_UK = 0x20, + IAPRegisterDescriptorCountryCodes_US = 0x21, + IAPRegisterDescriptorCountryCodes_Croatian = 0x22, + IAPRegisterDescriptorCountryCodes_TurkishF = 0x23, + IAPRegisterDescriptorCountryCodes_Thai = 0xFA, + IAPRegisterDescriptorCountryCodes_Flemish = 0xFB, + IAPRegisterDescriptorCountryCodes_Romanian = 0xFC, + IAPRegisterDescriptorCountryCodes_Bulgarian = 0xFD, + IAPRegisterDescriptorCountryCodes_Chinese = 0xFE, +}; + +struct IAPRegisterDescriptorPayload { + uint8_t index; + uint16_t vid; + uint16_t pid; + uint8_t country_code; /* IAPRegisterDescriptorCountryCodes */ + uint8_t descriptor[]; +} __attribute__((packed)); + +enum IAPHIDReportReportType { + IAPHIDReportReportType_Input = 0x00, + IAPHIDReportReportType_Output = 0x01, +}; + +struct IAPIPodHIDReportPayload { + uint8_t index; + uint8_t report_type; /* = IAPHIDReportReportType_Input */ + uint8_t report[]; +} __attribute__((packed)); + +struct IAPAccessoryHIDReportPayload { + uint8_t index; + uint8_t report_type; /* = IAPHIDReportReportType_Output */ + uint8_t report[]; +} __attribute__((packed)); + +struct IAPUnregisterDescriptorPayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h new file mode 100644 index 0000000000..a81839a9c4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h @@ -0,0 +1,26 @@ +#pragma once +#include + +enum IAPIPodOutButtonStatusSource { + IAPIPodOutButtonStatusSource_CarCenterConsole = 0x00, + IAPIPodOutButtonStatusSource_SteeringWheel = 0x01, + IAPIPodOutButtonStatusSource_CarDashboard = 0x02, +}; + +enum IAPIPodOutButtonBits { + /* bits1 */ + IAPIPodOutButtonBits_Select = 1 << 0, + IAPIPodOutButtonBits_Left = 1 << 1, + IAPIPodOutButtonBits_Rigth = 1 << 2, + IAPIPodOutButtonBits_Up = 1 << 3, + IAPIPodOutButtonBits_Down = 1 << 4, + IAPIPodOutButtonBits_Menu = 1 << 5, +}; + +struct IAPIPodOutButtonStatusPayload { + uint8_t source; /* IAPIPodOutButtonStatusSource */ + uint8_t bits1; /* IAPIPodOutButtonBits(bits1) */ + uint8_t bits2; /* IAPIPodOutButtonBits(bits2), optional */ + uint8_t bits3; /* IAPIPodOutButtonBits(bits3), optional */ + uint8_t bits4; /* IAPIPodOutButtonBits(bits4), optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h new file mode 100644 index 0000000000..71b4a7abc5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPRadioButtonStatus { + IAPRadioButtonStatus_Released = 0x00, + IAPRadioButtonStatus_Pushed = 0x02, +}; + +struct IAPRadioButtonStatusPayload { + uint8_t status; /* IAPRadioButtonStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h new file mode 100644 index 0000000000..05ca73726d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h @@ -0,0 +1,33 @@ +#pragma once +#include + +enum IAPRotationInputStatusControllerType { + IAPRotationInputStatusControllerType_FreeWheel = 0x00, +}; + +enum IAPRotationInputStatusRotationDirection { + IAPRotationInputStatusRotationDirection_Left = 0x00, /* CCW */ + IAPRotationInputStatusRotationDirection_Right = 0x01, /* CW */ +}; + +enum IAPRotationInputStatusRotationAction { + IAPRotationInputStatusRotationAction_Completed = 0x00, + IAPRotationInputStatusRotationAction_InProgress = 0x01, + IAPRotationInputStatusRotationAction_Repeat = 0x02, +}; + +enum IAPRotationInputStatusRotationType { + IAPRotationInputStatusRotationType_Detent = 0x00, + IAPRotationInputStatusRotationType_Degree = 0x01, +}; + +struct IAPRotationInputStatusPayload { + uint32_t user_action_duration_ms; + uint8_t source; /* IAPIPodOutButtonStatusSource */ + uint8_t controller_type; /* IAPRotationInputStatusControllerType */ + uint8_t rotation_direction; /* IAPRotationInputStatusRotationDirection */ + uint8_t rotation_action; /* IAPRotationInputStatusRotationAction */ + uint8_t rotation_type; /* IAPRotationInputStatusRotationType */ + uint16_t detents_or_degrees_moved; + uint16_t max_detents_or_degrees_moved; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h new file mode 100644 index 0000000000..05945067f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h @@ -0,0 +1,17 @@ +#pragma once +#include + +enum IAPSetVoiceOverContextType { + IAPSetVoiceOverContextType_None = 0x00, + IAPSetVoiceOverContextType_Header = 0x01, + IAPSetVoiceOverContextType_Link = 0x02, + IAPSetVoiceOverContextType_Form = 0x03, + IAPSetVoiceOverContextType_Cursor = 0x04, + IAPSetVoiceOverContextType_VerticalNavigation = 0x05, + IAPSetVoiceOverContextType_ValueAdjustment = 0x06, + IAPSetVoiceOverContextType_ZoomAdjustment = 0x07, +}; + +struct IAPSetVoiceOverContextPayload { + uint8_t type; /* IAPSetVoiceOverContextType */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h new file mode 100644 index 0000000000..f09290cee4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h @@ -0,0 +1,85 @@ +#pragma once +#include + +enum IAPVoiceOverEventType { + IAPVoiceOverEventType_MovePointer = 0x00, + IAPVoiceOverEventType_MoveToFirst = 0x01, + IAPVoiceOverEventType_MoveToLast = 0x02, + IAPVoiceOverEventType_MoveToNext = 0x03, + IAPVoiceOverEventType_MoveToPrev = 0x04, + IAPVoiceOverEventType_ScrollLeftPage = 0x05, + IAPVoiceOverEventType_ScrollRightPage = 0x06, + IAPVoiceOverEventType_ScrollUpPage = 0x07, + IAPVoiceOverEventType_ScrollDownPage = 0x08, + IAPVoiceOverEventType_ScrollToPoint = 0x09, + IAPVoiceOverEventType_SendTextToInput = 0x0A, + IAPVoiceOverEventType_Cut = 0x0B, + IAPVoiceOverEventType_Copy = 0x0C, + IAPVoiceOverEventType_Paste = 0x0D, + IAPVoiceOverEventType_Home = 0x0E, + IAPVoiceOverEventType_Touch = 0x0F, + IAPVoiceOverEventType_DisplayScale = 0x10, + IAPVoiceOverEventType_CenterDisplay = 0x11, + IAPVoiceOverEventType_PauseSpeak = 0x12, + IAPVoiceOverEventType_ResumeSpeak = 0x13, + IAPVoiceOverEventType_ReadTextFromCurrent = 0x14, + IAPVoiceOverEventType_ReadTextFromTop = 0x15, + IAPVoiceOverEventType_SendTextToSpeech = 0x16, + IAPVoiceOverEventType_Escape = 0x17, +}; + +struct IAPVoiceOverEventPayload { + uint8_t type; /* IAPVoiceOverEventType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPVoiceOverEventMovePointerPayload { + uint8_t type; /* = IAPVoiceOverEventType_MovePointer */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventScrollToPointPayload { + uint8_t type; /* = IAPVoiceOverEventType_ScrollToPoint */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventSendTextToInputPayload { + uint8_t type; /* = IAPVoiceOverEventType_SendTextToInput */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +enum IAPVoiceOverEventTouchEventType { + IAPVoiceOverEventTouchEventType_Begin = 0x00, + IAPVoiceOverEventTouchEventType_Moved = 0x01, + IAPVoiceOverEventTouchEventType_Stationary = 0x02, + IAPVoiceOverEventTouchEventType_Ended = 0x03, + IAPVoiceOverEventTouchEventType_Canceled = 0x04, +}; + +struct IAPVoiceOverEventTouchPayload { + uint8_t type; /* = IAPVoiceOverEventType_Touch */ + uint8_t reserved[4]; + uint8_t touch_event_type; /* IAPVoiceOverEventTouchEventType */ +} __attribute__((packed)); + +struct IAPVoiceOverEventDisplayScalePayload { + uint8_t type; /* = IAPVoiceOverEventType_DisplayScale */ + uint16_t scale_factor; +} __attribute__((packed)); + +struct IAPVoiceOverEventCenterDisplayPayload { + uint8_t type; /* = IAPVoiceOverEventType_CenterDisplay */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventSendTextToSpeechPayload { + uint8_t type; /* = IAPVoiceOverEventType_SendTextToSpeech */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h new file mode 100644 index 0000000000..0d409294b8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h @@ -0,0 +1,28 @@ +#pragma once +#include + +enum IAPGetVoiceOverParameterType { + IAPGetVoiceOverParameterType_VoiceOverVolume = 0x00, + IAPGetVoiceOverParameterType_SpeakingRate = 0x01, + IAPGetVoiceOverParameterType_VoiceOverEnabled = 0x02, +}; + +struct IAPGetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ +} __attribute__((packed)); + +struct IAPRetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); + +struct IAPSetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); + + +struct IAPVoiceOverParameterChangedPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h new file mode 100644 index 0000000000..a33e179f3a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetSoundCheckStatePayload { + uint8_t sound_check_status; +} __attribute__((packed)); + +struct IAPSetSoundCheckStatePayload { + uint8_t sound_check_status; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports.h new file mode 100644 index 0000000000..7969516dc8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports.h @@ -0,0 +1,25 @@ +#pragma once +#include + +/* [1] P.336 Table 4-212 Sports lingo command summary */ +enum IAPSportsCommandID { + IAPSportsCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPSportsCommandID_GetAccessoryVersion = 0x01, /* from dev, no payload */ + IAPSportsCommandID_RetAccessoryVersion = 0x02, /* from acc, accessory-version.h */ + IAPSportsCommandID_GetAccessoryCaps = 0x03, /* from dev, no payload */ + IAPSportsCommandID_RetAccessoryCaps = 0x04, /* from acc, accessory-caps.h */ + IAPSportsCommandID_IPodAck = 0x80, /* from dev, general/ipod-ack.h */ + IAPSportsCommandID_GetIPodCaps = 0x83, /* from acc, no payload */ + IAPSportsCommandID_RetIPodCaps = 0x84, /* from dev, ipod-caps.h */ + IAPSportsCommandID_GetUserIndex = 0x85, /* from acc, no payload */ + IAPSportsCommandID_RetUserIndex = 0x86, /* from dev, user-index.h */ + IAPSportsCommandID_GetUserData = 0x88, /* from acc, user-data.h */ + IAPSportsCommandID_RetUserData = 0x89, /* from dev, user-data.h */ + IAPSportsCommandID_SetUserData = 0x8A, /* from acc, user-data.h */ +}; + +#include "sports/accessory-caps.h" +#include "sports/accessory-version.h" +#include "sports/ipod-caps.h" +#include "sports/user-data.h" +#include "sports/user-index.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h new file mode 100644 index 0000000000..ddbdb779b4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPSportsAccessoryCapBits { + IAPAccessoryCapBits_LaterCommands = 1 << 9, +}; + +struct IAPRetAccessoryCapsPayload { + uint16_t mask; /* IAPSportsAccessoryCapBits */ + uint8_t reserved; /* = 0x00 */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h new file mode 100644 index 0000000000..6a6be98427 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPRetAccessoryVersionPayload { + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h new file mode 100644 index 0000000000..a203552a0f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h @@ -0,0 +1,12 @@ +#pragma once +#include + +enum IAPSportsIPodCapBits { + Cardio = 1 << 0, + UserData = 1 << 1, +}; + +struct IAPRetIPodCapsPayload { + uint16_t caps; /* IAPSportsIPodCapBits */ + uint8_t user_count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h new file mode 100644 index 0000000000..5044e64efa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h @@ -0,0 +1,73 @@ +#pragma once +#include + +/* [1] P.341 4.9.13 Command 0x88: GetUserData */ + +enum IAPSportsUserDataType { + IAPSportsUserDataType_UnitSystem = 0x00, + IAPSportsUserDataType_Name = 0x01, + IAPSportsUserDataType_Gender = 0x02, + IAPSportsUserDataType_Weight = 0x03, + IAPSportsUserDataType_Age = 0x04, + IAPSportsUserDataType_WorkoutRecording = 0x05, +}; + +struct IAPGetUserDataPayload { + uint8_t type; /* IAPSportsUserDataType */ +} __attribute__((packed)); + +struct IAPRetUserDataPayload { + uint8_t type; /* IAPSportsUserDataType */ + uint8_t data[]; +} __attribute__((packed)); + +enum IAPSportsUnitSystem { + IAPSportsUnitSystem_None = 0x00, + IAPSportsUnitSystem_Imperial = 0x01, + IAPSportsUnitSystem_Metric = 0x02, +}; + +struct IAPRetUserDataUnitSystemPayload { + uint8_t type; /* = IAPSportsUserDataType_UnitSystem */ + uint8_t preferred_unit_system; /* IAPSportsUnitSystem */ +} __attribute__((packed)); + +struct IAPRetUserDataNamePayload { + uint8_t type; /* = IAPSportsUserDataType_Name */ + char name[]; +} __attribute__((packed)); + +enum IAPSportsGender { + IAPSportsGender_None = 0x00, + IAPSportsGender_Female = 0x01, + IAPSportsGender_Male = 0x02, +}; + +struct IAPRetUserDataGenderPayload { + uint8_t type; /* = IAPSportsUserDataType_Gender */ + uint8_t gender; /* IAPSportsGender */ +} __attribute__((packed)); + +struct IAPRetUserDataWeightPayload { + uint8_t type; /* = IAPSportsUserDataType_Weight */ + uint16_t weight; +} __attribute__((packed)); + +struct IAPRetUserDataAgePayload { + uint8_t type; /* = IAPSportsUserDataType_Age */ + uint8_t age; +} __attribute__((packed)); + +enum IAPSportsWorkoutRecordingPreferences { + IAPSportsWorkoutRecordingPreferences_None = 0x00, + IAPSportsWorkoutRecordingPreferences_Never = 0x01, + IAPSportsWorkoutRecordingPreferences_Ask = 0x02, + IAPSportsWorkoutRecordingPreferences_Always = 0x03, +}; + +struct IAPRetUserDataWorkoutRecordingPayload { + uint8_t type; /* = IAPSportsUserDataType_WorkoutRecording */ + uint8_t preference; /* IAPSportsWorkoutRecordingPreferences */ +} __attribute__((packed)); + +/* IAPSetUserDataPayload = IAPRetUserDataPayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h new file mode 100644 index 0000000000..2fce3aa328 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetUserIndexPayload { + uint8_t user_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage.h new file mode 100644 index 0000000000..cfce9291b1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage.h @@ -0,0 +1,27 @@ +#pragma once +#include + +/* [1] P.357 Table 4-242 Storage lingo commands */ + +enum IAPStorageCommandID { + IAPStorageCommandID_IPodAck = 0x00, /* from dev, ipod-ack.h */ + IAPStorageCommandID_GetIPodCaps = 0x01, /* from acc, no payload */ + IAPStorageCommandID_RetIPodCaps = 0x02, /* from dev, ipod-caps.h */ + IAPStorageCommandID_RetIPodFileHandle = 0x04, /* from dev, ipod-file-handle.h */ + IAPStorageCommandID_WriteIPodFileData = 0x07, /* from acc, write-ipod-file-data.h */ + IAPStorageCommandID_CloseIPodFile = 0x08, /* from acc, ipod-file-handle.h */ + IAPStorageCommandID_GetIPodFreeSpace = 0x10, /* from acc, no payload */ + IAPStorageCommandID_RetIPodFreeSpace = 0x11, /* from dev, ipod-free-space.h */ + IAPStorageCommandID_OpenIPodFeatureFile = 0x12, /* from acc, ipod-file-handle.h */ + IAPStorageCommandID_AccessoryAck = 0x80, /* from acc, acc-ack.h */ + IAPStorageCommandID_GetAccessoryCaps = 0x81, /* from dev, no payload */ + IAPStorageCommandID_RetAccessoryCaps = 0x82, /* from acc, */ +}; + +#include "storage/acc-ack.h" +#include "storage/accessory-caps.h" +#include "storage/ipod-ack.h" +#include "storage/ipod-caps.h" +#include "storage/ipod-file-handle.h" +#include "storage/ipod-free-space.h" +#include "storage/write-ipod-file-data.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h new file mode 100644 index 0000000000..1980237af2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPStorageAccessoryAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; + uint8_t handle; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h new file mode 100644 index 0000000000..7a7c26deed --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h @@ -0,0 +1,11 @@ +#pragma once +#include + +/* [1] P.365 4.11.14 Command 0x82: RetAccessoryCaps */ + +struct IAPStorageRetAccessoryCapsPayload { + uint8_t reserved1; /* = 0x00 */ + uint8_t reserved2; /* = 0xFF */ + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h new file mode 100644 index 0000000000..05b9c012e2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.359 4.11.3 Command 0x00: iPodAck */ + +struct IAPStorageIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; + uint8_t handle; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h new file mode 100644 index 0000000000..2a4d04be46 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.359 4.11.5 Command 0x02: RetiPodCaps */ + +struct IAPIPodRetIPodCapsPayload { + uint64_t total_space; + uint32_t max_file_size; + uint16_t max_write_size; + uint8_t reserved[6]; + uint8_t major_version; + uint8_t minor_version; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h new file mode 100644 index 0000000000..edaeefc7cd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h @@ -0,0 +1,30 @@ +#pragma once +#include + +/* [1] P.360 4.11.6 Command 0x04: RetiPodFileHandle */ + +struct IAPRetIPodFileHandlePayload { + uint8_t handle; +} __attribute__((packed)); + +struct IAPCloseIPodFilePayload { + uint8_t handle; +} __attribute__((packed)); + +enum IAPOpenIPodFeatureFileFeatureType { + IAPOpenIPodFeatureFileFeatureType_TadioTagging = 0x01, + IAPOpenIPodFeatureFileFeatureType_CardioEquipmentWorkout = 0x02, +}; + +enum IAPOpenIPodFeatureFileOptionsMask { + IAPOpenIPodFeatureFileOptionsMask_AppendFileData = 1 << 0, + IAPOpenIPodFeatureFileOptionsMask_AppendIPodInfo = 1 << 1, + IAPOpenIPodFeatureFileOptionsMask_InsertSignature = 1 << 3, +}; + +struct IAPOpenIPodFeatureFilePayload { + uint8_t feature_type; /* IAPOpenIPodFeatureFileFeatureType */ + uint32_t options_mask; /* IAPOpenIPodFeatureFileOptionsMask */ + uint8_t file_data[]; +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h new file mode 100644 index 0000000000..8f81ec8059 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.362 4.11.10 Command 0x11: RetiPodFreeSpace */ + +struct IAPRetIPodFreeSpacePayload { + uint64_t free_space; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h new file mode 100644 index 0000000000..1c6fe7ce42 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.360 4.11.7 Command 0x07: WriteiPodFileData */ + +struct IAPWriteIPodFileDataPayload { + uint32_t offset; + uint8_t handle; + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h new file mode 100644 index 0000000000..cf4594bdec --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.283 Table 4-101 USB Host mode commands */ +enum IAPUSBHostModeCommandID { + IAPUSBHostModeCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPUSBHostModeCommandID_NotifyUSBMode = 0x04, /* from dev, usb-mode.h */ + IAPUSBHostModeCommandID_IPodAck = 0x80, /* from dev, general/ipod-ack.h */ + IAPUSBHostModeCommandID_GetIPodUSBMode = 0x81, /* from acc, no payload */ + IAPUSBHostModeCommandID_RetIPodUSBMode = 0x82, /* from dev, usb-mode.h */ + IAPUSBHostModeCommandID_SetIPodUSBMode = 0x83, /* from acc, usb-mode.h */ +}; + +#include "usb-host-mode/usb-mode.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h new file mode 100644 index 0000000000..fdc0f6e58d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.285 4.6.3 Command 0x04: NotifyUSBMode */ + +enum IAPUSBHostModeStatusCodes { + Disabled = 0x00, + DeviceMode = 0x01, + HostMode = 0x02, +}; + +struct IAPNotifyUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; + +struct IAPRetIPodUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; + +struct IAPSetIPodUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; diff --git a/firmware/usbstack/iap/libiap/time.h b/firmware/usbstack/iap/libiap/time.h new file mode 100644 index 0000000000..304bb4f04d --- /dev/null +++ b/firmware/usbstack/iap/libiap/time.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPPlatformTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t seconds; +}; diff --git a/firmware/usbstack/iap/libiap/unaligned.h b/firmware/usbstack/iap/libiap/unaligned.h new file mode 100644 index 0000000000..864cac0a34 --- /dev/null +++ b/firmware/usbstack/iap/libiap/unaligned.h @@ -0,0 +1,7 @@ +#pragma once +#include + +typedef __attribute__((aligned(1))) uint8_t uu8; +typedef __attribute__((aligned(1))) uint16_t uu16; +typedef __attribute__((aligned(1))) uint32_t uu32; +typedef __attribute__((aligned(1))) uint64_t uu64; diff --git a/firmware/usbstack/iap/macros.h b/firmware/usbstack/iap/macros.h new file mode 100644 index 0000000000..f741398560 --- /dev/null +++ b/firmware/usbstack/iap/macros.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 + +#include "debug.h" + +#if DEBUG_ENABLE_INFO == 1 +#define LOG(fmt, ...) logf("%lu %s:%d " fmt, iap_debug_timestamp(), __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); +#else +#define LOG(...) +#endif + +#if DEBUG_ENABLE_ERROR == 1 +#define ERROR(fmt, ...) logf("%lu %s:%d " fmt, iap_debug_timestamp(), __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); +#else +#define ERROR(...) +#endif + +#define check_act(cond, act, ...) \ + if(!(cond)) { \ + ERROR("assertion failed" __VA_OPT__(":" __VA_ARGS__)); \ + act; \ + } + +#define AS_PACKET_SIZE 192 +#define AS_EP_IN usb_iap_ep_allocs[0].ep +#define HID_EP_IN usb_iap_ep_allocs[1].ep diff --git a/firmware/usbstack/iap/notification.c b/firmware/usbstack/iap/notification.c new file mode 100644 index 0000000000..95444f077c --- /dev/null +++ b/firmware/usbstack/iap/notification.c @@ -0,0 +1,111 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "playback.h" + +#include "iap-usb.h" +#include "libiap/iap.h" +#include "platform.h" + +#include "macros.h" + +extern bool iap_initialized; + +void iap_on_track_time_position(uint32_t pos_ms) { + if(!iap_initialized) { + return; + } + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_track_time_position(ctx, pos_ms); +} + +void iap_on_track_playback_index(uint32_t index, bool track_ready) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + + if(track_ready) { + /* called from from audio_finish_load_track() */ + goto notify; + } + + /* called from audio_playlist_track_change() */ + struct Platform* plt = ctx->platform; + if(plt->aa_slot < 0) { + goto notify; + } + if(playback_current_aa_hid(plt->aa_slot) >= 0) { + /* artwork ready, maybe preloaded track */ + goto notify; + } else { + /* artwork not ready, maybe after a playlist jump. + * in this case, we will be called again from audio_finish_load_track(), + * with track_ready == true. */ + return; + } +notify: + iap_notify_track_playback_index(ctx, index); +} + +void iap_on_tracks_count(uint32_t count) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_tracks_count(ctx, count); +} + +void iap_on_play_status(int status) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_play_status(ctx, _iap_convert_play_status(status)); +} + +void iap_on_volume(int volume) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_volume(ctx, _iap_convert_volume(volume), iap_false); +} + +void iap_on_shuffle_state(bool state) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_shuffle_state(ctx, _iap_convert_shuffle_state(state)); +} + +void iap_on_repeat_state(int state) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_repeat_state(ctx, _iap_convert_repeat_state(state)); +} diff --git a/firmware/usbstack/iap/platform-macros.h b/firmware/usbstack/iap/platform-macros.h new file mode 100644 index 0000000000..6b1eb8a5ab --- /dev/null +++ b/firmware/usbstack/iap/platform-macros.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include + +#include "debug.h" + +#if DEBUG_ENABLE_INFO == 1 +#define IAP_LOGF(fmt, ...) logf("%lu " fmt, iap_debug_timestamp() __VA_OPT__(, __VA_ARGS__)) +#endif + +#if DEBUG_ENABLE_ERROR == 1 +#define IAP_ERRORF(fmt, ...) logf("%lu " fmt, iap_debug_timestamp() __VA_OPT__(, __VA_ARGS__)) +#endif + +#define IAP_ARTWORK_WIDTH 128 +#define IAP_ARTWORK_HEIGHT 128 +#define IAP_COLOR_ARTWORK iap_true diff --git a/firmware/usbstack/iap/platform.c b/firmware/usbstack/iap/platform.c new file mode 100644 index 0000000000..1e5ce22a9e --- /dev/null +++ b/firmware/usbstack/iap/platform.c @@ -0,0 +1,482 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "audio.h" +#include "buffering.h" +#include "core_alloc.h" +#include "metadata.h" +#include "misc.h" +#include "pcm_mixer.h" +#include "pcm_sink.h" +#include "playback.h" +#include "playlist.h" +#include "powermgmt.h" +#include "settings.h" +#include "sound.h" +#include "usb_drv.h" + +#include "../usb_iap.h" +#include "debug.h" +#include "libiap/iap.h" +#include "macros.h" +#include "platform.h" + +void* iap_platform_malloc(struct IAPContext* iap_ctx, size_t size, int flags) { + struct Platform* plt = iap_ctx->platform; + for(size_t i = 0; i < ARRAYLEN(plt->malloc_results); i += 1) { + if(plt->malloc_results[i].ptr != NULL) { + continue; + } + struct IAPAllocResult result; + if(flags & IAPPlatformMallocFlags_Uncached) { + check_act(iap_alloc_usb_send_buffer(size, &result), return NULL); + } else { + check_act(iap_alloc_buffer(size, &result), return NULL); + } + plt->malloc_results[i] = result; + return result.ptr; + } + ERROR("no free malloc slot"); + return NULL; +} + +void iap_platform_free(struct IAPContext* iap_ctx, void* ptr) { + struct Platform* plt = iap_ctx->platform; + for(size_t i = 0; i < ARRAYLEN(plt->malloc_results); i += 1) { + if(plt->malloc_results[i].ptr == ptr) { + core_free(plt->malloc_results[i].handle); + plt->malloc_results[i].ptr = NULL; + return; + } + } + ERROR("no matching malloc slot"); +} + +int iap_platform_send_hid_report(struct IAPContext* iap_ctx, const void* ptr, size_t size) { + (void)iap_ctx; +#if DEBUG_DUMP_TX == 1 + logf("==== dev ==== %p %u > %d", ptr, size, HID_EP_IN); + iap_platform_dump_hex(ptr, MIN(size, 48)); +#endif + const int ret = usb_drv_send_nonblocking(HID_EP_IN, (void*)ptr, size); + return ret == 0 ? (int)size : ret; +} + +IAPBool iap_platform_get_ipod_serial_num(struct IAPContext* iap_ctx, struct IAPSpan* serial) { + (void)iap_ctx; + static const char* serial_num = "000000000000"; + return iap_span_append(serial, serial_num, strlen(serial_num) + 1); +} + +IAPBool iap_platform_get_play_status(struct IAPContext* iap_ctx, struct IAPPlatformPlayStatus* status) { + struct Platform* plt = iap_ctx->platform; + + status->state = _iap_convert_play_status(audio_status()); + if(status->state == IAPIPodStatePlayStatus_PlaybackStopped) { + return iap_true; + } + + struct mp3entry* id3 = audio_current_track(); + check_act(id3 != NULL, return iap_false); + status->track_total_ms = id3->length; + status->track_pos_ms = id3->elapsed; + status->track_index = playlist_get_display_index() - 1; + status->track_count = playlist_amount(); + status->track_caps = IAPIPodStateTrackCapBits_HasReleaseDate; + if(plt->aa_slot >= 0 && playback_current_aa_hid(plt->aa_slot) >= 0) { + status->track_caps |= IAPIPodStateTrackCapBits_HasAlbumArts; + } + + return iap_true; +} + +void iap_platform_control(struct IAPContext* iap_ctx, enum IAPPlatformControl control, struct IAPPlatformPendingControl pending) { + struct Platform* plt = iap_ctx->platform; + + long button = BUTTON_NONE; + switch(control) { + case IAPPlatformControl_TogglePlayPause: + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + break; + case IAPPlatformControl_Play: + if(audio_status() != AUDIO_STATUS_PLAY) { + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + } + break; + case IAPPlatformControl_Pause: + if(audio_status() == AUDIO_STATUS_PLAY) { + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + } + break; + case IAPPlatformControl_Stop: + button = BUTTON_MULTIMEDIA_STOP; + break; + case IAPPlatformControl_Next: + button = BUTTON_MULTIMEDIA_NEXT; + break; + case IAPPlatformControl_Prev: + button = BUTTON_MULTIMEDIA_PREV; + break; + case IAPPlatformControl_VolumeUp: + adjust_volume(1); + break; + case IAPPlatformControl_VolumeDown: + adjust_volume(-1); + break; + case IAPPlatformControl_ToggleMute: + /* rockbox has no mute/unmute */ + break; + } + logf("control=%d button=0x%04lX", control, button); + + IAPBool ret = iap_true; + if(button == BUTTON_NONE) { + goto exit; + } + check_act(button_queue_try_post(button, 0), ret = false); + if(ret && button == BUTTON_MULTIMEDIA_PLAYPAUSE && audio_status() == 0) { + /* we are transitioning to stopped to playing, which may take time. + * to maintain synchronization with accessories, do not send an ack until playback actually begins. */ + plt->control_pending = true; + plt->pending_control = pending; + return; + } +exit: + check_act(iap_control_response(iap_ctx, pending, ret), ); +} + +static uint8_t normalize_8(int val, int min, int max) { + return 0xFF * (val - min) / (max - min); +} + +IAPBool iap_platform_get_volume(struct IAPContext* iap_ctx, struct IAPPlatformVolumeStatus* status) { + (void)iap_ctx; + + status->volume = _iap_convert_volume(global_status.volume); + status->muted = iap_false; + return iap_true; +} + +IAPBool iap_platform_get_power_status(struct IAPContext* iap_ctx, struct IAPPlatformPowerStatus* status) { + (void)iap_ctx; + status->battery_level = _iap_convert_battery_level(battery_level()); + status->state = _iap_convert_charge_status(charge_state); + return iap_true; +} + +IAPBool iap_platform_get_shuffle_setting(struct IAPContext* iap_ctx, uint8_t* status) { + (void)iap_ctx; + *status = _iap_convert_shuffle_state(global_settings.playlist_shuffle); + return iap_true; +} + +IAPBool iap_platform_set_shuffle_setting(struct IAPContext* iap_ctx, uint8_t status) { + (void)iap_ctx; + + if(status == IAPIPodStateShuffleSettingState_Tracks && !global_settings.playlist_shuffle) { + global_settings.playlist_shuffle = true; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + playlist_randomise(NULL, current_tick, true); + } + } else if(status == IAPIPodStateShuffleSettingState_Off && global_settings.playlist_shuffle) { + global_settings.playlist_shuffle = false; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + playlist_sort(NULL, true); + } + } + return iap_true; +} + +IAPBool iap_platform_get_repeat_setting(struct IAPContext* iap_ctx, uint8_t* status) { + (void)iap_ctx; + *status = _iap_convert_repeat_state(global_settings.repeat_mode); + return iap_true; +} + +IAPBool iap_platform_set_repeat_setting(struct IAPContext* iap_ctx, uint8_t status) { + (void)iap_ctx; + + /* there are more repeat options in Rockbox rather than iAP. + * e.g. RB Repeat One and AB are both mapped to iAP Repeat One. + * so we should not accept iAP Repeat One as RB Repeat One, if we have + * RB Repeat AB set. */ + if(_iap_convert_repeat_state(global_settings.repeat_mode) == status) { + /* already the same (or equievalent) mode set */ + return iap_true; + } + + static const uint8_t table[] = { + [IAPIPodStateRepeatSettingState_Off] = REPEAT_OFF, + [IAPIPodStateRepeatSettingState_One] = REPEAT_ONE, + [IAPIPodStateRepeatSettingState_All] = REPEAT_ALL, + }; + check_act(status < ARRAY_SIZE(table), return iap_false); + global_settings.repeat_mode = table[status]; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + audio_flush_and_reload_tracks(); + } + return iap_true; +} + +IAPBool iap_platform_get_date_time(struct IAPContext* iap_ctx, struct IAPDateTime* time) { + (void)iap_ctx; + _iap_convert_datetime(get_time(), time); + return iap_true; +} + +IAPBool iap_platform_get_backlight_level(struct IAPContext* iap_ctx, uint8_t* level) { + (void)iap_ctx; + +#ifdef HAVE_BACKLIGHT_BRIGHTNESS + *level = normalize_8(global_settings.brightness, MIN_BRIGHTNESS_SETTING, MAX_BRIGHTNESS_SETTING); +#else + *level = 0xFF; +#endif + return iap_true; +} + +IAPBool iap_platform_get_hold_switch_state(struct IAPContext* iap_ctx, IAPBool* state) { + (void)iap_ctx; + +#ifdef HAS_BUTTON_HOLD + *state = button_hold(); +#else + *state = iap_false; +#endif + return iap_true; +} + +/* taken from iap-core.c */ +static void get_trackinfo(const unsigned int track, struct mp3entry* id3) { + int tracknum = track; + tracknum += playlist_get_first_index(NULL); + if(tracknum >= playlist_amount()) { + tracknum -= playlist_amount(); + } + + if(playlist_next(0) != tracknum) { + struct playlist_track_info info; + playlist_get_track_info(NULL, tracknum, &info); + get_metadata(id3, -1, info.filename); + } else { + memcpy(id3, audio_current_track(), sizeof(*id3)); + } +} + +IAPBool iap_platform_get_indexed_track_info(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformTrackInfo* info) { + struct Platform* plt = iap_ctx->platform; + + struct playlist_track_info track; + struct mp3entry id3; + check_act(playlist_get_track_info(NULL, index, &track) == 0, return iap_false); + get_trackinfo(index, &id3); + + if(info->total_ms != NULL) { + *info->total_ms = id3.length; + } + if(info->caps != NULL) { + *info->caps = IAPIPodStateTrackCapBits_HasReleaseDate; + /* FIXME: respect index */ + if(plt->aa_slot >= 0 && playback_current_aa_hid(plt->aa_slot) >= 0) { + *info->caps |= IAPIPodStateTrackCapBits_HasAlbumArts; + } + } + if(info->release_date != NULL) { + info->release_date->year = id3.year; + info->release_date->month = 0; + info->release_date->day = 0; + info->release_date->hour = 0; + info->release_date->minute = 0; + info->release_date->seconds = 0; + } + if(info->artist != NULL) { + check_act(iap_span_append(info->artist, id3.artist, strlen(id3.artist) + 1), return iap_false); + } + if(info->composer != NULL) { + check_act(iap_span_append(info->composer, id3.composer, strlen(id3.composer) + 1), return iap_false); + } + if(info->album != NULL) { + check_act(iap_span_append(info->album, id3.album, strlen(id3.album) + 1), return iap_false); + } + if(info->title != NULL) { + check_act(iap_span_append(info->title, id3.title, strlen(id3.title) + 1), return iap_false); + } + return iap_true; +} + +IAPBool iap_platform_set_playing_track(struct IAPContext* iap_ctx, uint32_t index) { + (void)iap_ctx; + audio_skip((int)index - playlist_next(0)); + return iap_true; +} + +IAPBool iap_platform_open_artwork(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformArtwork* artwork) { + struct Platform* plt = iap_ctx->platform; + /* only aa for currently playing track is available */ + check_act((int)index == playlist_get_display_index() - 1, return iap_false); + const int hid = playback_current_aa_hid(plt->aa_slot); + check_act(hid >= 0, return iap_false, "%d %d", plt->aa_slot, hid); + struct bitmap* bmp; + check_act(bufgetdata(hid, 0, (void*)&bmp) > 0, return iap_false); + artwork->color = iap_true; + artwork->width = bmp->width; + artwork->height = bmp->height; + artwork->opaque = hid; + return iap_true; +} + +IAPBool iap_platform_get_artwork_ptr(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork, struct IAPSpan* span) { + struct Platform* plt = iap_ctx->platform; + + /* check the albumart has not reloaded */ + /* FIXME: not a correct check due to possibility of hid confliction */ + const int hid = playback_current_aa_hid(plt->aa_slot); + check_act(hid == (int)artwork->opaque, return iap_false); + + struct bitmap* bmp; + /* more checks */ + check_act(bufgetdata(hid, 0, (void*)&bmp) > 0, return iap_false); + check_act(bmp->width == artwork->width && bmp->height == artwork->height, return iap_false); + + span->ptr = bmp->data; + span->size = bmp->width * bmp->height * 2; + return iap_true; +} + +IAPBool iap_platform_close_artwork(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork) { + (void)iap_ctx; + (void)artwork; + return iap_true; +} + +void iap_platform_dump_hex(const void* ptr, size_t size) { + if(ptr == NULL) { + logf("(null)"); + return; + } + +#if DEBUG_HEXDUMP_NOLIMIT != 1 + size = MIN(size, 32); +#endif + + static const char chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + char line[4 * (8 + 1) + 1]; + for(size_t l = 0; l * 16 < size; l += 1) { + char* c = line; + for(size_t b = 0; b < 4; b += 1) { + for(size_t i = 0; i < 4; i += 1) { + size_t index = l * 16 + b * 4 + i; + if(index >= size) { + break; + } + *c++ = chars[((uint8_t*)ptr)[index] >> 4]; + *c++ = chars[((uint8_t*)ptr)[index] & 0xF]; + } + *c++ = ' '; + } + *c++ = '\0'; + logf("%04X: %s", l * 16, line); + } +} + +IAPBool iap_platform_on_acc_samprs_received(struct IAPContext* iap_ctx, struct IAPSpan* samprs) { + (void)iap_ctx; + + bool has_44k = false; + bool has_48k = false; + while(samprs->size > 0) { + uint32_t sample_rate; + check_act(iap_span_read_32(samprs, &sample_rate), return iap_false); + has_44k |= sample_rate == SAMPR_44; + has_48k |= sample_rate == SAMPR_48; + } + check_act(has_44k && has_48k, return iap_false, "accessory lacks mandatory freq support: 44k=%d 48k=%d", has_44k, has_48k); + check_act(mixer_switch_sink(PCM_SINK_IAP), return false); + return iap_true; +} + +uint8_t _iap_convert_play_status(int rb_audio_status) { + if(rb_audio_status & AUDIO_STATUS_PAUSE) { + return IAPIPodStatePlayStatus_PlaybackPaused; + } else if(rb_audio_status & AUDIO_STATUS_PLAY) { + return IAPIPodStatePlayStatus_Playing; + } else { + return IAPIPodStatePlayStatus_PlaybackStopped; + } +} + +uint8_t _iap_convert_volume(int rb_volume) { + return normalize_8(rb_volume, sound_min(SOUND_VOLUME), sound_max(SOUND_VOLUME)); +} + +uint8_t _iap_convert_shuffle_state(bool rb_state) { + return rb_state ? IAPIPodStateShuffleSettingState_Tracks : IAPIPodStateShuffleSettingState_Off; +} + +uint8_t _iap_convert_repeat_state(int rb_state) { + static const uint8_t table[] = { + [REPEAT_OFF] = IAPIPodStateRepeatSettingState_Off, + [REPEAT_ALL] = IAPIPodStateRepeatSettingState_All, + [REPEAT_ONE] = IAPIPodStateRepeatSettingState_One, + [REPEAT_SHUFFLE] = IAPIPodStateRepeatSettingState_All, +#ifdef AB_REPEAT_ENABLE + [REPEAT_AB] = IAPIPodStateRepeatSettingState_One, +#endif + }; + + if(rb_state == REPEAT_OFF && global_settings.next_folder) { + /* when repeat is off and next folder is enabled, report repeat all. + * without this hack, the accessory will likely set repeat to all, + * resulting in trapping us in the current directory. */ + return IAPIPodStateRepeatSettingState_All; + } else { + check_act(rb_state < ARRAY_SIZE(table), return iap_false); + return table[rb_state]; + } +} + +uint8_t _iap_convert_battery_level(int rb_battery_level) { + return 0xFF * rb_battery_level / 100; +} + +uint8_t _iap_convert_charge_status(enum charge_state_type rb_charge_state) { + switch(rb_charge_state) { + case CHARGING: + return IAPIPodStatePowerState_ExternalCharging; + case TOPOFF: + case TRICKLE: + return IAPIPodStatePowerState_ExternalCharged; + break; + default: + return IAPIPodStatePowerState_Internal; + } +} + +void _iap_convert_datetime(struct tm* rb_time, struct IAPDateTime* time) { + time->year = rb_time->tm_year + 1900; + time->month = rb_time->tm_mon + 1; + time->day = rb_time->tm_mday; + time->hour = rb_time->tm_hour; + time->minute = rb_time->tm_min; + time->seconds = rb_time->tm_sec; +} diff --git a/firmware/usbstack/iap/platform.h b/firmware/usbstack/iap/platform.h new file mode 100644 index 0000000000..2707722b04 --- /dev/null +++ b/firmware/usbstack/iap/platform.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include + +#include "libiap/datetime.h" +#include "libiap/platform.h" +#include "powermgmt.h" +#include "time.h" + +#include "buffer.h" + +/* usb_iap.c */ +struct IAPContext* _iap_acquire_ctx(bool lock); +void _iap_release_ctx(void); + +struct Platform { + struct IAPAllocResult malloc_results[4]; /* allow up to 4 mallocs */ + struct IAPPlatformPendingControl pending_control; + bool control_pending; + + int aa_slot; +}; + +/* helper functions */ +uint8_t _iap_convert_play_status(int rb_audio_status); +uint8_t _iap_convert_volume(int rb_volume); +uint8_t _iap_convert_shuffle_state(bool rb_state); +uint8_t _iap_convert_repeat_state(int rb_state); +uint8_t _iap_convert_battery_level(int rb_battery_level); +uint8_t _iap_convert_charge_status(enum charge_state_type rb_charge_state); +void _iap_convert_datetime(struct tm* rb_time, struct IAPDateTime* time); diff --git a/firmware/usbstack/usb_audio.c b/firmware/usbstack/usb_audio.c index fd135cf7a3..2b633e34b1 100644 --- a/firmware/usbstack/usb_audio.c +++ b/firmware/usbstack/usb_audio.c @@ -297,15 +297,15 @@ static int usb_as_playback_intf_alt; /* playback streaming interface alternate s static int as_playback_freq_idx; /* audio playback streaming frequency index (in hw_freq_sampr) */ -struct usb_class_driver_ep_allocation usb_audio_ep_allocs[2] = { +static struct usb_class_driver_ep_allocation ep_allocs[2] = { /* output isochronous endpoint */ - {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_OUT, .optional = false}, + {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_OUT, .optional = false, .mps = -1}, /* input feedback isochronous endpoint */ - {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_IN, .optional = false}, + {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_IN, .optional = false, .mps = -1}, }; -#define EP_ISO_OUT (usb_audio_ep_allocs[0].ep) -#define EP_ISO_FEEDBACK_IN (usb_audio_ep_allocs[1].ep) +#define EP_ISO_OUT (ep_allocs[0].ep) +#define EP_ISO_FEEDBACK_IN (ep_allocs[1].ep) /* small buffer used for control transfers */ static unsigned char usb_buffer[128] USB_DEVBSS_ATTR; @@ -501,7 +501,12 @@ unsigned long usb_audio_get_playback_sampling_frequency(void) return hw_freq_sampr[as_playback_freq_idx]; } -void usb_audio_init(void) +/* + * Initialize the driver. Called by usb_core_init(). + * Currently initializes the sampling frequency values available + * to the AudioStreaming interface. + */ +static void usb_audio_init(void) { unsigned int i; /* initialized tSamFreq array */ @@ -577,14 +582,34 @@ unsigned int usb_audio_get_in_ep(void) return EP_ISO_FEEDBACK_IN; } -int usb_audio_set_first_interface(int interface) +/* + * Required function for the class driver. + * + * Called by allocate_interfaces_and_endpoints() to + * tell the class driver what its first interface number is. + * Returns the number of the interface available for the next + * class driver to use. + * + * We need 2 interfaces, AudioControl and AudioStreaming. + * Return interface+2. + */ +static int usb_audio_set_first_interface(int interface) { usb_interface = interface; logf("usbaudio: usb_interface=%d", usb_interface); return interface + 2; /* Audio Control and Audio Streaming */ } -int usb_audio_get_config_descriptor(unsigned char *dest, int max_packet_size) +/* + * Required function for the class driver. + * + * Called by request_handler_device_get_descriptor(), which expects + * this function to fill *dest with the configuration descriptor for this + * class driver. + * + * Return the size of this descriptor in bytes. + */ +static int usb_audio_get_config_descriptor(unsigned char *dest, int max_packet_size) { (void)max_packet_size; unsigned int i; @@ -721,7 +746,13 @@ static void usb_audio_stop_playback(void) send_fb = false; } -int usb_audio_set_interface(int intf, int alt) +/* + * Called by control_request_handler_drivers(). + * Deal with changing the interface between control and streaming. + * + * Return 0 for success, -1 otherwise. + */ +static int usb_audio_set_interface(int intf, int alt) { if(intf == usb_interface) { @@ -757,7 +788,13 @@ int usb_audio_set_interface(int intf, int alt) } } -int usb_audio_get_interface(int intf) +/* + * Called by control_request_handler_drivers(). + * Get the alternate of the given interface. + * + * Return the alternate of the given interface, -1 if unknown. + */ +static int usb_audio_get_interface(int intf) { if(intf == usb_interface) { @@ -1139,7 +1176,13 @@ static bool usb_audio_interface_request(struct usb_ctrlrequest* req, void *reqda } } -bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsigned char* dest) +/* + * Called by control_request_handler_drivers(). + * Pass control requests down to the appropriate functions. + * + * Return true if this driver handles the request, false otherwise. + */ +static bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsigned char* dest) { (void) reqdata; (void) dest; @@ -1156,14 +1199,18 @@ bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsig } } -void usb_audio_init_connection(void) +/* + * Called by usb_core_do_set_config() when the + * connection is ready to be used. Currently just sets + * the audio sample rate to default. + */ +static int usb_audio_init_connection(void) { logf("usbaudio: init connection"); // make sure we can get the buffers first... - // TODO: disable this driver when failed if (usb_audio_request_buf()) - return; + return -1; usbaudio_active = true; dsp = dsp_get_config(CODEC_IDX_AUDIO); @@ -1179,9 +1226,16 @@ void usb_audio_init_connection(void) set_playback_sampling_frequency(HW_SAMPR_DEFAULT); tmp_saved_vol = sound_current(SOUND_VOLUME); usb_audio_playing = false; + return 0; } -void usb_audio_disconnect(void) +/* + * Called by usb_core_exit() AND usb_core_do_set_config(). + * + * Indicates to the Class driver that the connection is no + * longer active. Currently just calls usb_audio_stop_playback(). + */ +static void usb_audio_disconnect(void) { logf("usbaudio: disconnect"); @@ -1258,7 +1312,12 @@ int usb_audio_get_frames_dropped(void) return frames_dropped; } -void usb_audio_transfer_complete(int ep, int dir, int status, int length) +/* + * Dummy function. + * + * The fast_transfer_complete() function needs to be used instead. + */ +static void usb_audio_transfer_complete(int ep, int dir, int status, int length) { /* normal handler is too slow to handle the completion rate, because * of the low thread schedule rate */ @@ -1268,7 +1327,14 @@ void usb_audio_transfer_complete(int ep, int dir, int status, int length) (void) length; } -bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) +/* + * Called by usb_core_transfer_complete(). + * The normal transfer complete handler system is too slow to deal with + * ISO data at the rate required, so this is required. + * + * Return true if the transfer is handled, false otherwise. + */ +static bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) { (void) dir; bool retval = false; @@ -1428,3 +1494,21 @@ bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) return retval; } + +struct usb_class_driver usb_cdrv_audio = { + .needs_exclusive_storage = false, + .needs_cpu_boost = false, + .config = 1, + .ep_allocs_size = ARRAYLEN(ep_allocs), + .ep_allocs = ep_allocs, + .set_first_interface = usb_audio_set_first_interface, + .get_config_descriptor = usb_audio_get_config_descriptor, + .init_connection = usb_audio_init_connection, + .init = usb_audio_init, + .disconnect = usb_audio_disconnect, + .transfer_complete = usb_audio_transfer_complete, + .fast_transfer_complete = usb_audio_fast_transfer_complete, + .control_request = usb_audio_control_request, + .set_interface = usb_audio_set_interface, + .get_interface = usb_audio_get_interface, +}; diff --git a/firmware/usbstack/usb_audio.h b/firmware/usbstack/usb_audio.h index b6cd2461d7..a610fbe90a 100644 --- a/firmware/usbstack/usb_audio.h +++ b/firmware/usbstack/usb_audio.h @@ -30,64 +30,6 @@ * Relevant specifications are USB 2.0 and USB Audio Class 1.0. */ -extern struct usb_class_driver_ep_allocation usb_audio_ep_allocs[2]; - -/* - * usb_audio_set_first_interface(): - * - * Required function for the class driver. - * - * Called by allocate_interfaces_and_endpoints() to - * tell the class driver what its first interface number is. - * Returns the number of the interface available for the next - * class driver to use. - * - * We need 2 interfaces, AudioControl and AudioStreaming. - * Return interface+2. - */ -int usb_audio_set_first_interface(int interface); - -/* - * usb_audio_get_config_descriptor(): - * - * Required function for the class driver. - * - * Called by request_handler_device_get_descriptor(), which expects - * this function to fill *dest with the configuration descriptor for this - * class driver. - * - * Return the size of this descriptor in bytes. - */ -int usb_audio_get_config_descriptor(unsigned char *dest,int max_packet_size); - -/* - * usb_audio_init_connection(): - * - * Called by usb_core_do_set_config() when the - * connection is ready to be used. Currently just sets - * the audio sample rate to default. - */ -void usb_audio_init_connection(void); - -/* - * usb_audio_init(): - * - * Initialize the driver. Called by usb_core_init(). - * Currently initializes the sampling frequency values available - * to the AudioStreaming interface. - */ -void usb_audio_init(void); - -/* - * usb_audio_disconnect(): - * - * Called by usb_core_exit() AND usb_core_do_set_config(). - * - * Indicates to the Class driver that the connection is no - * longer active. Currently just calls usb_audio_stop_playback(). - */ -void usb_audio_disconnect(void); - /* * usb_audio_get_playing(): * @@ -103,56 +45,6 @@ bool usb_audio_get_playing(void); */ bool usb_audio_get_alloc_failed(void); -/* - * usb_audio_transfer_complete(): - * - * Dummy function. - * - * The fast_transfer_complete() function needs to be used instead. - */ -void usb_audio_transfer_complete(int ep,int dir, int status, int length); - -/* - * usb_audio_fast_transfer_complete(): - * - * Called by usb_core_transfer_complete(). - * The normal transfer complete handler system is too slow to deal with - * ISO data at the rate required, so this is required. - * - * Return true if the transfer is handled, false otherwise. - */ -bool usb_audio_fast_transfer_complete(int ep,int dir, int status, int length); - -/* - * usb_audio_control_request(): - * - * Called by control_request_handler_drivers(). - * Pass control requests down to the appropriate functions. - * - * Return true if this driver handles the request, false otherwise. - */ -bool usb_audio_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); - -/* - * usb_audio_set_interface(): - * - * Called by control_request_handler_drivers(). - * Deal with changing the interface between control and streaming. - * - * Return 0 for success, -1 otherwise. - */ -int usb_audio_set_interface(int intf, int alt); - -/* - * usb_audio_get_interface(): - * - * Called by control_request_handler_drivers(). - * Get the alternate of the given interface. - * - * Return the alternate of the given interface, -1 if unknown. - */ -int usb_audio_get_interface(int intf); - /* * usb_audio_get_playback_sampling_frequency(): * @@ -266,4 +158,6 @@ int usb_audio_get_cur_volume(void); bool usb_audio_get_active(void); +extern struct usb_class_driver usb_cdrv_audio; + #endif diff --git a/firmware/usbstack/usb_charging_only.c b/firmware/usbstack/usb_charging_only.c index 3fa384cfec..66683593db 100644 --- a/firmware/usbstack/usb_charging_only.c +++ b/firmware/usbstack/usb_charging_only.c @@ -46,13 +46,13 @@ static struct usb_interface_descriptor __attribute__((aligned(2))) static int usb_interface; -int usb_charging_only_set_first_interface(int interface) +static int usb_charging_only_set_first_interface(int interface) { usb_interface = interface; return interface + 1; } -int usb_charging_only_get_config_descriptor(unsigned char *dest,int max_packet_size) +static int usb_charging_only_get_config_descriptor(unsigned char *dest,int max_packet_size) { (void)max_packet_size; unsigned char *orig_dest = dest; @@ -62,3 +62,11 @@ int usb_charging_only_get_config_descriptor(unsigned char *dest,int max_packet_s return (dest-orig_dest); } + +struct usb_class_driver usb_cdrv_charging_only = { + .needs_exclusive_storage = false, + .needs_cpu_boost = false, + .config = 1, + .set_first_interface = usb_charging_only_set_first_interface, + .get_config_descriptor = usb_charging_only_get_config_descriptor, +}; diff --git a/firmware/usbstack/usb_charging_only.h b/firmware/usbstack/usb_charging_only.h index 68b60956e2..1c49c9788c 100644 --- a/firmware/usbstack/usb_charging_only.h +++ b/firmware/usbstack/usb_charging_only.h @@ -21,12 +21,8 @@ #ifndef USB_CHARGING_ONLY_H #define USB_CHARGING_ONLY_H -#include "usb_ch9.h" +#include "usb_class_driver.h" -void usb_charging_only_init(void); -int usb_charging_only_set_first_interface(int interface); -int usb_charging_only_get_config_descriptor(unsigned char *dest,int max_packet_size); -bool usb_charging_only_control_request(struct usb_ctrlrequest* req); +extern struct usb_class_driver usb_cdrv_charging_only; #endif - diff --git a/firmware/usbstack/usb_class_driver.h b/firmware/usbstack/usb_class_driver.h index f26324fe20..b6e5c16941 100644 --- a/firmware/usbstack/usb_class_driver.h +++ b/firmware/usbstack/usb_class_driver.h @@ -30,15 +30,23 @@ /* Common api, implemented by all class drivers */ struct usb_class_driver_ep_allocation { - uint8_t type; /* by driver, required ep type. USB_ENDPOINT_XFER_* */ - uint8_t dir; /* by driver, required ep dir. DIR_{IN,OUT} */ - uint8_t ep; /* by core, allocated ep. > 0 are valid but can be 0 if optional==true */ - bool optional; /* by driver, set true to mark this requirement to be optional */ + uint8_t ep; /* by core, allocated ep. > 0 are valid but can be 0 if optional==true */ + uint8_t type:2; /* by driver, required ep type. USB_ENDPOINT_XFER_* */ + uint8_t dir:1; /* by driver, required ep dir. DIR_{IN,OUT} */ + bool optional:1; /* by driver, set true to mark this requirement to be optional */ + int16_t mps; /* by driver, desired max packet size, or -1 for device driver default */ }; struct usb_class_driver { /* First some runtime data */ + + /* there are three possible runtime states for class driver: + * !enabled -> disabled, invisible from host + * enabled && !error -> enabled, healthy + * enabled && error -> recognized by host, but failed */ bool enabled; + bool error; + int first_interface; int last_interface; @@ -47,6 +55,9 @@ struct usb_class_driver { /* Set this to true if the driver needs exclusive disk access (e.g. usb storage) */ bool needs_exclusive_storage; + /* Set this to true if the driver needs to enable cpu boost */ + bool needs_cpu_boost; + /* USB config number this driver belongs to */ uint8_t config; @@ -69,8 +80,9 @@ struct usb_class_driver { /* Tells the driver that a usb connection has been set up and is now ready to use. + Returns 0 on success and -1 on error. Optional function */ - void (*init_connection)(void); + int (*init_connection)(void); /* Initialises the driver. This can be called multiple times, and should not perform any action that can disturb other threads diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 9b90fbd5ad..4961f653f3 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -28,7 +28,6 @@ #include "usb.h" #include "usb_ch9.h" -#include "usb_drv.h" #include "usb_core.h" #include "usb_class_driver.h" @@ -53,6 +52,16 @@ #include "usb_audio_def.h" // DEBUG #endif +#ifdef USB_ENABLE_IAP +#include "usb_iap.h" +#endif + +/* include order matters, include driver header before usb_drv.h */ +#if CONFIG_USBOTG == USBOTG_DESIGNWARE +#include "usb-designware.h" +#endif +#include "usb_drv.h" + /* TODO: Move target-specific stuff somewhere else (serial number reading) */ #if defined(IPOD_ARCH) && defined(CPU_PP) @@ -72,7 +81,11 @@ #define USB_MAX_CURRENT 500 #endif +#ifdef USB_ENABLE_IAP +#define NUM_CONFIGS 2 +#else #define NUM_CONFIGS 1 +#endif /*-------------------------------------------------------------------------*/ /* USB protocol descriptors: */ @@ -107,7 +120,7 @@ static struct usb_config_descriptor __attribute__((aligned(2))) .bDescriptorType = USB_DT_CONFIG, .wTotalLength = 0, /* will be filled in later */ .bNumInterfaces = 1, - .bConfigurationValue = 1, + .bConfigurationValue = 0, /* will be filled in later */ .iConfiguration = 0, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = (USB_MAX_CURRENT + 1) / 2, /* In 2mA units */ @@ -171,8 +184,6 @@ static int usb_no_host_callback(struct timeout *tmo) } #endif -static int usb_core_num_interfaces[NUM_CONFIGS]; - typedef void (*completion_handler_t)(int ep, int dir, int status, int length); typedef bool (*fast_completion_handler_t)(int ep, int dir, int status, int length); typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata, @@ -191,117 +202,33 @@ struct ep_alloc_state { struct usb_class_driver* owner[2]; }; -static struct ep_alloc_state ep_alloc_states[NUM_CONFIGS][USB_NUM_ENDPOINTS]; +struct config_state { + struct usb_drv_ep_alloc_ctx ep_alloc_ctx; + struct ep_alloc_state ep_alloc_states[USB_NUM_ENDPOINTS]; + uint8_t num_interfaces; +}; -static struct usb_class_driver drivers[USB_NUM_DRIVERS] = +struct config_state config_states[NUM_CONFIGS]; + +static struct usb_class_driver* drivers[USB_NUM_DRIVERS] = { #ifdef USB_ENABLE_STORAGE - [USB_DRIVER_MASS_STORAGE] = { - .enabled = false, - .needs_exclusive_storage = true, - .config = 1, - .first_interface = 0, - .last_interface = 0, - .ep_allocs_size = ARRAYLEN(usb_storage_ep_allocs), - .ep_allocs = usb_storage_ep_allocs, - .set_first_interface = usb_storage_set_first_interface, - .get_config_descriptor = usb_storage_get_config_descriptor, - .init_connection = usb_storage_init_connection, - .init = usb_storage_init, - .disconnect = usb_storage_disconnect, - .transfer_complete = usb_storage_transfer_complete, - .control_request = usb_storage_control_request, -#ifdef HAVE_HOTSWAP - .notify_hotswap = usb_storage_notify_hotswap, -#endif - }, + [USB_DRIVER_MASS_STORAGE] = &usb_cdrv_storage, #endif #ifdef USB_ENABLE_SERIAL - [USB_DRIVER_SERIAL] = { - .enabled = false, - .needs_exclusive_storage = false, - .config = 1, - .first_interface = 0, - .last_interface = 0, - .ep_allocs_size = ARRAYLEN(usb_serial_ep_allocs), - .ep_allocs = usb_serial_ep_allocs, - .set_first_interface = usb_serial_set_first_interface, - .get_config_descriptor = usb_serial_get_config_descriptor, - .init_connection = usb_serial_init_connection, - .init = usb_serial_init, - .disconnect = usb_serial_disconnect, - .transfer_complete = usb_serial_transfer_complete, - .control_request = usb_serial_control_request, -#ifdef HAVE_HOTSWAP - .notify_hotswap = NULL, -#endif - }, + [USB_DRIVER_SERIAL] = &usb_cdrv_serial, #endif #ifdef USB_ENABLE_CHARGING_ONLY - [USB_DRIVER_CHARGING_ONLY] = { - .enabled = false, - .needs_exclusive_storage = false, - .config = 1, - .first_interface = 0, - .last_interface = 0, - .ep_allocs_size = 0, - .ep_allocs = NULL, - .set_first_interface = usb_charging_only_set_first_interface, - .get_config_descriptor = usb_charging_only_get_config_descriptor, - .init_connection = NULL, - .init = NULL, - .disconnect = NULL, - .transfer_complete = NULL, - .control_request = NULL, -#ifdef HAVE_HOTSWAP - .notify_hotswap = NULL, -#endif - }, + [USB_DRIVER_CHARGING_ONLY] = &usb_cdrv_charging_only, #endif #ifdef USB_ENABLE_HID - [USB_DRIVER_HID] = { - .enabled = false, - .needs_exclusive_storage = false, - .config = 1, - .first_interface = 0, - .last_interface = 0, - .ep_allocs_size = ARRAYLEN(usb_hid_ep_allocs), - .ep_allocs = usb_hid_ep_allocs, - .set_first_interface = usb_hid_set_first_interface, - .get_config_descriptor = usb_hid_get_config_descriptor, - .init_connection = usb_hid_init_connection, - .init = usb_hid_init, - .disconnect = usb_hid_disconnect, - .transfer_complete = usb_hid_transfer_complete, - .control_request = usb_hid_control_request, -#ifdef HAVE_HOTSWAP - .notify_hotswap = NULL, -#endif - }, + [USB_DRIVER_HID] = &usb_cdrv_hid, #endif #ifdef USB_ENABLE_AUDIO - [USB_DRIVER_AUDIO] = { - .enabled = false, - .needs_exclusive_storage = false, - .config = 1, - .first_interface = 0, - .last_interface = 0, - .ep_allocs_size = ARRAYLEN(usb_audio_ep_allocs), - .ep_allocs = usb_audio_ep_allocs, - .set_first_interface = usb_audio_set_first_interface, - .get_config_descriptor = usb_audio_get_config_descriptor, - .init_connection = usb_audio_init_connection, - .init = usb_audio_init, - .disconnect = usb_audio_disconnect, - .transfer_complete = usb_audio_transfer_complete, - .fast_transfer_complete = usb_audio_fast_transfer_complete, - .control_request = usb_audio_control_request, -#ifdef HAVE_HOTSWAP - .notify_hotswap = NULL, + [USB_DRIVER_AUDIO] = &usb_cdrv_audio, #endif - .set_interface = usb_audio_set_interface, - .get_interface = usb_audio_get_interface, - }, +#ifdef USB_ENABLE_IAP + [USB_DRIVER_IAP] = &usb_cdrv_iap, #endif }; @@ -318,8 +245,8 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* static unsigned char response_data[256] USB_DEVBSS_ATTR; -#define is_active(driver) ((driver).enabled && (driver).config == usb_config) -#define has_if(driver, interface) ((interface) >= (driver).first_interface && (interface) < (driver).last_interface) +#define is_active(driver) ((driver)->enabled && !(driver)->error && (driver)->config == usb_config) +#define has_if(driver, interface) ((interface) >= (driver)->first_interface && (interface) < (driver)->last_interface) /** NOTE Serial Number * The serial number string is split into two parts: @@ -479,12 +406,15 @@ void usb_core_init(void) /* class driver init functions should be safe to call even if the driver * won't be used. This simplifies other logic (i.e. we don't need to know * yet which drivers will be enabled */ - for(i = 0; i < USB_NUM_DRIVERS; i++) - if(drivers[i].init != NULL) - drivers[i].init(); - - /* clear endpoint allocation state */ - memset(ep_alloc_states, 0, sizeof(ep_alloc_states)); + for(i = 0; i < USB_NUM_DRIVERS; i++) { + drivers[i]->enabled = false; + drivers[i]->error = false; + drivers[i]->first_interface = 0; + drivers[i]->last_interface = 0; + if(drivers[i]->init != NULL) { + drivers[i]->init(); + } + } initialized = true; usb_state = DEFAULT; @@ -536,12 +466,12 @@ void usb_core_handle_transfer_completion( void usb_core_enable_driver(int driver, bool enabled) { - drivers[driver].enabled = enabled; + drivers[driver]->enabled = enabled; } bool usb_core_driver_enabled(int driver) { - return drivers[driver].enabled; + return drivers[driver]->enabled; } #ifdef HAVE_HOTSWAP @@ -549,8 +479,8 @@ void usb_core_hotswap_event(int volume, bool inserted) { int i; for(i = 0; i < USB_NUM_DRIVERS; i++) - if(drivers[i].enabled && drivers[i].notify_hotswap != NULL) - drivers[i].notify_hotswap(volume, inserted); + if(is_active(drivers[i]) && drivers[i]->notify_hotswap != NULL) + drivers[i]->notify_hotswap(volume, inserted); } #endif @@ -617,54 +547,93 @@ static void usb_core_set_serial_function_id(void) int i, id = 0; for(i = 0; i < USB_NUM_DRIVERS; i++) - if(drivers[i].enabled) + if(drivers[i]->enabled) id |= 1 << i; usb_string_iSerial.wString[0] = hex[id]; } /* synchronize endpoint initialization state to allocation state */ -static void init_deinit_endpoints(uint8_t conf_index, bool init) { +static void init_deinit_endpoints(int config, bool init) { for(int epnum = 0; epnum < USB_NUM_ENDPOINTS; epnum += 1) { for(int dir = 0; dir < 2; dir += 1) { - struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum]; - if(alloc->owner[dir] == NULL) { + struct config_state* cstate = &config_states[config - 1]; + struct ep_alloc_state* astate = &cstate->ep_alloc_states[epnum]; + struct usb_class_driver* driver = astate->owner[dir]; + if(driver == NULL) { continue; } int ep = epnum | (dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); - int ret = init ? - usb_drv_init_endpoint(ep, alloc->type[dir], -1) : - usb_drv_deinit_endpoint(ep); - if(ret) { - logf("usb_core: usb_drv_%s_endpoint failed ep=%d dir=%d", init ? "init" : "deinit", epnum, dir); - continue; - } if(init) { - ep_data[epnum].completion_handler[dir] = alloc->owner[dir]->transfer_complete; - ep_data[epnum].fast_completion_handler[dir] = alloc->owner[dir]->fast_transfer_complete; - ep_data[epnum].control_handler[dir] = alloc->owner[dir]->control_request; + usb_drv_ep_init(&cstate->ep_alloc_ctx, ep); + ep_data[epnum].completion_handler[dir] = driver->transfer_complete; + ep_data[epnum].fast_completion_handler[dir] = driver->fast_transfer_complete; + ep_data[epnum].control_handler[dir] = driver->control_request; + } else { + usb_drv_ep_deinit(&cstate->ep_alloc_ctx, ep); } } } } +#ifndef usb_drv_ep_alloc_ctx +/* default endpoint allocator using usb_drv_ep_specs table */ +static void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx) { + for(int i = 0; i < USB_NUM_ENDPOINTS; i += 1) { + ctx->type[i][0] = -1; + ctx->type[i][1] = -1; + } +} + +static bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size) { + const uint8_t epnum = EP_NUM(ep); + const uint8_t epdir = EP_DIR(ep); + + struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum]; + + const int8_t spec_type = spec->type[epdir]; + if(spec_type != type && spec_type != USB_ENDPOINT_TYPE_ANY) { + return false; + } + + const int8_t other_type = ctx->type[epnum][!epdir]; + if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE && other_type != -1) { + /* the other side is allocated */ + return false; + } + + if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH && other_type != -1 && other_type != type) { + /* the other side is allocated with another type */ + return false; + } + + ctx->type[epnum][epdir] = type; + ctx->max_packet_size[epnum][epdir] = max_packet_size; + + return true; +} +#endif + static void allocate_interfaces_and_endpoints(void) { if(usb_config != 0) { /* deinit currently used endpoints */ - init_deinit_endpoints(usb_config - 1, false); + init_deinit_endpoints(usb_config, false); } +retry: /* reset allocations */ - memset(ep_alloc_states, 0, sizeof(ep_alloc_states)); - - int interface[NUM_CONFIGS] = {0}; + for(int i = 0; i < NUM_CONFIGS; i += 1) { + config_states[i].num_interfaces = 0; + memset(config_states[i].ep_alloc_states, 0, sizeof(config_states[i].ep_alloc_states)); + usb_drv_ep_reset_alloc_ctx(&config_states[i].ep_alloc_ctx); + } for(int i = 0; i < USB_NUM_DRIVERS; i++) { - struct usb_class_driver* driver = &drivers[i]; - const uint8_t conf_index = driver->config - 1; + struct usb_class_driver* driver = drivers[i]; + struct config_state* cstate = &config_states[driver->config - 1]; - if(!driver->enabled) { + if(!driver->enabled || driver->error) { continue; } @@ -674,69 +643,37 @@ static void allocate_interfaces_and_endpoints(void) struct usb_class_driver_ep_allocation* req = &driver->ep_allocs[reqnum]; req->ep = 0; for(int epnum = 1; epnum < USB_NUM_ENDPOINTS; epnum += 1) { - struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum]; - /* ep type check */ - const int8_t spec_type = spec->type[req->dir]; - if(spec_type != req->type && spec_type != USB_ENDPOINT_TYPE_ANY) { - continue; - } /* free check */ - struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum]; + struct ep_alloc_state* alloc = &cstate->ep_alloc_states[epnum]; if(alloc->owner[req->dir] != NULL) { continue; } - /* this ep's requested direction is free */ - - /* another checks */ - if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE) { - /* check for the other direction type */ - if(alloc->owner[!req->dir] != NULL) { - /* the other side is allocated */ - continue; - } - } - if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH) { - /* check for other direction type */ - if(alloc->owner[!req->dir] != NULL && alloc->type[!req->dir] != req->type) { - /* the other side is allocated with another type */ - continue; - } + /* driver specific check */ + const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); + if(!usb_drv_ep_allocate(&cstate->ep_alloc_ctx, ep, req->type, req->mps)) { + continue; } /* all checks passed, assign it */ - const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); req->ep = ep; alloc->owner[req->dir] = driver; alloc->type[req->dir] = req->type; break; } if(req->ep == 0 && !req->optional) { - /* no matching ep found, disable the driver */ + /* no matching ep found, retry allocation excluding this driver */ + logf("usb_core: no endpoint allocated for driver %d", i); driver->enabled = false; - /* also revert all allocations for this driver */ - for(reqnum = reqnum - 1; reqnum >= 0; reqnum -= 1) { - const uint8_t ep = driver->ep_allocs[reqnum].ep; - const uint8_t epnum = EP_NUM(ep); - const uint8_t epdir = EP_DIR(ep); - const uint8_t dir = epdir == USB_DIR_OUT ? DIR_OUT : DIR_IN; - ep_alloc_states[conf_index][epnum].owner[dir] = NULL; - } - break; + goto retry; } } - if(!driver->enabled) { - continue; - } - /* assign interfaces */ - driver->first_interface = interface[conf_index]; - interface[conf_index] = driver->set_first_interface(interface[conf_index]); - driver->last_interface = interface[conf_index]; + driver->first_interface = cstate->num_interfaces; + cstate->num_interfaces = driver->set_first_interface(cstate->num_interfaces); + driver->last_interface = cstate->num_interfaces; } - - memcpy(usb_core_num_interfaces, interface, sizeof(interface)); } @@ -746,8 +683,8 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r bool handled = false; for(i = 0; i < USB_NUM_DRIVERS; i++) { - struct usb_class_driver* driver = &drivers[i]; - if(!is_active(*driver) || !has_if(*driver, interface) || driver->control_request == NULL) { + struct usb_class_driver* driver = drivers[i]; + if(!is_active(driver) || !has_if(driver, interface) || driver->control_request == NULL) { continue; } @@ -765,8 +702,8 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r int alt = -1; logf("usb_core: GET INTERFACE 0x%x", req->wIndex); - if(drivers[i].get_interface) - alt = drivers[i].get_interface(req->wIndex); + if(driver->get_interface) + alt = driver->get_interface(req->wIndex); if(alt >= 0 && alt < 255) { response_data[0] = alt; @@ -831,12 +768,12 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v size = sizeof(struct usb_config_descriptor); for(i = 0; i < USB_NUM_DRIVERS; i++) { - if(drivers[i].enabled && drivers[i].config == index + 1 && drivers[i].get_config_descriptor) { - size += drivers[i].get_config_descriptor(&response_data[size], max_packet_size); + if(drivers[i]->enabled && drivers[i]->config == index + 1 && drivers[i]->get_config_descriptor) { + size += drivers[i]->get_config_descriptor(&response_data[size], max_packet_size); } } - config_descriptor.bNumInterfaces = usb_core_num_interfaces[index]; + config_descriptor.bNumInterfaces = config_states[index].num_interfaces; config_descriptor.bConfigurationValue = index + 1; config_descriptor.wTotalLength = (uint16_t)size; memcpy(&response_data[0], &config_descriptor, sizeof(struct usb_config_descriptor)); @@ -894,6 +831,11 @@ static void usb_core_do_set_addr(uint8_t address) usb_state = ADDRESS; } +#if !defined(HAVE_PRIORITY_SCHEDULING) +#define thread_set_priority(...) +#define thread_get_priority(...) +#endif /* HAVE_PRIORITY_SCHEDULING */ + static int usb_core_do_set_config(uint8_t new_config) { logf("usb_core: SET_CONFIG %d to %d", usb_config, new_config); @@ -906,11 +848,11 @@ static int usb_core_do_set_config(uint8_t new_config) /* deactivate old config */ if(usb_config != 0) { for(int i = 0; i < USB_NUM_DRIVERS; i++) { - if(is_active(drivers[i]) && drivers[i].disconnect != NULL) { - drivers[i].disconnect(); + if(is_active(drivers[i]) && drivers[i]->disconnect != NULL) { + drivers[i]->disconnect(); } } - init_deinit_endpoints(usb_config - 1, false); + init_deinit_endpoints(usb_config, false); /* clear any pending transfer completions, * because they are depend on contents of ep_data */ @@ -923,15 +865,21 @@ static int usb_core_do_set_config(uint8_t new_config) usb_state = usb_config == 0 ? ADDRESS : CONFIGURED; bool require_exclusive = false; + bool require_cpu_boost = false; /* activate new config */ if(usb_config != 0) { - init_deinit_endpoints(usb_config - 1, true); + init_deinit_endpoints(usb_config, 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(!is_active(drivers[i])) { + continue; } + if(drivers[i]->init_connection != NULL && drivers[i]->init_connection() < 0) { + drivers[i]->error = true; + continue; + } + require_exclusive |= drivers[i]->needs_exclusive_storage; + require_cpu_boost |= drivers[i]->needs_cpu_boost; } } @@ -943,6 +891,13 @@ static int usb_core_do_set_config(uint8_t new_config) } else { usb_release_exclusive_storage(); } + if(require_cpu_boost) { + trigger_cpu_boost(); + thread_set_priority(thread_self(), PRIORITY_REALTIME); + } else { + thread_set_priority(thread_self(), PRIORITY_SYSTEM); + cancel_cpu_boost(); + } #ifdef HAVE_USB_CHARGING_ENABLE usb_charging_maxcurrent_change(usb_charging_maxcurrent()); @@ -1007,6 +962,11 @@ static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata) response_data[1] = 0; usb_drv_control_response(USB_CONTROL_ACK, response_data, 2); break; + #ifdef USB_ENABLE_IAP + case USB_REQ_APPLE_SET_AVAIL_CURRENT: + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + break; + #endif default: logf("bad req:desc %d:%d", req->bRequest, req->wValue); usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); @@ -1258,8 +1218,8 @@ void usb_core_handle_notify(long id, intptr_t data) 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); + if(is_active(drivers[index]) && drivers[index]->notify_event != NULL) { + drivers[index]->notify_event(data & 0x00ffffff); } } break; default: diff --git a/firmware/usbstack/usb_hid.c b/firmware/usbstack/usb_hid.c index ee99e29fdc..bd0e590a04 100644 --- a/firmware/usbstack/usb_hid.c +++ b/firmware/usbstack/usb_hid.c @@ -121,11 +121,11 @@ static bool active = false; static bool currently_sending = false; static int usb_interface; -struct usb_class_driver_ep_allocation usb_hid_ep_allocs[1] = { - {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = false}, +static struct usb_class_driver_ep_allocation ep_allocs[1] = { + {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = false, .mps = -1}, }; -#define EP_IN (usb_hid_ep_allocs[0].ep) +#define EP_IN (ep_allocs[0].ep) static void usb_hid_try_send_drv(void); @@ -173,7 +173,7 @@ static void pack_parameter(unsigned char **dest, bool is_signed, bool mark_size, } } -int usb_hid_set_first_interface(int interface) +static int usb_hid_set_first_interface(int interface) { usb_interface = interface; @@ -519,7 +519,7 @@ static void descriptor_hid_get(unsigned char **dest) PACK_DATA(dest, hid_descriptor); } -int usb_hid_get_config_descriptor(unsigned char *dest, int max_packet_size) +static int usb_hid_get_config_descriptor(unsigned char *dest, int max_packet_size) { (void)max_packet_size; @@ -543,15 +543,16 @@ int usb_hid_get_config_descriptor(unsigned char *dest, int max_packet_size) return (int)(dest - orig_dest); } -void usb_hid_init_connection(void) +static int usb_hid_init_connection(void) { logf("hid: init connection"); active = true; currently_sending = false; + return 0; } /* called by usb_core_init() */ -void usb_hid_init(void) +static void usb_hid_init(void) { logf("hid: init"); @@ -565,7 +566,7 @@ void usb_hid_init(void) currently_sending = false; } -void usb_hid_disconnect(void) +static void usb_hid_disconnect(void) { logf("hid: disconnect"); active = false; @@ -573,7 +574,7 @@ void usb_hid_disconnect(void) } /* called by usb_core_transfer_complete() */ -void usb_hid_transfer_complete(int ep, int dir, int status, int length) +static void usb_hid_transfer_complete(int ep, int dir, int status, int length) { (void)ep; (void)length; @@ -678,7 +679,7 @@ static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char* dest) } /* called by usb_core_control_request() */ -bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata, unsigned char *dest) +static bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata, unsigned char *dest) { (void)reqdata; @@ -828,3 +829,18 @@ void usb_hid_send(usage_page_t usage_page, int id) usb_hid_try_send_drv(); } + +struct usb_class_driver usb_cdrv_hid = { + .needs_exclusive_storage = false, + .needs_cpu_boost = false, + .config = 1, + .ep_allocs_size = ARRAYLEN(ep_allocs), + .ep_allocs = ep_allocs, + .set_first_interface = usb_hid_set_first_interface, + .get_config_descriptor = usb_hid_get_config_descriptor, + .init_connection = usb_hid_init_connection, + .init = usb_hid_init, + .disconnect = usb_hid_disconnect, + .transfer_complete = usb_hid_transfer_complete, + .control_request = usb_hid_control_request, +}; diff --git a/firmware/usbstack/usb_hid.h b/firmware/usbstack/usb_hid.h index 379b17d2bf..7a62b4e353 100644 --- a/firmware/usbstack/usb_hid.h +++ b/firmware/usbstack/usb_hid.h @@ -25,17 +25,8 @@ #include "usb_class_driver.h" #include "usb_hid_usage_tables.h" -extern struct usb_class_driver_ep_allocation usb_hid_ep_allocs[1]; - -int usb_hid_set_first_interface(int interface); -int usb_hid_get_config_descriptor(unsigned char *dest, int max_packet_size); -void usb_hid_init_connection(void); -void usb_hid_init(void); -void usb_hid_disconnect(void); -void usb_hid_transfer_complete(int ep, int dir, int status, int length); -bool usb_hid_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); - void usb_hid_send(usage_page_t usage_page, int id); -#endif +extern struct usb_class_driver usb_cdrv_hid; +#endif diff --git a/firmware/usbstack/usb_iap.c b/firmware/usbstack/usb_iap.c new file mode 100644 index 0000000000..df2a0e575c --- /dev/null +++ b/firmware/usbstack/usb_iap.c @@ -0,0 +1,699 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "audio.h" +#include "pcm_mixer.h" +#include "pcm_sink.h" +#include "playback.h" +#include "powermgmt.h" +#include "timefuncs.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "panic.h" + +#include "iap/audio.h" +#include "iap/libiap/iap.h" +#include "iap/libiap/platform.h" +#include "iap/macros.h" +#include "iap/platform-macros.h" +#include "iap/platform.h" +#include "usb_audio_def.h" +#include "usb_class_driver.h" +#include "usb_hid_def.h" +#include "usb_iap.h" + +struct usb_class_driver_ep_allocation usb_iap_ep_allocs[2] = { + /* uac input */ + {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_IN, .optional = false, .mps = 1024}, + /* hid input */ + {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = false, .mps = 64}, +}; + +/* interface 0 (audio control) */ +static struct usb_interface_descriptor ipod_audio_control_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, +}; + +static struct usb_ac_header ipod_audio_control_uac_header = { + .bLength = USB_AC_SIZEOF_HEADER(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_HEADER, + .bcdADC = 0x0100, /* 1.00 */ + .wTotalLength = -1, /* dynamic */ + .bInCollection = 1, + .baInterfaceNr = {-1}, /* dynamic */ +}; + +static struct usb_ac_input_terminal ipod_audio_control_uac_input_terminal = { + .bLength = sizeof(struct usb_ac_input_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_INPUT_TERMINAL, + .bTerminalId = 1, + .wTerminalType = USB_AC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 2, /* ipod_audio_control_uac_output_terminal */ + .bNrChannels = 2, + .wChannelConfig = USB_AC_CHANNELS_LEFT_RIGHT_FRONT, +}; + +static struct usb_ac_output_terminal ipod_audio_control_uac_output_terminal = { + .bLength = sizeof(struct usb_ac_output_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_OUTPUT_TERMINAL, + .bTerminalId = 2, + .wTerminalType = USB_AC_TERMINAL_STREAMING, + .bAssocTerminal = 1, /* ipod_audio_control_uac_input_terminal */ + .bSourceId = 1, +}; + +/* interface 1 (audio stream) */ +/* interface 1 alt 0 */ +static struct usb_interface_descriptor ipod_audio_stream_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, +}; + +/* interface 1 alt 1 */ +static struct usb_interface_descriptor ipod_audio_stream_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, +}; + +static struct usb_as_interface ipod_audio_stream_1_uac_header = { + .bLength = sizeof(struct usb_as_interface), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_GENERAL, + .bTerminalLink = 2, /* ipod_audio_control_uac_output_terminal */ + .bDelay = 1, + .wFormatTag = USB_AS_FORMAT_TYPE_I_PCM, +}; + +/* TODO: remove unsupported freqs */ +static struct usb_as_format_type_i_discrete ipod_audio_stream_1_uac_discrete = { + .bLength = USB_AS_SIZEOF_FORMAT_TYPE_I_DISCRETE(9), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_FORMAT_TYPE, + .bFormatType = USB_AS_FORMAT_TYPE_I, + .bNrChannels = 2, + .bSubframeSize = 2, /* bBitResolution / 8 */ + .bBitResolution = 16, + .bSamFreqType = 9, + .tSamFreq = { + {0x40, 0x1F, 0x00}, /* 8000 */ + {0x11, 0x2B, 0x00}, /* 11025 */ + {0xE0, 0x2E, 0x00}, /* 12000 */ + {0x80, 0x3E, 0x00}, /* 16000 */ + {0x22, 0x56, 0x00}, /* 22050 */ + {0xC0, 0x5D, 0x00}, /* 24000 */ + {0x00, 0x7D, 0x00}, /* 32000 */ + {0x44, 0xAC, 0x00}, /* 44100 */ + {0x80, 0xBB, 0x00}, /* 48000 */ + }, +}; + +/* interface 1 endpoint 0 */ +static struct usb_as_iso_audio_endpoint ipod_audio_stream_1_endpoint = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = -1, /* dynamic */ + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = AS_PACKET_SIZE, + .bInterval = -1, /* dynamic */ + .bRefresh = 0, + .bSynchAddress = 0, +}; + +static struct usb_as_iso_ctrldata_endpoint ipod_audio_stream_1_endpoint_uac = { + .bLength = sizeof(struct usb_as_iso_ctrldata_endpoint), + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_AS_EP_GENERAL, + .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +/* interface 2 (hid) */ +static struct usb_interface_descriptor ipod_hid_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, +}; + +#define INPUT_REPORT(id, count) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x95, count, /* Report Count */ \ + 0x82, 0x02, 0x01 /* Input 0x0102 (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes) */ + +#define OUTPUT_REPORT(id, count) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x95, count, /* Report Count */ \ + 0x92, 0x02, 0x01 /* Output 0x0102 (...) */ + +#define INPUT_REPORT2(id, count1, count2) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x96, count1, count2, /* Report Count */ \ + 0x82, 0x02, 0x01 /* Input 0x0102 (...) */ + +#define OUTPUT_REPORT2(id, count1, count2) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x96, count1, count2, /* Report Count */ \ + 0x92, 0x02, 0x01 /* Output 0x0102 (...) */ + +// clang-format off +static const uint8_t ipod_hid_report_fs[] = { + 0x06, 0x00, 0xFF, /* Usage Page 0xFF00 (Vendor-defined) */ + 0x09, 0x01, /* Usage 0x01 */ + 0xA1, 0x01, /* Collection 0x01 (Application) */ + 0x75, 0x08, /* Report Size 0x08 */ + 0x26, 0x80, 0x00, /* Logical Maximum 0x0081 (128) */ + 0x15, 0x00, /* Logical Minumum 0x0000 (0) */ + + INPUT_REPORT(0x01, 0x0C), + INPUT_REPORT(0x02, 0x0E), + INPUT_REPORT(0x03, 0x14), + INPUT_REPORT(0x04, 0x3F), + + OUTPUT_REPORT(0x05, 0x08), + OUTPUT_REPORT(0x06, 0x0A), + OUTPUT_REPORT(0x07, 0x0E), + OUTPUT_REPORT(0x08, 0x14), + OUTPUT_REPORT(0x09, 0x3F), + + 0xC0, /* End Collection */ +}; + +static const uint8_t ipod_hid_report_hs[] = { + 0x06, 0x00, 0xFF, /* Usage Page 0xFF00 (Vendor-defined) */ + 0x09, 0x01, /* Usage 0x01 */ + 0xA1, 0x01, /* Collection 0x01 (Application) */ + 0x75, 0x08, /* Report Size 0x08 */ + 0x26, 0x80, 0x00, /* Logical Maximum 0x0081 (128) */ + 0x15, 0x00, /* Logical Minumum 0x0000 (0) */ + + INPUT_REPORT(0x01, 0x05), + INPUT_REPORT(0x02, 0x09), + INPUT_REPORT(0x03, 0x0D), + INPUT_REPORT(0x04, 0x11), + INPUT_REPORT(0x05, 0x19), + INPUT_REPORT(0x06, 0x31), + INPUT_REPORT(0x07, 0x5F), + INPUT_REPORT(0x08, 0xC1), + INPUT_REPORT2(0x09, 0x01, 0x01), + INPUT_REPORT2(0x0A, 0x81, 0x01), + INPUT_REPORT2(0x0B, 0x01, 0x02), + INPUT_REPORT2(0x0C, 0xFF, 0x02), + + OUTPUT_REPORT(0x0D, 0x05), + OUTPUT_REPORT(0x0E, 0x09), + OUTPUT_REPORT(0x1F, 0x0D), + OUTPUT_REPORT(0x10, 0x11), + OUTPUT_REPORT(0x11, 0x19), + OUTPUT_REPORT(0x12, 0x31), + OUTPUT_REPORT(0x13, 0x5F), + OUTPUT_REPORT(0x14, 0xC1), + OUTPUT_REPORT(0x15, 0xFF), + + 0xC0, /* End Collection */ +}; +// clang-format on + +static struct usb_hid_descriptor ipod_hid_hid_desc = { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_DT_HID, + .wBcdHID = 0x0111, /* 1.11 */ + .bCountryCode = 0, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DT_REPORT, + .wDescriptorLength0 = -1, /* dynamic */ +}; + +/* interface 2 endpoint 0 */ +static struct usb_endpoint_descriptor ipod_hid_endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = -1, /* dynamic */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = -1, /* dynamic */ + .bInterval = 1, +}; + +static struct { + int interface; +} ctrl; + +static struct { + uint32_t sample_rate; + int interface; + int8_t alt; +} stream; + +static struct { + int interface; +} hid; + +static struct mutex iap_ctx_mutex; +static struct Platform platform; +static struct timeout tick_tmo; +static bool iap_ctx_mutex_initialized = false; + +struct IAPContext* _iap_acquire_ctx(bool lock) { + static struct IAPContext ctx; + if(lock) { + mutex_lock(&iap_ctx_mutex); + } + return &ctx; +} + +void _iap_release_ctx() { + mutex_unlock(&iap_ctx_mutex); +} + +bool iap_initialized; + +/* those notifications need pollling */ +static enum charge_state_type last_charge_state; +static uint8_t last_battery_level; +static int8_t last_minute; +static int8_t last_hold_switch_state; + +enum Notify { + Notify_Tick, +}; + +static int tick_callback(struct timeout* tmo) { + (void)tmo; + usb_signal_class_notify(USB_DRIVER_IAP, Notify_Tick); + return HZ / 10; +} + +static int usb_iap_set_first_interface(int interface) { + ctrl.interface = interface + 0; + stream.interface = interface + 1; + hid.interface = interface + 2; + return interface + 3; +} + +#define PACK_DESC(desc) pack_data(&dest, &desc, ((struct usb_descriptor_header*)&desc)->bLength) + +static int usb_iap_get_config_descriptor(unsigned char* dest, int max_packet_size) { + (void)max_packet_size; + + unsigned char* orig_dest = dest; + + ipod_audio_control_desc.bInterfaceNumber = ctrl.interface; + PACK_DESC(ipod_audio_control_desc); + ipod_audio_control_uac_header.baInterfaceNr[0] = stream.interface; + ipod_audio_control_uac_header.wTotalLength = + sizeof(ipod_audio_control_uac_header) + + sizeof(ipod_audio_control_uac_input_terminal) + + sizeof(ipod_audio_control_uac_output_terminal); + PACK_DESC(ipod_audio_control_uac_header); + PACK_DESC(ipod_audio_control_uac_input_terminal); + PACK_DESC(ipod_audio_control_uac_output_terminal); + + ipod_audio_stream_0_desc.bInterfaceNumber = stream.interface; + ipod_audio_stream_1_desc.bInterfaceNumber = stream.interface; + ipod_audio_stream_1_endpoint.bEndpointAddress = AS_EP_IN; + ipod_audio_stream_1_endpoint.bInterval = usb_drv_port_speed() ? 4 : 1; + PACK_DESC(ipod_audio_stream_0_desc); + PACK_DESC(ipod_audio_stream_1_desc); + PACK_DESC(ipod_audio_stream_1_uac_header); + PACK_DESC(ipod_audio_stream_1_uac_discrete); + PACK_DESC(ipod_audio_stream_1_endpoint); + PACK_DESC(ipod_audio_stream_1_endpoint_uac); + + ipod_hid_desc.bInterfaceNumber = hid.interface; + ipod_hid_endpoint.bEndpointAddress = HID_EP_IN; + if(usb_drv_port_speed()) { + ipod_hid_hid_desc.wDescriptorLength0 = sizeof(ipod_hid_report_hs); + ipod_hid_endpoint.wMaxPacketSize = 64; + } else { + ipod_hid_hid_desc.wDescriptorLength0 = sizeof(ipod_hid_report_fs); + ipod_hid_endpoint.wMaxPacketSize = 64; + } + PACK_DESC(ipod_hid_desc); + PACK_DESC(ipod_hid_hid_desc); + PACK_DESC(ipod_hid_endpoint); + + return dest - orig_dest; +} + +static int usb_iap_init_connection(void) { + stream.sample_rate = 48000; + last_charge_state = -1; + last_minute = -1; + last_hold_switch_state = -1; + + iap_debug_reset_timestamp(); + + /* init audio sink */ + check_act(iap_audio_init(), return -1); + + /* init libiap */ + if(!iap_ctx_mutex_initialized) { + iap_ctx_mutex_initialized = true; + mutex_init(&iap_ctx_mutex); + } + + struct IAPContext* ctx = _iap_acquire_ctx(true); + + const struct IAPOpts opts = { + .usb_highspeed = usb_drv_port_speed(), + .ignore_hid_report_id = iap_true, + .artwork_single_report = iap_false, + .enable_packet_dump = iap_false, + }; + check_act(iap_init_ctx(ctx, opts, &platform), goto cleanup_audio); + _iap_release_ctx(); + + /* prepare artwork */ + struct dim dim = {IAP_ARTWORK_WIDTH, IAP_ARTWORK_HEIGHT}; + platform.aa_slot = playback_claim_aa_slot(&dim); + if(platform.aa_slot < 0) { + ERROR("failed to claim albumart slot"); + } + platform.control_pending = false; + + /* register timer */ + timeout_register(&tick_tmo, tick_callback, HZ / 10, 0); + + iap_initialized = true; + LOG("initialized"); + return 0; + +cleanup_audio: + _iap_release_ctx(); + iap_audio_deinit(); + return -1; +} + +static int usb_iap_set_interface(int intf, int alt) { + LOG("set interface interface=%d alt=%d", intf, alt); + check_act(intf == stream.interface, return -1); + if(alt == 0) { + check_act(iap_audio_disable(), return -1); + } else if(alt == 1) { + check_act(iap_audio_enable(), return -1); + } else { + ERROR("invalid alt %d", alt); + return -1; + } + return 0; +} + +static int usb_iap_get_interface(int intf) { + LOG("get interface interface=%d", intf); + check_act(intf == stream.interface, return -1); + return stream.alt; +} + +static void usb_iap_init(void) { + LOG("init"); +} + +static void usb_iap_disconnect(void) { + iap_initialized = false; + audio_pause(); + mixer_switch_sink(PCM_SINK_BUILTIN); + timeout_cancel(&tick_tmo); + if(platform.aa_slot >= 0) { + playback_release_aa_slot(platform.aa_slot); + } + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_deinit_ctx(ctx), ); + _iap_release_ctx(); + check_act(iap_audio_deinit(), ); + LOG("disconnected"); +} + +static void usb_iap_transfer_complete(int ep, int dir, int status, int length) { + (void)length; + + if((ep | dir) == HID_EP_IN) { + check_act(status == 0, return); +#if DEBUG_DUMP_TX + LOG("ep=%d dir=%d state=%d length=%d", ep, dir, status, length); +#endif + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_notify_send_complete(ctx), ); + _iap_release_ctx(); + } +} + +static bool usb_iap_fast_transfer_complete(int ep, int dir, int status, int length) { + (void)status; + (void)length; + return (ep | dir) == AS_EP_IN; +} + +static unsigned char ctrl_buf[256] USB_DEVBSS_ATTR; + +static void respond_zero(struct usb_ctrlrequest* req) { + if(req->wLength > sizeof(ctrl_buf)) { + ERROR("required data too long %u > %u", req->wLength, sizeof(ctrl_buf)); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + } else { + memset(ctrl_buf, 0, req->wLength); + usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength); + } +} + +/* returns true when ctrl_buf has received data */ +static bool receive_data(struct usb_ctrlrequest* req, void* reqdata) { + if(reqdata == NULL) { + /* setup */ + if(req->wLength > sizeof(ctrl_buf)) { + ERROR("parameter too long"); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + } else { + usb_drv_control_response(USB_CONTROL_RECEIVE, ctrl_buf, req->wLength); + } + return false; + } else { + /* data */ + return true; + } +} + +static bool control_request_if_std(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)reqdata; + + unsigned char* const orig_dest = dest; + switch(req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: { + const uint8_t desc_type = req->wValue >> 8; + const uint8_t desc_index = req->wValue & 0xff; + LOG("descriptor request type=%x index=%x", desc_type, desc_index); + (void)desc_index; + switch(desc_type) { + case USB_DT_HID: + PACK_DATA(&dest, ipod_hid_hid_desc); + break; + case USB_DT_REPORT: + if(usb_drv_port_speed()) { + PACK_DATA(&dest, ipod_hid_report_hs); + } else { + PACK_DATA(&dest, ipod_hid_report_fs); + } + break; + } + if(dest != orig_dest) { + usb_drv_control_response(USB_CONTROL_ACK, orig_dest, MIN(dest - orig_dest, req->wLength)); + return true; + } + } break; + } + return false; +} + +static bool control_request_if_class(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)dest; + + const uint8_t recip_interface = req->wIndex & 0xff; + if(recip_interface == hid.interface) { + switch(req->bRequest) { + case USB_HID_GET_REPORT: + respond_zero(req); + return true; + case USB_HID_SET_REPORT: { + if(!receive_data(req, reqdata)) { + return true; + } +#if DEBUG_DUMP_RX == 1 + logf("==== acc: %u bytes ====", req->wLength); + iap_platform_dump_hex(reqdata, req->wLength); +#endif + + struct IAPContext* ctx = _iap_acquire_ctx(true); + const bool ret = iap_feed_hid_report(ctx, reqdata, req->wLength); + _iap_release_ctx(); + + check_act(ret, return false); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + } + case USB_HID_SET_IDLE: + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + } + } + return false; +} + +static bool control_request_if_endpoint(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)dest; + + LOG("ctrl to endpoint %x (stream=%x, hid=%x)", req->wIndex, AS_EP_IN, HID_EP_IN); + if(req->wIndex == AS_EP_IN) { + const uint8_t recip_entity = req->wIndex >> 8; + const uint8_t control_selector = req->wValue >> 8; + (void)recip_entity; + switch(req->bRequest) { + case USB_AC_SET_CUR: + if(!receive_data(req, reqdata)) { + return true; + } + LOG("audio ctrl to stream endpoint entity=0x%02X request=0x%02X length=%u", recip_entity, req->bRequest, req->wLength); + switch(control_selector) { + case USB_AS_EP_CS_SAMPLING_FREQ_CTL: + check_act(req->wLength == 3, goto stall); + stream.sample_rate = ctrl_buf[0] | (ctrl_buf[1] << 8) | (ctrl_buf[2] << 16); + LOG("audio stream sampling rate %lu", stream.sample_rate); + check_act(iap_audio_set_sampr(stream.sample_rate), goto stall); + break; + } + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + case USB_AC_GET_CUR: + switch(control_selector) { + case USB_AS_EP_CS_SAMPLING_FREQ_CTL: + check_act(req->wLength == 3, goto stall); + ctrl_buf[2] = (stream.sample_rate >> 16) & 0xff; + ctrl_buf[1] = (stream.sample_rate >> 8) & 0xff; + ctrl_buf[0] = (stream.sample_rate & 0xff); + usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength); + return true; + } + /* fallthrough */ + case USB_AC_GET_MIN: + case USB_AC_GET_MAX: + case USB_AC_GET_RES: + respond_zero(req); + return true; + stall: + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + return true; + } + } + return false; +} + +static bool usb_iap_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + const uint8_t req_recipient = req->bRequestType & USB_RECIP_MASK; + const uint8_t req_type = req->bRequestType & USB_TYPE_MASK; +#if 0 + LOG("bRequestType=%x, bRequest=%x, wValue=%x, wIndex=%x, wLength=%x", req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength); + LOG("recip=%x type=%x", req_recipient, req_type); +#endif + if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_STANDARD) { + return control_request_if_std(req, reqdata, dest); + } else if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_CLASS) { + return control_request_if_class(req, reqdata, dest); + } else if(req_recipient == USB_RECIP_ENDPOINT && req_type == USB_TYPE_CLASS) { + return control_request_if_endpoint(req, reqdata, dest); + } + return false; +} + +static void usb_iap_notify_event(intptr_t data) { + switch(data) { + case Notify_Tick: { + struct IAPContext* ctx = _iap_acquire_ctx(true); + struct Platform* plt = ctx->platform; + if(plt->control_pending) { + /* waiting for playback begins */ + _iap_release_ctx(); + return; + } + + if(last_charge_state < 0 || last_charge_state != charge_state || last_battery_level != battery_level()) { + last_charge_state = charge_state; + last_battery_level = battery_level(); + iap_notify_power_state(ctx, _iap_convert_charge_status(last_charge_state), _iap_convert_battery_level(last_battery_level)); + } + + struct tm* tm = get_time(); + if(last_minute == -1 || last_minute != tm->tm_min) { + last_minute = tm->tm_min; + struct IAPDateTime time; + _iap_convert_datetime(get_time(), &time); + iap_notify_time_setting(ctx, &time); + } + +#ifdef HAS_BUTTON_HOLD + int8_t hold = button_hold() ? 1 : 0; + if(hold != last_hold_switch_state) { + last_hold_switch_state = hold; + iap_notify_hold_switch_state(ctx, last_hold_switch_state); + } +#endif + + check_act(iap_periodic_tick(ctx), ); + _iap_release_ctx(); + } break; + } +} + +struct usb_class_driver usb_cdrv_iap = { + .needs_exclusive_storage = false, + .needs_cpu_boost = true, + .config = 2, + .ep_allocs_size = ARRAYLEN(usb_iap_ep_allocs), + .ep_allocs = usb_iap_ep_allocs, + .set_first_interface = usb_iap_set_first_interface, + .get_config_descriptor = usb_iap_get_config_descriptor, + .init_connection = usb_iap_init_connection, + .init = usb_iap_init, + .disconnect = usb_iap_disconnect, + .transfer_complete = usb_iap_transfer_complete, + .fast_transfer_complete = usb_iap_fast_transfer_complete, + .control_request = usb_iap_control_request, + .set_interface = usb_iap_set_interface, + .get_interface = usb_iap_get_interface, + .notify_event = usb_iap_notify_event, +}; diff --git a/firmware/usbstack/usb_iap.h b/firmware/usbstack/usb_iap.h new file mode 100644 index 0000000000..dcf1336f2d --- /dev/null +++ b/firmware/usbstack/usb_iap.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include "usb_class_driver.h" + +/* [2] P.32 Table 2-8 USB Device Vendor Request to set available current from accessory (USB Device Mode only) */ +#define USB_REQ_APPLE_SET_AVAIL_CURRENT (0x40) + +extern struct usb_class_driver_ep_allocation usb_iap_ep_allocs[2]; + +extern struct usb_class_driver usb_cdrv_iap; diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c index 5c3b084b4b..3dbf41828b 100644 --- a/firmware/usbstack/usb_serial.c +++ b/firmware/usbstack/usb_serial.c @@ -206,26 +206,26 @@ static int buffer_length; static int buffer_transitlength; static bool active = false; -struct usb_class_driver_ep_allocation usb_serial_ep_allocs[3] = { - {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_IN, .optional = false}, - {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_OUT, .optional = false}, - {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = true}, +static struct usb_class_driver_ep_allocation ep_allocs[3] = { + {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_IN, .optional = false, .mps = -1}, + {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_OUT, .optional = false, .mps = -1}, + {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = true, .mps = -1}, }; -#define EP_IN (usb_serial_ep_allocs[0].ep) -#define EP_OUT (usb_serial_ep_allocs[1].ep) -#define EP_INT (usb_serial_ep_allocs[2].ep) +#define EP_IN (ep_allocs[0].ep) +#define EP_OUT (ep_allocs[1].ep) +#define EP_INT (ep_allocs[2].ep) static int control_interface, data_interface; -int usb_serial_set_first_interface(int interface) +static int usb_serial_set_first_interface(int interface) { control_interface = interface; data_interface = interface + 1; return interface + 2; } -int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) +static int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) { unsigned char *orig_dest = dest; @@ -270,7 +270,7 @@ int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) } /* called by usb_core_control_request() */ -bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) +static bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { bool handled = false; @@ -329,7 +329,7 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsi return handled; } -void usb_serial_init_connection(void) +static int usb_serial_init_connection(void) { /* prime rx endpoint */ usb_drv_recv_nonblocking(EP_OUT, receive_buffer, RECV_BUFFER_SIZE); @@ -341,10 +341,11 @@ void usb_serial_init_connection(void) sendout(); } active=true; + return 0; } /* called by usb_code_init() */ -void usb_serial_init(void) +static void usb_serial_init(void) { logf("serial: init"); buffer_start = 0; @@ -352,7 +353,7 @@ void usb_serial_init(void) buffer_transitlength = 0; } -void usb_serial_disconnect(void) +static void usb_serial_disconnect(void) { active = false; } @@ -412,7 +413,7 @@ void usb_serial_send(const unsigned char *data,int length) } /* called by usb_core_transfer_complete() */ -void usb_serial_transfer_complete(int ep,int dir, int status, int length) +static void usb_serial_transfer_complete(int ep,int dir, int status, int length) { (void)ep; (void)length; @@ -441,3 +442,18 @@ void usb_serial_transfer_complete(int ep,int dir, int status, int length) break; } } + +struct usb_class_driver usb_cdrv_serial = { + .needs_exclusive_storage = false, + .needs_cpu_boost = false, + .config = 1, + .ep_allocs_size = ARRAYLEN(ep_allocs), + .ep_allocs = ep_allocs, + .set_first_interface = usb_serial_set_first_interface, + .get_config_descriptor = usb_serial_get_config_descriptor, + .init_connection = usb_serial_init_connection, + .init = usb_serial_init, + .disconnect = usb_serial_disconnect, + .transfer_complete = usb_serial_transfer_complete, + .control_request = usb_serial_control_request, +}; diff --git a/firmware/usbstack/usb_serial.h b/firmware/usbstack/usb_serial.h index e78273cb8e..a71e91b920 100644 --- a/firmware/usbstack/usb_serial.h +++ b/firmware/usbstack/usb_serial.h @@ -23,17 +23,8 @@ #include "usb_class_driver.h" -extern struct usb_class_driver_ep_allocation usb_serial_ep_allocs[3]; - -int usb_serial_set_first_interface(int interface); -int usb_serial_get_config_descriptor(unsigned char *dest,int max_packet_size); -void usb_serial_init_connection(void); -void usb_serial_init(void); -void usb_serial_disconnect(void); -void usb_serial_transfer_complete(int ep,int dir, int status, int length); -bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest); - void usb_serial_send(const unsigned char *data, int length); -#endif +extern struct usb_class_driver usb_cdrv_serial; +#endif diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c index 4aadf1e0f1..2a24f71f36 100644 --- a/firmware/usbstack/usb_storage.c +++ b/firmware/usbstack/usb_storage.c @@ -322,13 +322,13 @@ static bool locked[NUM_DRIVES]; static int usb_interface; -struct usb_class_driver_ep_allocation usb_storage_ep_allocs[2] = { - {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_IN, .optional = false}, - {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_OUT, .optional = false}, +static struct usb_class_driver_ep_allocation ep_allocs[2] = { + {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_IN, .optional = false, .mps = -1}, + {.type = USB_ENDPOINT_XFER_BULK, .dir = DIR_OUT, .optional = false, .mps = -1}, }; -#define EP_IN (usb_storage_ep_allocs[0].ep) -#define EP_OUT (usb_storage_ep_allocs[1].ep) +#define EP_IN (ep_allocs[0].ep) +#define EP_OUT (ep_allocs[1].ep) #if defined(HAVE_MULTIDRIVE) static bool skip_first = 0; @@ -378,7 +378,7 @@ static bool check_disk_present(IF_MD_NONVOID(int volume)) } #ifdef HAVE_HOTSWAP -void usb_storage_notify_hotswap(int volume,bool inserted) +static void usb_storage_notify_hotswap(int volume,bool inserted) { logf("notify %d",inserted); if(inserted && check_disk_present(IF_MD(volume))) { @@ -401,18 +401,18 @@ void usb_set_skip_first_drive(bool skip) #endif /* called by usb_core_init() */ -void usb_storage_init(void) +static void usb_storage_init(void) { logf("usb_storage_init done"); } -int usb_storage_set_first_interface(int interface) +static int usb_storage_set_first_interface(int interface) { usb_interface = interface; return interface + 1; } -int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size) +static int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size) { unsigned char *orig_dest = dest; @@ -436,7 +436,7 @@ int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size) #else static int usb_handle = 0; #endif -void usb_storage_init_connection(void) +static int usb_storage_init_connection(void) { logf("ums: set config"); /* prime rx endpoint. We only need room for commands */ @@ -483,6 +483,7 @@ void usb_storage_init_connection(void) ejected[i] = !check_disk_present(IF_MD(i)); queue_broadcast(SYS_USB_LUN_LOCKED, (i<<16)+0); } + return 0; } void usb_storage_disconnect(void) @@ -493,7 +494,7 @@ void usb_storage_disconnect(void) } /* called by usb_core_transfer_complete() */ -void usb_storage_transfer_complete(int ep,int dir,int status,int length) +static void usb_storage_transfer_complete(int ep,int dir,int status,int length) { (void)ep; struct command_block_wrapper* cbw = (void*)cbw_buffer; @@ -694,7 +695,7 @@ static void usb_storage_send_smart(uint8_t cmd) #endif /* STORAGE_ATA */ /* called by usb_core_control_request() */ -bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) +static bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { bool handled = false; @@ -1479,3 +1480,21 @@ static void fill_inquiry(IF_MD_NONVOID(int lun)) tb.inquiry->DeviceTypeModifier = DEVICE_REMOVABLE; } + +struct usb_class_driver usb_cdrv_storage = { + .needs_exclusive_storage = true, + .needs_cpu_boost = true, + .config = 1, + .ep_allocs_size = ARRAYLEN(ep_allocs), + .ep_allocs = ep_allocs, + .set_first_interface = usb_storage_set_first_interface, + .get_config_descriptor = usb_storage_get_config_descriptor, + .init_connection = usb_storage_init_connection, + .init = usb_storage_init, + .disconnect = usb_storage_disconnect, + .transfer_complete = usb_storage_transfer_complete, + .control_request = usb_storage_control_request, +#ifdef HAVE_HOTSWAP + .notify_hotswap = usb_storage_notify_hotswap, +#endif +}; diff --git a/firmware/usbstack/usb_storage.h b/firmware/usbstack/usb_storage.h index 8dbe965882..16bc172ce5 100644 --- a/firmware/usbstack/usb_storage.h +++ b/firmware/usbstack/usb_storage.h @@ -23,18 +23,6 @@ #include "usb_class_driver.h" -extern struct usb_class_driver_ep_allocation usb_storage_ep_allocs[2]; - -int usb_storage_set_first_interface(int interface); -int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size); -void usb_storage_init_connection(void); -void usb_storage_disconnect(void); -void usb_storage_init(void); -void usb_storage_transfer_complete(int ep,int dir,int state,int length); -bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); -#ifdef HAVE_HOTSWAP -void usb_storage_notify_hotswap(int volume,bool inserted); -#endif - +extern struct usb_class_driver usb_cdrv_storage; #endif diff --git a/lib/rbcodec/codecs/codec_crt0.c b/lib/rbcodec/codecs/codec_crt0.c index e95b3e31e6..54035fc232 100644 --- a/lib/rbcodec/codecs/codec_crt0.c +++ b/lib/rbcodec/codecs/codec_crt0.c @@ -78,3 +78,12 @@ void __attribute__((naked)) __div0(void) asm volatile("bx %0" : : "r"(ci->__div0)); } #endif + +#if defined(USE_STACK_PROTECTOR) +const uint32_t __stack_chk_guard = 0x2BADC0DE; + +void __stack_chk_fail(void) +{ + ci->panicf("codec smashed stack"); +} +#endif diff --git a/lib/rbcodec/codecs/codecs.h b/lib/rbcodec/codecs/codecs.h index 5e977c6f13..dad5cbe8f2 100644 --- a/lib/rbcodec/codecs/codecs.h +++ b/lib/rbcodec/codecs/codecs.h @@ -224,6 +224,7 @@ struct codec_api { /* new stuff at the end, sort into place next time the API gets incompatible */ + void (*panicf)(const char *msg, ...); }; /* codec header */ diff --git a/lib/rbcodec/codecs/lib/codeclib.c b/lib/rbcodec/codecs/lib/codeclib.c index 6e8c209489..f1400bbd4e 100644 --- a/lib/rbcodec/codecs/lib/codeclib.c +++ b/lib/rbcodec/codecs/lib/codeclib.c @@ -38,7 +38,7 @@ int codec_init(void) /* codec_get_buffer() aligns the resulting point to MEM_ALIGN_SIZE. */ mem_ptr = 0; mallocbuf = (unsigned char *)ci->codec_get_buffer((size_t *)&bufsize); - + return 0; } @@ -64,9 +64,9 @@ void* codec_malloc(size_t size) if (mem_ptr + (long)size > bufsize) return NULL; - + x=&mallocbuf[mem_ptr]; - + /* Keep memory aligned to MEM_ALIGN_SIZE. */ mem_ptr += MEM_ALIGN_UP(size); @@ -136,6 +136,7 @@ int memcmp(const void *s1, const void *s2, size_t n) return(ci->memcmp(s1,s2,n)); } +#undef memchr void* memchr(const void *s, int c, size_t n) { return(ci->memchr(s,c,n)); diff --git a/lib/rbcodec/metadata/metadata.c b/lib/rbcodec/metadata/metadata.c index fafd227c14..16af58a291 100644 --- a/lib/rbcodec/metadata/metadata.c +++ b/lib/rbcodec/metadata/metadata.c @@ -27,6 +27,10 @@ #include "logf.h" #include "metadata.h" +#ifdef UTF8PROC_EXPORTS +#include "rbunicode.h" +#endif + #include "metadata_parsers.h" /* For trailing tag stripping and base audio data types */ @@ -381,7 +385,7 @@ bool format_buffers_with_offset(int afmt) /* Simple file type probing by looking at the filename extension. */ unsigned int probe_file_format(const char *filename) { - char *suffix; + const char *suffix; unsigned int i; suffix = strrchr(filename, '.'); @@ -418,7 +422,7 @@ unsigned int probe_file_format(const char *filename) * file that would prevent playback. supply a filedescriptor <0 and the file will be opened * and closed automatically within the get_metadata call * get_metadata_ex allows flags to change the way get_metadata behaves - * METADATA_EXCLUDE_ID3_PATH won't copy filename path to the id3 path buffer + * METADATA_EXCLUDE_ID3_PATH won't copy filename path to the id3 path buffer * METADATA_CLOSE_FD_ON_EXIT closes the open filedescriptor on exit */ bool get_metadata_ex(struct mp3entry* id3, int fd, const char* trackname, int flags) @@ -471,6 +475,22 @@ bool get_metadata_ex(struct mp3entry* id3, int fd, const char* trackname, int fl wipe_mp3entry(id3); /* ensure the mp3entry is clear */ } +#ifdef UTF8PROC_EXPORTS + if (success) { + utf8_normalize(id3->title); + utf8_normalize(id3->artist); + utf8_normalize(id3->album); + utf8_normalize(id3->genre_string); + utf8_normalize(id3->disc_string); + utf8_normalize(id3->track_string); + utf8_normalize(id3->year_string); + utf8_normalize(id3->composer); + utf8_normalize(id3->comment); + utf8_normalize(id3->albumartist); + utf8_normalize(id3->grouping); + } +#endif + if ((flags & METADATA_CLOSE_FD_ON_EXIT)) close(fd); else @@ -554,8 +574,8 @@ void fill_metadata_from_path(struct mp3entry *id3, const char *trackname) wipe_mp3entry(id3); /* Find the filename portion of the path */ - p = strrchr(trackname, '/'); - strlcpy(id3->id3v2buf, p ? ++p : id3->path, ID3V2_BUF_SIZE); + const char *pt = strrchr(trackname, '/'); + strlcpy(id3->id3v2buf, pt ? ++pt : id3->path, ID3V2_BUF_SIZE); /* Get the format from the extension and trim it off */ p = strrchr(id3->id3v2buf, '.'); diff --git a/lib/skin_parser/skin_debug.c b/lib/skin_parser/skin_debug.c index b9e57d9573..44fba90445 100644 --- a/lib/skin_parser/skin_debug.c +++ b/lib/skin_parser/skin_debug.c @@ -317,7 +317,7 @@ void skin_error_format_message(void) char text[128]; if (!error_line_start) return; - char* line_end = strchr(error_line_start, '\n'); + const char* line_end = strchr(error_line_start, '\n'); int len = MIN(line_end - error_line_start, 80); if (!line_end) len = strlen(error_line_start); diff --git a/manual/appendix/file_formats.tex b/manual/appendix/file_formats.tex index cb37d9b532..32e4ddbd8b 100644 --- a/manual/appendix/file_formats.tex +++ b/manual/appendix/file_formats.tex @@ -89,7 +89,7 @@ & \fname{.aac}, \fname{.m4a}, \fname{.m4b}, \fname{.mp4}, \fname{.rm}, \fname{.ra}, \fname{.rmvb} \opt{codec_aac_he}{ & Supports AAC-LC, -HEv1, and -HEv2 profiles\\} - \nopt{codec_aac_he}{ % low memory (CODEC_SIZE <= 512 KB) or slow cpu targets + \nopt{codec_aac_he}{ % low memory (CODEC_SIZE <= 512 KB) or slow cpu targets & Supports AAC-LC profile\\} MPEG audio & \fname{.mpa}, \fname{.mpga}, \fname{.mp1}, \fname{.mp2}, \fname{.mp3} @@ -146,7 +146,7 @@ & Linear PCM 8/16/24/32 bit, IEEE float 32/64 bit, ITU-T G.711 a-law/$\mu$-law\\ Free Lossless Audio & \fname{.flac} - & Multichannel and/or high-resolution files will be downmixed to 16-bit stereo. + & Multichannel and/or high-resolution files will be downmixed to 16-bit stereo. \opt{ipod,iriverh10,sansaclip,sansam200v4,sansac200v2,iriverh100,iriverh300,iaudiox5,iaudiom5,iaudiom3,mpiohd200,mpiphd300,cowond2}{ \nopt{ipodnano2g,ipod6g}{Due to resource constraints, files with large block sizes are not supported, and realtime playback may not be possible where downmixing is necessary.}} \\ @@ -377,4 +377,14 @@ \item The maximum size of each metadata item (e.g. Artists) is limited to 90 bytes. } + \opt{utf8proc}{ + \item All textual metadata is normalized into most compact Unicode + NFC form. This simplifies font/glpyh rendering, and makes + it possible to compare/sort strings that use different + normalization forms. + } + \nopt{utf8proc}{ + \item Due to RAM limitations, the \playertype does not process or + normalize textual metadata in any way. + } \end{enumerate} diff --git a/manual/plugins/countdown_timer.tex b/manual/plugins/countdown_timer.tex index c022203a92..8dc0a27bc7 100644 --- a/manual/plugins/countdown_timer.tex +++ b/manual/plugins/countdown_timer.tex @@ -1,5 +1,4 @@ -% $Id$ % -\subsection{Countdown_timer} +\subsection{Countdown Timer} A countdown timer. Set the desired duration, start the countdown, and the \dap{} will alert you with a beep sequence when time is up. The timer @@ -8,7 +7,7 @@ defaults to 10 minutes on launch. \subsubsection{Setting the timer} When the plugin starts, the display shows the time to count down from with -the active field highlighted in brackets (e.g.\ \texttt{[10]:00}). +the active field highlighted in brackets (e.g. \texttt{[10]:00}). \begin{btnmap} \opt{scrollwheel}{\PluginScrollFwd{} / \PluginScrollBack{} or } diff --git a/tools/Makefile b/tools/Makefile index 525048d39e..ee015fca94 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,7 +13,7 @@ LDFLAGS := CLEANALL := scramble descramble iriver bmp2rb rdf2binary convbdf \ generate_rocklatin mkboot ipod_fw codepages uclpack mi4 gigabeat lngdump \ telechips gigabeats creative hmac-sha1 rbspeexenc mkzenboot mk500boot \ - convttf mkspl-x1000 wavtrim voicefont + convttf mkspl-x1000 wavtrim voicefont reggen all: scramble descramble rdf2binary mkboot mkzenboot convbdf codepages \ uclpack rbspeexenc voicefont mk500boot mkspl-x1000 diff --git a/tools/configure b/tools/configure index 4e6439a0c1..800013ee25 100755 --- a/tools/configure +++ b/tools/configure @@ -1531,6 +1531,9 @@ help() { --with-address-sanitizer Enables the AddressSanitizer feature. Forces SDL threads. --with-ubsan Enables the UB Sanitizer feature. Forces SDL threads. + --with-stack-protector[=MODE] + Enable -fstack-protector-MODE option. With no MODE, + enables the normal -fstack-protector option. --32-bit Force a 32-bit simulator (use with --sdl-threads for duke3d) --prefix Target installation directory --compiler-prefix Override compiler prefix (inherently dangerous) @@ -1557,6 +1560,7 @@ ARG_THREAD_SUPPORT= ARG_32BIT= ARG_ADDR_SAN= ARG_UBSAN= +ARG_STACK_PROTECTOR= ARG_PLUGINS= err= for arg in "$@"; do @@ -1583,6 +1587,8 @@ for arg in "$@"; do ARG_THREAD_SUPPORT=0;; --with-address-sanitizer) ARG_ADDR_SAN=1;; --with-ubsan) ARG_UBSAN=1;; + --with-stack-protector=*) ARG_STACK_PROTECTOR=`echo "$arg" | cut -d = -f 2`;; + --with-stack-protector) ARG_STACK_PROTECTOR=default;; --prefix=*) ARG_PREFIX=`echo "$arg" | cut -d = -f 2`;; --compiler-prefix=*) ARG_COMPILER_PREFIX=`echo "$arg" | cut -d = -f 2`;; --help) help;; @@ -3970,6 +3976,7 @@ fi # player version, for bootloader usage # version 3 extradefines="$extradefines -DEROSQN_VER=3" + bootloader_only=1 ;; 249|erosqnative_v4) @@ -3998,6 +4005,7 @@ fi # player version, for bootloader usage # version 4 extradefines="$extradefines -DEROSQN_VER=4" + bootloader_only=1 ;; 232|ihifi770c) @@ -4406,6 +4414,11 @@ if [ -z "$debug" ]; then GCCOPTS="$GCCOPTS $GCCOPTIMIZE" fi +if [ "$bootloader_only" = "1" -a "$bootloader" != "1" ]; then + echo "Target can only be used for bootloader builds." + exit 9 +fi + # if building a simulator for an hosted port, APPLICATION # define clashes with SIMULATOR define @@ -4694,6 +4707,17 @@ if [ "$ARG_RBDIR" != "" ]; then echo "Using alternate rockbox dir: ${rbdir}" fi +if [ "$ARG_STACK_PROTECTOR" != "" ]; then + echo "Stack protector mode: $ARG_STACK_PROTECTOR" + extradefines="$extradefines -DUSE_STACK_PROTECTOR" + + if [ "$ARG_STACK_PROTECTOR" = "default" ]; then + GCCOPTS="$GCCOPTS -fstack-protector" + else + GCCOPTS="$GCCOPTS -fstack-protector-${ARG_STACK_PROTECTOR}" + fi +fi + cat > autoconf.h.new <