From 472a6a69c69d571e3b89599883a0de0ec0453b73 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Wed, 6 Aug 2025 14:26:34 -0400 Subject: [PATCH] IAP: Reset IAP state upon headphone or dock unplug Provides a semi-automatic way of recovering from desynchronization Change-Id: I527b0bacc22ef38c1e7213653e522ea1b0ac155d --- apps/debug_menu.c | 11 +++--- apps/iap/iap-core.c | 75 +++++++++++++++++++++++++++++++++------ firmware/drivers/button.c | 11 ++++++ firmware/export/iap.h | 3 +- firmware/usb.c | 8 +++++ 5 files changed, 93 insertions(+), 15 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index af28de9005..2714d53e59 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -118,10 +118,6 @@ #include "usb_core.h" #endif -#if defined(IPOD_ACCESSORY_PROTOCOL) -#include "iap.h" -#endif - #include "talk.h" #if defined(HAVE_DEVICEDATA)// && !defined(SIMULATOR) @@ -140,6 +136,10 @@ #include "norboot-target.h" #endif +#if defined(IPOD_ACCESSORY_PROTOCOL) +#include "iap.h" +#endif + #define SCREEN_MAX_CHARS (LCD_WIDTH / SYSFONT_WIDTH) static const char* threads_getname(int selected_item, void *data, @@ -2852,6 +2852,9 @@ static const struct { #if (defined(HAVE_WHEEL_ACCELERATION) && (CONFIG_KEYPAD==IPOD_4G_PAD) \ && !defined(IPOD_MINI) && !defined(SIMULATOR)) {"Debug scrollwheel", dbg_scrollwheel }, +#endif +#if defined(IPOD_ACCESSORY_PROTOCOL) + {"Debug IAP", dbg_iap }, #endif {"Talk engine stats", dbg_talk }, #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c index cb04ea8d35..e3f992924f 100644 --- a/apps/iap/iap-core.c +++ b/apps/iap/iap-core.c @@ -177,7 +177,7 @@ unsigned char lingo_versions[32][2] = { /* states of the iap de-framing state machine */ enum fsm_state { - ST_SYNC, /* wait for 0xFF sync byte */ + ST_SYNC = 0, /* wait for 0xFF sync byte */ ST_SOF, /* wait for 0x55 start-of-frame byte */ ST_LEN, /* receive length byte (small packet) */ ST_LENH, /* receive length high byte (large packet) */ @@ -210,6 +210,17 @@ static struct buflib_callbacks iap_buflib_callbacks = { static void iap_malloc(void); +static void iap_reset_buffers(void) +{ + iap_txstart = iap_buffers; + iap_txpayload = iap_txstart+5; + iap_txnext = iap_txpayload; + iap_rxstart = iap_buffers+(TX_BUFLEN+6); + iap_rxpayload = iap_rxstart; + iap_rxnext = iap_rxpayload; + iap_rxlen = RX_BUFLEN+2; +} + void put_u16(unsigned char *buf, const uint16_t data) { buf[0] = (data >> 8) & 0xFF; @@ -295,6 +306,27 @@ void iap_reset_auth(struct auth_t* auth) auth->next_section = 0; } +void iap_reset_state(int port) +{ + if (!iap_running) + return; + + /* 0 is dock, 1 is headphone. This is for + when we eventually maintain independent state */ + (void)port; + + iap_reset_device(&device); + iap_bitrate_set(global_settings.serial_bitrate); + +#if 0 // XXX this is still screwed up + memset(&frame_state, 0, sizeof(frame_state)); + interface_state = IST_STANDARD; + frame_state.state = ST_SYNC; + + iap_reset_buffers(); +#endif +} + void iap_reset_device(struct device_t* device) { iap_reset_auth(&(device->auth)); @@ -326,7 +358,6 @@ void iap_set_remote_volume(void) iap_send_tx(); } - /* This thread is waiting for events posted to iap_queue and calls * the appropriate subroutines in response */ @@ -460,13 +491,8 @@ static void iap_malloc(void) #else iap_buffers = serbuf; #endif - iap_txstart = iap_buffers; - iap_txpayload = iap_txstart+5; - iap_txnext = iap_txpayload; - iap_rxstart = iap_buffers+(TX_BUFLEN+6); - iap_rxpayload = iap_rxstart; - iap_rxnext = iap_rxpayload; - iap_rxlen = RX_BUFLEN+2; + + iap_reset_buffers(); iap_running = true; } @@ -996,7 +1022,7 @@ void iap_periodic(void) if (device.play_status != play_status) { /* If play_status = PAUSE/STOP we should mute else - * we should unmute + * we should unmute * 0 = Stopped * 1 = Playing * 2 = Pause @@ -1418,3 +1444,32 @@ void iap_fill_power_state(void) IAP_TX_PUT(0x00); } } + +#include "lcd.h" +#include "font.h" +bool dbg_iap(void) +{ + lcd_setfont(FONT_SYSFIXED); + + while (1) + { + if (action_userabort(HZ/10)) + break; + + lcd_clear_display(); + + /* show internal state of IAP subsystem */ + lcd_putsf(0, 0, "auth: %d acc: %d", device.auth.state, device.accinfo); + lcd_putsf(0, 1, "lin: %08x", device.lingoes); + lcd_putsf(0, 2, "notif: %08x", device.notifications); + lcd_putsf(0, 3, "cap: %08x/%08x", device.capabilities, device.capabilities_queried); + + // frame_state.state + // serial state + + lcd_update(); + } + + lcd_setfont(FONT_UI); + return false; +} diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c index d6cf5ef62a..b04598d995 100644 --- a/firmware/drivers/button.c +++ b/firmware/drivers/button.c @@ -45,6 +45,10 @@ #include "lcd.h" /* lcd_active() prototype */ #endif +#if defined(IPOD_ACCESSORY_PROTOCOL) && (defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IPOD_MINI2G)) +#include "iap.h" +#endif + static long lastbtn; /* Last valid button status */ static long last_read; /* Last button status, for debouncing/filtering */ static bool flipped; /* buttons can be flipped to match the LCD flip */ @@ -102,6 +106,12 @@ static int hp_detect_callback(struct timeout *tmo) /* Try to post only transistions */ const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED; button_queue_post_remove_head(id, 0); + +#if defined(IPOD_ACCESSORY_PROTOCOL) && (defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IPOD_MINI2G)) + if (id == SYS_PHONE_UNPLUGGED) + iap_reset_state(1); +#endif + return 0; /*misc.c:hp_unplug_change*/ } @@ -113,6 +123,7 @@ static int lo_detect_callback(struct timeout *tmo) /* Try to post only transistions */ const long id = tmo->data ? SYS_LINEOUT_PLUGGED : SYS_LINEOUT_UNPLUGGED; button_queue_post_remove_head(id, 0); + return 0; /*misc.c:lo_unplug_change*/ } diff --git a/firmware/export/iap.h b/firmware/export/iap.h index 2626f48355..8463ecba29 100644 --- a/firmware/export/iap.h +++ b/firmware/export/iap.h @@ -37,5 +37,6 @@ const unsigned char *iap_get_serbuf(void); #ifdef HAVE_LINE_REC extern bool iap_record(bool onoff); #endif - +void iap_reset_state(int port); /* 0 is dock, 1 is headphone */ +bool dbg_iap(void); #endif diff --git a/firmware/usb.c b/firmware/usb.c index 32f4902c7c..5894213fb7 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -51,6 +51,10 @@ #include "gui/skin_engine/skin_engine.h" #endif +#if defined(IPOD_ACCESSORY_PROTOCOL) +#include "iap.h" +#endif + /* Conditions under which we want the entire driver */ #if !defined(BOOTLOADER) || \ (defined(HAVE_USBSTACK) && defined(HAVE_BOOTLOADER_USB_MODE)) || \ @@ -521,6 +525,10 @@ static void NORETURN_ATTR usb_thread(void) if(usb_state == USB_POWERED || usb_state == USB_INSERTED) usb_stack_enable(false); +#ifdef IPOD_ACCESSORY_PROTOCOL + iap_reset_state(0); +#endif + /* Only disable the USB slave mode if we really have enabled it. Some expected acks may not have been received. */ if(usb_state == USB_INSERTED)