From efd047a08bf990b9d10be5e5df1bce83968bd4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1stor=20Mu=C3=B1oz?= Date: Sat, 6 Dec 2014 23:13:44 +0100 Subject: iPod Classic: implement IPOD_ACCESSORY_PROTOCOL Change-Id: I0f0950c42ae5bf5c5b4c2c2f097f8c68a92ba4dd --- firmware/export/config/ipod6g.h | 8 +- .../target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c | 4 +- firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c | 108 +++++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) (limited to 'firmware') diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h index a885a11144..7e7025e157 100644 --- a/firmware/export/config/ipod6g.h +++ b/firmware/export/config/ipod6g.h @@ -247,10 +247,14 @@ #define USB_NUM_ENDPOINTS 6 #define USB_DEVBSS_ATTR __attribute__((aligned(16))) +#define HAVE_SERIAL +/* Disable iAP when LOGF_SERIAL is enabled to avoid conflicts */ +#ifndef LOGF_SERIAL +#define IPOD_ACCESSORY_PROTOCOL +#endif + /* Define this if you can switch on/off the accessory power supply */ #define HAVE_ACCESSORY_SUPPLY -//#define IPOD_ACCESSORY_PROTOCOL -#define HAVE_SERIAL /* Define this, if you can switch on/off the lineout */ #define HAVE_LINEOUT_POWEROFF diff --git a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c index ace66295f8..4553b03685 100644 --- a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c @@ -69,12 +69,12 @@ void accessory_supply_set(bool enable) if (enable) { /* Accessory voltage supply on */ -//TODO: pmu_ldo_power_on(6); + pmu_ldo_power_on(6); } else { /* Accessory voltage supply off */ -//TODO: pmu_ldo_power_off(6); + pmu_ldo_power_off(6); } } #endif diff --git a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c index c03ba9bc24..b022fef675 100644 --- a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c @@ -41,6 +41,9 @@ #define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ extern struct uartc s5l8702_uart; +#ifdef IPOD_ACCESSORY_PROTOCOL +void iap_rx_isr(int, char*, char*, uint32_t); +#endif struct uartc_port ser_port IDATA_ATTR = { /* location */ @@ -54,7 +57,11 @@ struct uartc_port ser_port IDATA_ATTR = { .clkhz = IPOD6G_UART_CLK_HZ, /* interrupt callbacks */ +#ifdef IPOD_ACCESSORY_PROTOCOL + .rx_cb = iap_rx_isr, +#else .rx_cb = NULL, +#endif .tx_cb = NULL, /* polling */ }; @@ -85,3 +92,104 @@ void tx_writec(unsigned char c) { uartc_port_tx_byte(&ser_port, c); } + +#ifdef IPOD_ACCESSORY_PROTOCOL +#include "iap.h" + +enum { + ABR_STATUS_LAUNCHED, /* ST_SYNC */ + ABR_STATUS_SYNCING, /* ST_SOF */ + ABR_STATUS_DONE +}; + +int abr_status; + +void serial_bitrate(int rate) +{ + logf("[%lu] serial_bitrate(%d)", USEC_TIMER, rate); + + if (rate == 0) { + /* Using auto-bitrate (ABR) to detect accessory Tx speed: + * + * + Here: + * - Disable Rx logic to clean the FIFO and the shift + * register, thus no Rx data interrupts are generated. + * - Launch ABR and wait for a low pulse in Rx line. + * + * + In ISR, when a low pulse is detected (ideally it is the + * start bit of 0xff): + * - Calculate and configure detected speed. + * - Enable Rx to verify that the next received data frame + * is 0x55 or 0xff: + * - If so, it's assumed bit rate is correctly detected, + * it will not be modified until speed is changed using + * RB options menu. + * - If not, reset iAP state machine and launch a new ABR. + */ + uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED); + uartc_port_abr_start(&ser_port); + abr_status = ABR_STATUS_LAUNCHED; + } + else { + uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */ + uartc_port_set_bitrate(&ser_port, rate); + uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); + abr_status = ABR_STATUS_DONE; + } +} + +void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) +{ + /* ignore Rx errors, upper layer will discard bad packets */ + (void) err; + + static int sync_retry; + + if (abr_status == ABR_STATUS_LAUNCHED) { + /* autobauding */ + if (abr_cnt) { + #define BR2CNT(s) (IPOD6G_UART_CLK_HZ / (unsigned)(s)) + unsigned speed; + + if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) { + /* detected speed out of range, relaunch ABR */ + uartc_port_abr_start(&ser_port); + return; + } + /* valid speed detected, select it */ + else if (abr_cnt < BR2CNT(48000)) speed = 57600; + else if (abr_cnt < BR2CNT(33600)) speed = 38400; + else if (abr_cnt < BR2CNT(24000)) speed = 28800; + else if (abr_cnt < BR2CNT(14400)) speed = 19200; + else speed = 9600; + + /* set detected speed */ + uartc_port_set_bitrate(&ser_port, speed); + uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); + + /* enter SOF state */ + iap_getc(0xff); + + abr_status = ABR_STATUS_SYNCING; + sync_retry = 2; /* we are expecting [0xff] 0x55 */ + } + } + + /* process received data */ + while (len--) { + bool sync_done = !iap_getc(*data++); + + if (abr_status == ABR_STATUS_SYNCING) { + if (sync_done) { + abr_status = ABR_STATUS_DONE; + } + else if (--sync_retry == 0) { + /* invalid speed detected, relaunch ABR + discarding remaining data (if any) */ + serial_bitrate(0); + break; + } + } + } +} +#endif /* IPOD_ACCESSORY_PROTOCOL */ -- cgit v1.2.3