summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2014-12-06 23:13:44 +0100
committerCástor Muñoz <cmvidal@gmail.com>2015-10-07 06:15:03 +0200
commitefd047a08bf990b9d10be5e5df1bce83968bd4e4 (patch)
tree8f2ac064275f315741d78e5908a7c10c43937b02
parent38ae0d53e834e6538e3820c3fd1977c971a81c15 (diff)
downloadrockbox-efd047a.tar.gz
rockbox-efd047a.zip
iPod Classic: implement IPOD_ACCESSORY_PROTOCOL
Change-Id: I0f0950c42ae5bf5c5b4c2c2f097f8c68a92ba4dd
-rw-r--r--firmware/export/config/ipod6g.h8
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c4
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c108
3 files changed, 116 insertions, 4 deletions
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 */