summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c')
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c b/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
index 487984fbe3..5ff0b0ca25 100644
--- a/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
+++ b/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
@@ -47,6 +47,9 @@
47 47
48 48
49extern const struct uartc s5l8701_uartc0; 49extern const struct uartc s5l8701_uartc0;
50#ifdef IPOD_ACCESSORY_PROTOCOL
51void iap_rx_isr(int, char*, char*, uint32_t);
52#endif
50 53
51struct uartc_port ser_port IDATA_ATTR = 54struct uartc_port ser_port IDATA_ATTR =
52{ 55{
@@ -61,7 +64,11 @@ struct uartc_port ser_port IDATA_ATTR =
61 .clkhz = NANO2G_UART_CLK_HZ, 64 .clkhz = NANO2G_UART_CLK_HZ,
62 65
63 /* interrupt callbacks */ 66 /* interrupt callbacks */
67#ifdef IPOD_ACCESSORY_PROTOCOL
68 .rx_cb = iap_rx_isr,
69#else
64 .rx_cb = NULL, 70 .rx_cb = NULL,
71#endif
65 .tx_cb = NULL, /* polling */ 72 .tx_cb = NULL, /* polling */
66}; 73};
67 74
@@ -94,3 +101,109 @@ void tx_writec(unsigned char c)
94{ 101{
95 uartc_port_tx_byte(&ser_port, c); 102 uartc_port_tx_byte(&ser_port, c);
96} 103}
104
105
106#ifdef IPOD_ACCESSORY_PROTOCOL
107#include "iap.h"
108
109static enum {
110 ABR_STATUS_LAUNCHED, /* ST_SYNC */
111 ABR_STATUS_SYNCING, /* ST_SOF */
112 ABR_STATUS_DONE
113} abr_status;
114
115void serial_bitrate(int rate)
116{
117 logf("[%lu] serial_bitrate(%d)", (uint32_t)USEC_TIMER, rate);
118
119 if (rate == 0) {
120 /* Using auto-bitrate (ABR) to detect accessory Tx speed:
121 *
122 * + Here:
123 * - Disable Rx logic to clean the FIFO and the shift
124 * register, thus no Rx data interrupts are generated.
125 * - Launch ABR and wait for a low pulse in Rx line.
126 *
127 * + In ISR, when a low pulse is detected (ideally it is the
128 * start bit of 0xff):
129 * - Calculate and configure detected speed.
130 * - Enable Rx to verify that the next received data frame
131 * is 0x55 or 0xff:
132 * - If so, it's assumed bit rate is correctly detected,
133 * it will not be modified until speed is changed using
134 * RB options menu.
135 * - If not, reset iAP state machine and launch a new ABR.
136 */
137 uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED);
138 uartc_port_abr_start(&ser_port);
139 abr_status = ABR_STATUS_LAUNCHED;
140 }
141 else {
142 uint32_t brdata;
143 if (rate == 57600) brdata = BRDATA_57600;
144 else if (rate == 38400) brdata = BRDATA_38400;
145 else if (rate == 19200) brdata = BRDATA_19200;
146 else brdata = BRDATA_9600;
147 uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */
148 uartc_port_set_bitrate_raw(&ser_port, brdata);
149 uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
150 abr_status = ABR_STATUS_DONE;
151 }
152}
153
154void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt)
155{
156 /* ignore Rx errors, upper layer will discard bad packets */
157 (void) err;
158
159 static int sync_retry;
160
161 if (abr_status == ABR_STATUS_LAUNCHED) {
162 /* autobauding */
163 if (abr_cnt) {
164 #define BR2CNT(s) (NANO2G_UART_CLK_HZ / (unsigned)(s))
165 if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) {
166 /* detected speed out of range, relaunch ABR */
167 uartc_port_abr_start(&ser_port);
168 return;
169 }
170 /* valid speed detected, select it */
171 uint32_t brdata;
172 if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600;
173 else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400;
174 else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800;
175 else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200;
176 else brdata = BRDATA_9600;
177
178 /* set detected speed */
179 uartc_port_set_bitrate_raw(&ser_port, brdata);
180 uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
181
182 /* enter SOF state */
183 iap_getc(0xff);
184
185 abr_status = ABR_STATUS_SYNCING;
186 sync_retry = 2; /* we are expecting [0xff] 0x55 */
187 }
188 }
189
190 /* process received data */
191 while (len--)
192 {
193 bool sync_done = !iap_getc(*data++);
194
195 if (abr_status == ABR_STATUS_SYNCING)
196 {
197 if (sync_done) {
198 abr_status = ABR_STATUS_DONE;
199 }
200 else if (--sync_retry == 0) {
201 /* invalid speed detected, relaunch ABR
202 discarding remaining data (if any) */
203 serial_bitrate(0);
204 break;
205 }
206 }
207 }
208}
209#endif /* IPOD_ACCESSORY_PROTOCOL */