diff options
Diffstat (limited to 'apps/iap/iap-core.c')
-rw-r--r-- | apps/iap/iap-core.c | 1392 |
1 files changed, 1392 insertions, 0 deletions
diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c new file mode 100644 index 0000000000..ddcb22853a --- /dev/null +++ b/apps/iap/iap-core.c | |||
@@ -0,0 +1,1392 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Alan Korr & Nick Robinson | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <stdarg.h> | ||
22 | #include <string.h> | ||
23 | |||
24 | #include "panic.h" | ||
25 | #include "iap-core.h" | ||
26 | #include "iap-lingo.h" | ||
27 | #include "button.h" | ||
28 | #include "config.h" | ||
29 | #include "cpu.h" | ||
30 | #include "system.h" | ||
31 | #include "kernel.h" | ||
32 | #include "thread.h" | ||
33 | #include "serial.h" | ||
34 | #include "appevents.h" | ||
35 | #include "core_alloc.h" | ||
36 | |||
37 | #include "playlist.h" | ||
38 | #include "playback.h" | ||
39 | #include "audio.h" | ||
40 | #include "settings.h" | ||
41 | #include "metadata.h" | ||
42 | #include "sound.h" | ||
43 | #include "action.h" | ||
44 | #include "powermgmt.h" | ||
45 | |||
46 | #include "tuner.h" | ||
47 | #include "ipod_remote_tuner.h" | ||
48 | |||
49 | |||
50 | /* MS_TO_TICKS converts a milisecond time period into the | ||
51 | * corresponding amount of ticks. If the time period cannot | ||
52 | * be accurately measured in ticks it will round up. | ||
53 | */ | ||
54 | #if (HZ>1000) | ||
55 | #error "HZ is >1000, please fix MS_TO_TICKS" | ||
56 | #endif | ||
57 | #define MS_PER_HZ (1000/HZ) | ||
58 | #define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ) | ||
59 | /* IAP specifies a timeout of 25ms for traffic from a device to the iPod. | ||
60 | * Depending on HZ this cannot be accurately measured. Find out the next | ||
61 | * best thing. | ||
62 | */ | ||
63 | #define IAP_PKT_TIMEOUT (MS_TO_TICKS(25)) | ||
64 | |||
65 | /* Events in the iap_queue */ | ||
66 | #define IAP_EV_TICK (1) /* The regular task timeout */ | ||
67 | #define IAP_EV_MSG_RCVD (2) /* A complete message has been received from the device */ | ||
68 | #define IAP_EV_MALLOC (3) /* Allocate memory for the RX/TX buffers */ | ||
69 | |||
70 | static bool iap_started = false; | ||
71 | static bool iap_setupflag = false, iap_running = false; | ||
72 | /* This is set to true if a SYS_POWEROFF message is received, | ||
73 | * signalling impending power off | ||
74 | */ | ||
75 | static bool iap_shutdown = false; | ||
76 | static struct timeout iap_task_tmo; | ||
77 | |||
78 | unsigned long iap_remotebtn = 0; | ||
79 | /* Used to make sure a button press is delivered to the processing | ||
80 | * backend. While this is !0, no new incoming messasges are processed. | ||
81 | * Counted down by remote_control_rx() | ||
82 | */ | ||
83 | int iap_repeatbtn = 0; | ||
84 | /* Used to time out button down events in case we miss the button up event | ||
85 | * from the device somehow. | ||
86 | * If a device sends a button down event it's required to repeat that event | ||
87 | * every 30 to 100ms as long as the button is pressed, and send an explicit | ||
88 | * button up event if the button is released. | ||
89 | * In case the button up event is lost any down events will time out after | ||
90 | * ~200ms. | ||
91 | * iap_periodic() will count down this variable and reset all buttons if | ||
92 | * it reaches 0 | ||
93 | */ | ||
94 | unsigned int iap_timeoutbtn = 0; | ||
95 | bool iap_btnrepeat = false, iap_btnshuffle = false; | ||
96 | |||
97 | static long thread_stack[(DEFAULT_STACK_SIZE*6)/sizeof(long)]; | ||
98 | static struct event_queue iap_queue; | ||
99 | |||
100 | /* These are pointer used to manage a dynamically allocated buffer which | ||
101 | * will hold both the RX and TX side of things. | ||
102 | * | ||
103 | * iap_buffer_handle is the handle returned from core_alloc() | ||
104 | * iap_buffers points to the start of the complete buffer | ||
105 | * | ||
106 | * The buffer is partitioned as follows: | ||
107 | * - TX_BUFLEN+6 bytes for the TX buffer | ||
108 | * The 6 extra bytes are for the sync byte, the SOP byte, the length indicators | ||
109 | * (3 bytes) and the checksum byte. | ||
110 | * iap_txstart points to the beginning of the TX buffer | ||
111 | * iap_txpayload points to the beginning of the payload portion of the TX buffer | ||
112 | * iap_txnext points to the position where the next byte will be placed | ||
113 | * | ||
114 | * - RX_BUFLEN+2 bytes for the RX buffer | ||
115 | * The RX buffer can hold multiple packets at once, up to it's | ||
116 | * maximum capacity. Every packet consists of a two byte length | ||
117 | * indicator followed by the actual payload. The length indicator | ||
118 | * is two bytes for every length, even for packets with a length <256 | ||
119 | * bytes. | ||
120 | * | ||
121 | * Once a packet has been processed from the RX buffer the rest | ||
122 | * of the buffer (and the pointers below) are shifted to the front | ||
123 | * so that the next packet again starts at the beginning of the | ||
124 | * buffer. This happens with interrupts disabled, to prevent | ||
125 | * writing into the buffer during the move. | ||
126 | * | ||
127 | * iap_rxstart points to the beginning of the RX buffer | ||
128 | * iap_rxpayload starts to the beginning of the currently recieved | ||
129 | * packet | ||
130 | * iap_rxnext points to the position where the next incoming byte | ||
131 | * will be placed | ||
132 | * iap_rxlen is not a pointer, but an indicator of the free | ||
133 | * space left in the RX buffer. | ||
134 | * | ||
135 | * The RX buffer is placed behind the TX buffer so that an eventual TX | ||
136 | * buffer overflow has some place to spill into where it will not cause | ||
137 | * immediate damage. See the comments for IAP_TX_* and iap_send_tx() | ||
138 | */ | ||
139 | #define IAP_MALLOC_SIZE (TX_BUFLEN+6+RX_BUFLEN+2) | ||
140 | #ifdef IAP_MALLOC_DYNAMIC | ||
141 | static int iap_buffer_handle; | ||
142 | #endif | ||
143 | static unsigned char* iap_buffers; | ||
144 | static unsigned char* iap_rxstart; | ||
145 | static unsigned char* iap_rxpayload; | ||
146 | static unsigned char* iap_rxnext; | ||
147 | static uint32_t iap_rxlen; | ||
148 | static unsigned char* iap_txstart; | ||
149 | unsigned char* iap_txpayload; | ||
150 | unsigned char* iap_txnext; | ||
151 | |||
152 | /* The versions of the various Lingoes we support. A major version | ||
153 | * of 0 means unsupported | ||
154 | */ | ||
155 | unsigned char lingo_versions[32][2] = { | ||
156 | {1, 9}, /* General lingo, 0x00 */ | ||
157 | {0, 0}, /* Microphone lingo, 0x01, unsupported */ | ||
158 | {1, 2}, /* Simple remote lingo, 0x02 */ | ||
159 | {1, 5}, /* Display remote lingo, 0x03 */ | ||
160 | {1, 12}, /* Extended Interface lingo, 0x04 */ | ||
161 | {1, 1}, /* RF/BT Transmitter lingo, 0x05 */ | ||
162 | {} /* All others are unsupported */ | ||
163 | }; | ||
164 | |||
165 | /* states of the iap de-framing state machine */ | ||
166 | enum fsm_state { | ||
167 | ST_SYNC, /* wait for 0xFF sync byte */ | ||
168 | ST_SOF, /* wait for 0x55 start-of-frame byte */ | ||
169 | ST_LEN, /* receive length byte (small packet) */ | ||
170 | ST_LENH, /* receive length high byte (large packet) */ | ||
171 | ST_LENL, /* receive length low byte (large packet) */ | ||
172 | ST_DATA, /* receive data */ | ||
173 | ST_CHECK /* verify checksum */ | ||
174 | }; | ||
175 | |||
176 | static struct state_t { | ||
177 | enum fsm_state state; /* current fsm state */ | ||
178 | unsigned int len; /* payload data length */ | ||
179 | unsigned int check; /* running checksum over [len,payload,check] */ | ||
180 | unsigned int count; /* playload bytes counter */ | ||
181 | } frame_state = { | ||
182 | .state = ST_SYNC | ||
183 | }; | ||
184 | |||
185 | enum interface_state interface_state = IST_STANDARD; | ||
186 | |||
187 | struct device_t device; | ||
188 | |||
189 | #ifdef IAP_MALLOC_DYNAMIC | ||
190 | static int iap_move_callback(int handle, void* current, void* new); | ||
191 | |||
192 | static struct buflib_callbacks iap_buflib_callbacks = { | ||
193 | iap_move_callback, | ||
194 | NULL | ||
195 | }; | ||
196 | #endif | ||
197 | |||
198 | static void iap_malloc(void); | ||
199 | |||
200 | void put_u16(unsigned char *buf, const uint16_t data) | ||
201 | { | ||
202 | buf[0] = (data >> 8) & 0xFF; | ||
203 | buf[1] = (data >> 0) & 0xFF; | ||
204 | } | ||
205 | |||
206 | void put_u32(unsigned char *buf, const uint32_t data) | ||
207 | { | ||
208 | buf[0] = (data >> 24) & 0xFF; | ||
209 | buf[1] = (data >> 16) & 0xFF; | ||
210 | buf[2] = (data >> 8) & 0xFF; | ||
211 | buf[3] = (data >> 0) & 0xFF; | ||
212 | } | ||
213 | |||
214 | uint32_t get_u32(const unsigned char *buf) | ||
215 | { | ||
216 | return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
217 | } | ||
218 | |||
219 | uint16_t get_u16(const unsigned char *buf) | ||
220 | { | ||
221 | return (buf[0] << 8) | buf[1]; | ||
222 | } | ||
223 | |||
224 | #if defined(LOGF_ENABLE) && defined(ROCKBOX_HAS_LOGF) | ||
225 | /* Convert a buffer into a printable string, perl style | ||
226 | * buf contains the data to be converted, len is the length | ||
227 | * of the buffer. | ||
228 | * | ||
229 | * This will convert at most 1024 bytes from buf | ||
230 | */ | ||
231 | static char* hexstring(const unsigned char *buf, unsigned int len) { | ||
232 | static char hexbuf[4097]; | ||
233 | unsigned int l; | ||
234 | const unsigned char* p; | ||
235 | unsigned char* out; | ||
236 | unsigned char h[] = {'0', '1', '2', '3', '4', '5', '6', '7', | ||
237 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | ||
238 | |||
239 | if (len > 1024) { | ||
240 | l = 1024; | ||
241 | } else { | ||
242 | l = len; | ||
243 | } | ||
244 | p = buf; | ||
245 | out = hexbuf; | ||
246 | do { | ||
247 | *out++ = h[(*p)>>4]; | ||
248 | *out++ = h[*p & 0x0F]; | ||
249 | } while(--l && p++); | ||
250 | |||
251 | *out = 0x00; | ||
252 | |||
253 | return hexbuf; | ||
254 | } | ||
255 | #endif | ||
256 | |||
257 | |||
258 | void iap_tx_strlcpy(const unsigned char *str) | ||
259 | { | ||
260 | ptrdiff_t txfree; | ||
261 | int r; | ||
262 | |||
263 | txfree = TX_BUFLEN - (iap_txnext - iap_txstart); | ||
264 | r = strlcpy(iap_txnext, str, txfree); | ||
265 | |||
266 | if (r < txfree) | ||
267 | { | ||
268 | /* No truncation occured | ||
269 | * Account for the terminating \0 | ||
270 | */ | ||
271 | iap_txnext += (r+1); | ||
272 | } else { | ||
273 | /* Truncation occured, the TX buffer is now full. */ | ||
274 | iap_txnext = iap_txstart + TX_BUFLEN; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | void iap_reset_auth(struct auth_t* auth) | ||
279 | { | ||
280 | auth->state = AUST_NONE; | ||
281 | auth->max_section = 0; | ||
282 | auth->next_section = 0; | ||
283 | } | ||
284 | |||
285 | void iap_reset_device(struct device_t* device) | ||
286 | { | ||
287 | iap_reset_auth(&(device->auth)); | ||
288 | device->lingoes = 0; | ||
289 | device->notifications = 0; | ||
290 | device->changed_notifications = 0; | ||
291 | device->do_notify = false; | ||
292 | device->do_power_notify = false; | ||
293 | device->accinfo = ACCST_NONE; | ||
294 | device->capabilities = 0; | ||
295 | device->capabilities_queried = 0; | ||
296 | } | ||
297 | |||
298 | static int iap_task(struct timeout *tmo) | ||
299 | { | ||
300 | (void) tmo; | ||
301 | |||
302 | queue_post(&iap_queue, IAP_EV_TICK, 0); | ||
303 | return MS_TO_TICKS(100); | ||
304 | } | ||
305 | |||
306 | /* This thread is waiting for events posted to iap_queue and calls | ||
307 | * the appropriate subroutines in response | ||
308 | */ | ||
309 | static void iap_thread(void) | ||
310 | { | ||
311 | struct queue_event ev; | ||
312 | while(1) { | ||
313 | queue_wait(&iap_queue, &ev); | ||
314 | switch (ev.id) | ||
315 | { | ||
316 | /* Handle the regular 100ms tick used for driving the | ||
317 | * authentication state machine and notifications | ||
318 | */ | ||
319 | case IAP_EV_TICK: | ||
320 | { | ||
321 | iap_periodic(); | ||
322 | break; | ||
323 | } | ||
324 | |||
325 | /* Handle a newly received message from the device */ | ||
326 | case IAP_EV_MSG_RCVD: | ||
327 | { | ||
328 | iap_handlepkt(); | ||
329 | break; | ||
330 | } | ||
331 | |||
332 | /* Handle memory allocation. This is used only once, during | ||
333 | * startup | ||
334 | */ | ||
335 | case IAP_EV_MALLOC: | ||
336 | { | ||
337 | iap_malloc(); | ||
338 | break; | ||
339 | } | ||
340 | |||
341 | /* Handle poweroff message */ | ||
342 | case SYS_POWEROFF: | ||
343 | { | ||
344 | iap_shutdown = true; | ||
345 | break; | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /* called by playback when the next track starts */ | ||
352 | static void iap_track_changed(void *ignored) | ||
353 | { | ||
354 | (void)ignored; | ||
355 | if ((interface_state == IST_EXTENDED) && device.do_notify) { | ||
356 | long playlist_pos = playlist_next(0); | ||
357 | playlist_pos -= playlist_get_first_index(NULL); | ||
358 | if(playlist_pos < 0) | ||
359 | playlist_pos += playlist_amount(); | ||
360 | |||
361 | IAP_TX_INIT4(0x04, 0x0027); | ||
362 | IAP_TX_PUT(0x01); | ||
363 | IAP_TX_PUT_U32(playlist_pos); | ||
364 | |||
365 | iap_send_tx(); | ||
366 | return; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /* Do general setup of the needed infrastructure. | ||
371 | * | ||
372 | * Please note that a lot of additional work is done by iap_start() | ||
373 | */ | ||
374 | void iap_setup(const int ratenum) | ||
375 | { | ||
376 | iap_bitrate_set(ratenum); | ||
377 | iap_remotebtn = BUTTON_NONE; | ||
378 | iap_setupflag = true; | ||
379 | iap_started = false; | ||
380 | iap_running = false; | ||
381 | } | ||
382 | |||
383 | /* Actually bring up the message queue, message handler thread and | ||
384 | * notification timer | ||
385 | * | ||
386 | * NOTE: This is running in interrupt context | ||
387 | */ | ||
388 | static void iap_start(void) | ||
389 | { | ||
390 | unsigned int tid; | ||
391 | |||
392 | if (iap_started) | ||
393 | return; | ||
394 | |||
395 | iap_reset_device(&device); | ||
396 | queue_init(&iap_queue, true); | ||
397 | tid = create_thread(iap_thread, thread_stack, sizeof(thread_stack), | ||
398 | 0, "iap" | ||
399 | IF_PRIO(, PRIORITY_SYSTEM) | ||
400 | IF_COP(, CPU)); | ||
401 | if (!tid) | ||
402 | panicf("Could not create iap thread"); | ||
403 | timeout_register(&iap_task_tmo, iap_task, MS_TO_TICKS(100), (intptr_t)NULL); | ||
404 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed); | ||
405 | |||
406 | /* Since we cannot allocate memory while in interrupt context | ||
407 | * post a message to our own queue to get that done | ||
408 | */ | ||
409 | queue_post(&iap_queue, IAP_EV_MALLOC, 0); | ||
410 | iap_started = true; | ||
411 | } | ||
412 | |||
413 | static void iap_malloc(void) | ||
414 | { | ||
415 | #ifndef IAP_MALLOC_DYNAMIC | ||
416 | static unsigned char serbuf[IAP_MALLOC_SIZE]; | ||
417 | #endif | ||
418 | |||
419 | if (iap_running) | ||
420 | return; | ||
421 | |||
422 | #ifdef IAP_MALLOC_DYNAMIC | ||
423 | iap_buffer_handle = core_alloc_ex("iap", IAP_MALLOC_SIZE, &iap_buflib_callbacks); | ||
424 | if (iap_buffer_handle < 0) | ||
425 | panicf("Could not allocate buffer memory"); | ||
426 | iap_buffers = core_get_data(iap_buffer_handle); | ||
427 | #else | ||
428 | iap_buffers = serbuf; | ||
429 | #endif | ||
430 | iap_txstart = iap_buffers; | ||
431 | iap_txpayload = iap_txstart+5; | ||
432 | iap_txnext = iap_txpayload; | ||
433 | iap_rxstart = iap_buffers+(TX_BUFLEN+6); | ||
434 | iap_rxpayload = iap_rxstart; | ||
435 | iap_rxnext = iap_rxpayload; | ||
436 | iap_rxlen = RX_BUFLEN+2; | ||
437 | iap_running = true; | ||
438 | } | ||
439 | |||
440 | void iap_bitrate_set(const int ratenum) | ||
441 | { | ||
442 | switch(ratenum) | ||
443 | { | ||
444 | case 0: | ||
445 | serial_bitrate(0); | ||
446 | break; | ||
447 | case 1: | ||
448 | serial_bitrate(9600); | ||
449 | break; | ||
450 | case 2: | ||
451 | serial_bitrate(19200); | ||
452 | break; | ||
453 | case 3: | ||
454 | serial_bitrate(38400); | ||
455 | break; | ||
456 | case 4: | ||
457 | serial_bitrate(57600); | ||
458 | break; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | /* Message format: | ||
463 | 0xff | ||
464 | 0x55 | ||
465 | length | ||
466 | mode | ||
467 | command (2 bytes) | ||
468 | parameters (0-n bytes) | ||
469 | checksum (length+mode+parameters+checksum == 0) | ||
470 | */ | ||
471 | |||
472 | /* Send the current content of the TX buffer. | ||
473 | * This will check for TX buffer overflow and panic, but it might | ||
474 | * be too late by then (although one would have to overflow the complete | ||
475 | * RX buffer as well) | ||
476 | */ | ||
477 | void iap_send_tx(void) | ||
478 | { | ||
479 | int i, chksum; | ||
480 | ptrdiff_t txlen; | ||
481 | unsigned char* txstart; | ||
482 | |||
483 | txlen = iap_txnext - iap_txpayload; | ||
484 | |||
485 | if (txlen <= 0) | ||
486 | return; | ||
487 | |||
488 | if (txlen > TX_BUFLEN) | ||
489 | panicf("IAP: TX buffer overflow"); | ||
490 | |||
491 | if (txlen < 256) | ||
492 | { | ||
493 | /* Short packet */ | ||
494 | txstart = iap_txstart+2; | ||
495 | *(txstart+2) = txlen; | ||
496 | chksum = txlen; | ||
497 | } else { | ||
498 | /* Long packet */ | ||
499 | txstart = iap_txstart; | ||
500 | *(txstart+2) = 0x00; | ||
501 | *(txstart+3) = (txlen >> 8) & 0xFF; | ||
502 | *(txstart+4) = (txlen) & 0xFF; | ||
503 | chksum = *(txstart+3) + *(txstart+4); | ||
504 | } | ||
505 | *(txstart) = 0xFF; | ||
506 | *(txstart+1) = 0x55; | ||
507 | |||
508 | for (i=0; i<txlen; i++) | ||
509 | { | ||
510 | chksum += iap_txpayload[i]; | ||
511 | } | ||
512 | *(iap_txnext) = 0x100 - (chksum & 0xFF); | ||
513 | |||
514 | #ifdef LOGF_ENABLE | ||
515 | logf("T: %s", hexstring(txstart+3, (iap_txnext - txstart)-3)); | ||
516 | #endif | ||
517 | for (i=0; i <= (iap_txnext - txstart); i++) | ||
518 | { | ||
519 | while(!tx_rdy()) ; | ||
520 | tx_writec(txstart[i]); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | /* This is just a compatibility wrapper around the new TX buffer | ||
525 | * infrastructure | ||
526 | */ | ||
527 | void iap_send_pkt(const unsigned char * data, const int len) | ||
528 | { | ||
529 | if (!iap_running) | ||
530 | return; | ||
531 | |||
532 | iap_txnext = iap_txpayload; | ||
533 | IAP_TX_PUT_DATA(data, len); | ||
534 | iap_send_tx(); | ||
535 | } | ||
536 | |||
537 | bool iap_getc(const unsigned char x) | ||
538 | { | ||
539 | struct state_t *s = &frame_state; | ||
540 | static long pkt_timeout; | ||
541 | |||
542 | if (!iap_setupflag) | ||
543 | return false; | ||
544 | |||
545 | /* Check the time since the last packet arrived. */ | ||
546 | if ((s->state != ST_SYNC) && TIME_AFTER(current_tick, pkt_timeout)) { | ||
547 | /* Packet timeouts only make sense while not waiting for the | ||
548 | * sync byte */ | ||
549 | s->state = ST_SYNC; | ||
550 | return iap_getc(x); | ||
551 | } | ||
552 | |||
553 | |||
554 | /* run state machine to detect and extract a valid frame */ | ||
555 | switch (s->state) { | ||
556 | case ST_SYNC: | ||
557 | if (x == 0xFF) { | ||
558 | /* The IAP infrastructure is started by the first received sync | ||
559 | * byte. It takes a while to spin up, so do not advance the state | ||
560 | * machine until it has started. | ||
561 | */ | ||
562 | if (!iap_running) | ||
563 | { | ||
564 | iap_start(); | ||
565 | break; | ||
566 | } | ||
567 | iap_rxnext = iap_rxpayload; | ||
568 | s->state = ST_SOF; | ||
569 | } | ||
570 | break; | ||
571 | case ST_SOF: | ||
572 | if (x == 0x55) { | ||
573 | /* received a valid sync/SOF pair */ | ||
574 | s->state = ST_LEN; | ||
575 | } else { | ||
576 | s->state = ST_SYNC; | ||
577 | return iap_getc(x); | ||
578 | } | ||
579 | break; | ||
580 | case ST_LEN: | ||
581 | s->check = x; | ||
582 | s->count = 0; | ||
583 | if (x == 0) { | ||
584 | /* large packet */ | ||
585 | s->state = ST_LENH; | ||
586 | } else { | ||
587 | /* small packet */ | ||
588 | if (x > (iap_rxlen-2)) | ||
589 | { | ||
590 | /* Packet too long for buffer */ | ||
591 | s->state = ST_SYNC; | ||
592 | break; | ||
593 | } | ||
594 | s->len = x; | ||
595 | s->state = ST_DATA; | ||
596 | put_u16(iap_rxnext, s->len); | ||
597 | iap_rxnext += 2; | ||
598 | } | ||
599 | break; | ||
600 | case ST_LENH: | ||
601 | s->check += x; | ||
602 | s->len = x << 8; | ||
603 | s->state = ST_LENL; | ||
604 | break; | ||
605 | case ST_LENL: | ||
606 | s->check += x; | ||
607 | s->len += x; | ||
608 | if ((s->len == 0) || (s->len > (iap_rxlen-2))) { | ||
609 | /* invalid length */ | ||
610 | s->state = ST_SYNC; | ||
611 | break; | ||
612 | } else { | ||
613 | s->state = ST_DATA; | ||
614 | put_u16(iap_rxnext, s->len); | ||
615 | iap_rxnext += 2; | ||
616 | } | ||
617 | break; | ||
618 | case ST_DATA: | ||
619 | s->check += x; | ||
620 | *(iap_rxnext++) = x; | ||
621 | s->count += 1; | ||
622 | if (s->count == s->len) { | ||
623 | s->state = ST_CHECK; | ||
624 | } | ||
625 | break; | ||
626 | case ST_CHECK: | ||
627 | s->check += x; | ||
628 | if ((s->check & 0xFF) == 0) { | ||
629 | /* done, received a valid frame */ | ||
630 | iap_rxpayload = iap_rxnext; | ||
631 | queue_post(&iap_queue, IAP_EV_MSG_RCVD, 0); | ||
632 | } else { | ||
633 | /* Invalid frame */ | ||
634 | } | ||
635 | s->state = ST_SYNC; | ||
636 | break; | ||
637 | default: | ||
638 | #ifdef LOGF_ENABLE | ||
639 | logf("Unhandled iap state %d", (int) s->state); | ||
640 | #else | ||
641 | panicf("Unhandled iap state %d", (int) s->state); | ||
642 | #endif | ||
643 | break; | ||
644 | } | ||
645 | |||
646 | pkt_timeout = current_tick + IAP_PKT_TIMEOUT; | ||
647 | |||
648 | /* return true while still hunting for the sync and start-of-frame byte */ | ||
649 | return (s->state == ST_SYNC) || (s->state == ST_SOF); | ||
650 | } | ||
651 | |||
652 | void iap_get_trackinfo(const unsigned int track, struct mp3entry* id3) | ||
653 | { | ||
654 | int tracknum; | ||
655 | int fd; | ||
656 | struct playlist_track_info info; | ||
657 | |||
658 | tracknum = track; | ||
659 | |||
660 | tracknum += playlist_get_first_index(NULL); | ||
661 | if(tracknum >= playlist_amount()) | ||
662 | tracknum -= playlist_amount(); | ||
663 | |||
664 | /* If the tracknumber is not the current one, | ||
665 | read id3 from disk */ | ||
666 | if(playlist_next(0) != tracknum) | ||
667 | { | ||
668 | playlist_get_track_info(NULL, tracknum, &info); | ||
669 | fd = open(info.filename, O_RDONLY); | ||
670 | memset(id3, 0, sizeof(*id3)); | ||
671 | get_metadata(id3, fd, info.filename); | ||
672 | close(fd); | ||
673 | } else { | ||
674 | memcpy(id3, audio_current_track(), sizeof(*id3)); | ||
675 | } | ||
676 | } | ||
677 | |||
678 | uint32_t iap_get_trackpos(void) | ||
679 | { | ||
680 | struct mp3entry *id3 = audio_current_track(); | ||
681 | |||
682 | return id3->elapsed; | ||
683 | } | ||
684 | |||
685 | uint32_t iap_get_trackindex(void) | ||
686 | { | ||
687 | struct playlist_info* playlist = playlist_get_current(); | ||
688 | |||
689 | return (playlist->index - playlist->first_index); | ||
690 | } | ||
691 | |||
692 | void iap_periodic(void) | ||
693 | { | ||
694 | static int count; | ||
695 | |||
696 | if(!iap_setupflag) return; | ||
697 | |||
698 | /* Handle pending authentication tasks */ | ||
699 | switch (device.auth.state) | ||
700 | { | ||
701 | case AUST_INIT: | ||
702 | { | ||
703 | /* Send out GetDevAuthenticationInfo */ | ||
704 | IAP_TX_INIT(0x00, 0x14); | ||
705 | |||
706 | iap_send_tx(); | ||
707 | device.auth.state = AUST_CERTREQ; | ||
708 | break; | ||
709 | } | ||
710 | |||
711 | case AUST_CERTDONE: | ||
712 | { | ||
713 | /* Send out GetDevAuthenticationSignature, with | ||
714 | * 20 bytes of challenge and a retry counter of 1. | ||
715 | * Since we do not really care about the content of the | ||
716 | * challenge we just use the first 20 bytes of whatever | ||
717 | * is in the RX buffer right now. | ||
718 | */ | ||
719 | IAP_TX_INIT(0x00, 0x17); | ||
720 | IAP_TX_PUT_DATA(iap_rxstart, 20); | ||
721 | IAP_TX_PUT(0x01); | ||
722 | |||
723 | iap_send_tx(); | ||
724 | device.auth.state = AUST_CHASENT; | ||
725 | break; | ||
726 | } | ||
727 | |||
728 | default: | ||
729 | { | ||
730 | break; | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* Time out button down events */ | ||
735 | if (iap_timeoutbtn) | ||
736 | iap_timeoutbtn -= 1; | ||
737 | |||
738 | if (!iap_timeoutbtn) | ||
739 | { | ||
740 | iap_remotebtn = BUTTON_NONE; | ||
741 | iap_repeatbtn = 0; | ||
742 | iap_btnshuffle = false; | ||
743 | iap_btnrepeat = false; | ||
744 | } | ||
745 | |||
746 | /* Handle power down messages. */ | ||
747 | if (iap_shutdown && device.do_power_notify) | ||
748 | { | ||
749 | /* NotifyiPodStateChange */ | ||
750 | IAP_TX_INIT(0x00, 0x23); | ||
751 | IAP_TX_PUT(0x01); | ||
752 | |||
753 | iap_send_tx(); | ||
754 | |||
755 | /* No further actions, we're going down */ | ||
756 | iap_reset_device(&device); | ||
757 | return; | ||
758 | } | ||
759 | |||
760 | /* Handle GetAccessoryInfo messages */ | ||
761 | if (device.accinfo == ACCST_INIT) | ||
762 | { | ||
763 | /* GetAccessoryInfo */ | ||
764 | IAP_TX_INIT(0x00, 0x27); | ||
765 | IAP_TX_PUT(0x00); | ||
766 | |||
767 | iap_send_tx(); | ||
768 | device.accinfo = ACCST_SENT; | ||
769 | } | ||
770 | |||
771 | /* Do not send requests for device information while | ||
772 | * an authentication is still running, this seems to | ||
773 | * confuse some devices | ||
774 | */ | ||
775 | if (!DEVICE_AUTH_RUNNING && (device.accinfo == ACCST_DATA)) | ||
776 | { | ||
777 | int first_set; | ||
778 | |||
779 | /* Find the first bit set in the capabilities field, | ||
780 | * ignoring those we already asked for | ||
781 | */ | ||
782 | first_set = find_first_set_bit(device.capabilities & (~device.capabilities_queried)); | ||
783 | |||
784 | if (first_set != 32) | ||
785 | { | ||
786 | /* Add bit to queried cababilities */ | ||
787 | device.capabilities_queried |= BIT_N(first_set); | ||
788 | |||
789 | switch (first_set) | ||
790 | { | ||
791 | /* Name */ | ||
792 | case 0x01: | ||
793 | /* Firmware version */ | ||
794 | case 0x04: | ||
795 | /* Hardware version */ | ||
796 | case 0x05: | ||
797 | /* Manufacturer */ | ||
798 | case 0x06: | ||
799 | /* Model number */ | ||
800 | case 0x07: | ||
801 | /* Serial number */ | ||
802 | case 0x08: | ||
803 | /* Maximum payload size */ | ||
804 | case 0x09: | ||
805 | { | ||
806 | IAP_TX_INIT(0x00, 0x27); | ||
807 | IAP_TX_PUT(first_set); | ||
808 | |||
809 | iap_send_tx(); | ||
810 | break; | ||
811 | } | ||
812 | |||
813 | /* Minimum supported iPod firmware version */ | ||
814 | case 0x02: | ||
815 | { | ||
816 | IAP_TX_INIT(0x00, 0x27); | ||
817 | IAP_TX_PUT(2); | ||
818 | IAP_TX_PUT_U32(IAP_IPOD_MODEL); | ||
819 | IAP_TX_PUT(IAP_IPOD_FIRMWARE_MAJOR); | ||
820 | IAP_TX_PUT(IAP_IPOD_FIRMWARE_MINOR); | ||
821 | IAP_TX_PUT(IAP_IPOD_FIRMWARE_REV); | ||
822 | |||
823 | iap_send_tx(); | ||
824 | break; | ||
825 | } | ||
826 | |||
827 | /* Minimum supported lingo version. Queries Lingo 0 */ | ||
828 | case 0x03: | ||
829 | { | ||
830 | IAP_TX_INIT(0x00, 0x27); | ||
831 | IAP_TX_PUT(3); | ||
832 | IAP_TX_PUT(0); | ||
833 | |||
834 | iap_send_tx(); | ||
835 | break; | ||
836 | } | ||
837 | } | ||
838 | |||
839 | device.accinfo = ACCST_SENT; | ||
840 | } | ||
841 | } | ||
842 | |||
843 | if (!device.do_notify) return; | ||
844 | if (device.notifications == 0) return; | ||
845 | |||
846 | /* Volume change notifications are sent every 100ms */ | ||
847 | if (device.notifications & (BIT_N(4) | BIT_N(16))) { | ||
848 | /* Currently we do not track volume changes, so this is | ||
849 | * never sent. | ||
850 | * | ||
851 | * TODO: Fix volume tracking | ||
852 | */ | ||
853 | } | ||
854 | |||
855 | /* All other events are sent every 500ms */ | ||
856 | count += 1; | ||
857 | if (count < 5) return; | ||
858 | |||
859 | count = 0; | ||
860 | |||
861 | /* RemoteEventNotification */ | ||
862 | |||
863 | /* Mode 04 PlayStatusChangeNotification */ | ||
864 | /* Are we in Extended Mode */ | ||
865 | if (interface_state == IST_EXTENDED) { | ||
866 | /* Return Track Position */ | ||
867 | struct mp3entry *id3 = audio_current_track(); | ||
868 | unsigned long time_elapsed = id3->elapsed; | ||
869 | IAP_TX_INIT4(0x04, 0x0027); | ||
870 | IAP_TX_PUT(0x04); | ||
871 | IAP_TX_PUT_U32(time_elapsed); | ||
872 | |||
873 | iap_send_tx(); | ||
874 | } | ||
875 | |||
876 | /* Track position (ms) or Track position (s) */ | ||
877 | if (device.notifications & (BIT_N(0) | BIT_N(15))) | ||
878 | { | ||
879 | uint32_t t; | ||
880 | uint16_t ts; | ||
881 | bool changed; | ||
882 | |||
883 | t = iap_get_trackpos(); | ||
884 | ts = (t / 1000) & 0xFFFF; | ||
885 | |||
886 | if ((device.notifications & BIT_N(0)) && (device.trackpos_ms != t)) | ||
887 | { | ||
888 | IAP_TX_INIT(0x03, 0x09); | ||
889 | IAP_TX_PUT(0x00); | ||
890 | IAP_TX_PUT_U32(t); | ||
891 | device.changed_notifications |= BIT_N(0); | ||
892 | changed = true; | ||
893 | |||
894 | iap_send_tx(); | ||
895 | } | ||
896 | |||
897 | if ((device.notifications & BIT_N(15)) && (device.trackpos_s != ts)) { | ||
898 | IAP_TX_INIT(0x03, 0x09); | ||
899 | IAP_TX_PUT(0x0F); | ||
900 | IAP_TX_PUT_U16(ts); | ||
901 | device.changed_notifications |= BIT_N(15); | ||
902 | changed = true; | ||
903 | |||
904 | iap_send_tx(); | ||
905 | } | ||
906 | |||
907 | if (changed) | ||
908 | { | ||
909 | device.trackpos_ms = t; | ||
910 | device.trackpos_s = ts; | ||
911 | } | ||
912 | } | ||
913 | |||
914 | /* Track index */ | ||
915 | if (device.notifications & BIT_N(1)) | ||
916 | { | ||
917 | uint32_t index; | ||
918 | |||
919 | index = iap_get_trackindex(); | ||
920 | |||
921 | if (device.track_index != index) { | ||
922 | IAP_TX_INIT(0x03, 0x09); | ||
923 | IAP_TX_PUT(0x01); | ||
924 | IAP_TX_PUT_U32(index); | ||
925 | device.changed_notifications |= BIT_N(1); | ||
926 | |||
927 | iap_send_tx(); | ||
928 | |||
929 | device.track_index = index; | ||
930 | } | ||
931 | } | ||
932 | |||
933 | /* Chapter index */ | ||
934 | if (device.notifications & BIT_N(2)) | ||
935 | { | ||
936 | uint32_t index; | ||
937 | |||
938 | index = iap_get_trackindex(); | ||
939 | |||
940 | if (device.track_index != index) | ||
941 | { | ||
942 | IAP_TX_INIT(0x03, 0x09); | ||
943 | IAP_TX_PUT(0x02); | ||
944 | IAP_TX_PUT_U32(index); | ||
945 | IAP_TX_PUT_U16(0); | ||
946 | IAP_TX_PUT_U16(0xFFFF); | ||
947 | device.changed_notifications |= BIT_N(2); | ||
948 | |||
949 | iap_send_tx(); | ||
950 | |||
951 | device.track_index = index; | ||
952 | } | ||
953 | } | ||
954 | |||
955 | /* Play status */ | ||
956 | if (device.notifications & BIT_N(3)) | ||
957 | { | ||
958 | unsigned char play_status; | ||
959 | |||
960 | play_status = audio_status(); | ||
961 | |||
962 | if (device.play_status != play_status) | ||
963 | { | ||
964 | IAP_TX_INIT(0x03, 0x09); | ||
965 | IAP_TX_PUT(0x03); | ||
966 | if (play_status & AUDIO_STATUS_PLAY) { | ||
967 | /* Playing or paused */ | ||
968 | if (play_status & AUDIO_STATUS_PAUSE) { | ||
969 | /* Paused */ | ||
970 | IAP_TX_PUT(0x02); | ||
971 | } else { | ||
972 | /* Playing */ | ||
973 | IAP_TX_PUT(0x01); | ||
974 | } | ||
975 | } else { | ||
976 | IAP_TX_PUT(0x00); | ||
977 | } | ||
978 | device.changed_notifications |= BIT_N(3); | ||
979 | |||
980 | iap_send_tx(); | ||
981 | |||
982 | device.play_status = play_status; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | /* Power/Battery */ | ||
987 | if (device.notifications & BIT_N(5)) | ||
988 | { | ||
989 | unsigned char power_state; | ||
990 | unsigned char battery_l; | ||
991 | |||
992 | power_state = charger_input_state; | ||
993 | battery_l = battery_level(); | ||
994 | |||
995 | if ((device.power_state != power_state) || (device.battery_level != battery_l)) | ||
996 | { | ||
997 | IAP_TX_INIT(0x03, 0x09); | ||
998 | IAP_TX_PUT(0x05); | ||
999 | |||
1000 | iap_fill_power_state(); | ||
1001 | device.changed_notifications |= BIT_N(5); | ||
1002 | |||
1003 | iap_send_tx(); | ||
1004 | |||
1005 | device.power_state = power_state; | ||
1006 | device.battery_level = battery_l; | ||
1007 | } | ||
1008 | } | ||
1009 | |||
1010 | /* Equalizer state | ||
1011 | * This is not handled yet. | ||
1012 | * | ||
1013 | * TODO: Fix equalizer handling | ||
1014 | */ | ||
1015 | |||
1016 | /* Shuffle */ | ||
1017 | if (device.notifications & BIT_N(7)) | ||
1018 | { | ||
1019 | unsigned char shuffle; | ||
1020 | |||
1021 | shuffle = global_settings.playlist_shuffle; | ||
1022 | |||
1023 | if (device.shuffle != shuffle) | ||
1024 | { | ||
1025 | IAP_TX_INIT(0x03, 0x09); | ||
1026 | IAP_TX_PUT(0x07); | ||
1027 | IAP_TX_PUT(shuffle?0x01:0x00); | ||
1028 | device.changed_notifications |= BIT_N(7); | ||
1029 | |||
1030 | iap_send_tx(); | ||
1031 | |||
1032 | device.shuffle = shuffle; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | /* Repeat */ | ||
1037 | if (device.notifications & BIT_N(8)) | ||
1038 | { | ||
1039 | unsigned char repeat; | ||
1040 | |||
1041 | repeat = global_settings.repeat_mode; | ||
1042 | |||
1043 | if (device.repeat != repeat) | ||
1044 | { | ||
1045 | IAP_TX_INIT(0x03, 0x09); | ||
1046 | IAP_TX_PUT(0x08); | ||
1047 | switch (repeat) | ||
1048 | { | ||
1049 | case REPEAT_OFF: | ||
1050 | { | ||
1051 | IAP_TX_PUT(0x00); | ||
1052 | break; | ||
1053 | } | ||
1054 | |||
1055 | case REPEAT_ONE: | ||
1056 | { | ||
1057 | IAP_TX_PUT(0x01); | ||
1058 | break; | ||
1059 | } | ||
1060 | |||
1061 | case REPEAT_ALL: | ||
1062 | { | ||
1063 | IAP_TX_PUT(0x02); | ||
1064 | break; | ||
1065 | } | ||
1066 | } | ||
1067 | device.changed_notifications |= BIT_N(8); | ||
1068 | |||
1069 | iap_send_tx(); | ||
1070 | |||
1071 | device.repeat = repeat; | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | /* Date/Time */ | ||
1076 | if (device.notifications & BIT_N(9)) | ||
1077 | { | ||
1078 | struct tm* tm; | ||
1079 | |||
1080 | tm = get_time(); | ||
1081 | |||
1082 | if (memcmp(tm, &(device.datetime), sizeof(struct tm))) | ||
1083 | { | ||
1084 | IAP_TX_INIT(0x03, 0x09); | ||
1085 | IAP_TX_PUT(0x09); | ||
1086 | IAP_TX_PUT_U16(tm->tm_year); | ||
1087 | |||
1088 | /* Month */ | ||
1089 | IAP_TX_PUT(tm->tm_mon+1); | ||
1090 | |||
1091 | /* Day */ | ||
1092 | IAP_TX_PUT(tm->tm_mday); | ||
1093 | |||
1094 | /* Hour */ | ||
1095 | IAP_TX_PUT(tm->tm_hour); | ||
1096 | |||
1097 | /* Minute */ | ||
1098 | IAP_TX_PUT(tm->tm_min); | ||
1099 | |||
1100 | device.changed_notifications |= BIT_N(9); | ||
1101 | |||
1102 | iap_send_tx(); | ||
1103 | |||
1104 | memcpy(&(device.datetime), tm, sizeof(struct tm)); | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | /* Alarm | ||
1109 | * This is not supported yet. | ||
1110 | * | ||
1111 | * TODO: Fix alarm handling | ||
1112 | */ | ||
1113 | |||
1114 | /* Backlight | ||
1115 | * This is not supported yet. | ||
1116 | * | ||
1117 | * TODO: Fix backlight handling | ||
1118 | */ | ||
1119 | |||
1120 | /* Hold switch */ | ||
1121 | if (device.notifications & BIT_N(0x0C)) | ||
1122 | { | ||
1123 | unsigned char hold; | ||
1124 | |||
1125 | hold = button_hold(); | ||
1126 | if (device.hold != hold) { | ||
1127 | IAP_TX_INIT(0x03, 0x09); | ||
1128 | IAP_TX_PUT(0x0C); | ||
1129 | IAP_TX_PUT(hold?0x01:0x00); | ||
1130 | |||
1131 | device.changed_notifications |= BIT_N(0x0C); | ||
1132 | |||
1133 | iap_send_tx(); | ||
1134 | |||
1135 | device.hold = hold; | ||
1136 | } | ||
1137 | } | ||
1138 | |||
1139 | /* Sound check | ||
1140 | * This is not supported yet. | ||
1141 | * | ||
1142 | * TODO: Fix sound check handling | ||
1143 | */ | ||
1144 | |||
1145 | /* Audiobook check | ||
1146 | * This is not supported yet. | ||
1147 | * | ||
1148 | * TODO: Fix audiobook handling | ||
1149 | */ | ||
1150 | } | ||
1151 | |||
1152 | /* Change the current interface state. | ||
1153 | * On a change from IST_EXTENDED to IST_STANDARD, or from IST_STANDARD | ||
1154 | * to IST_EXTENDED, pause playback, if playing | ||
1155 | */ | ||
1156 | void iap_interface_state_change(const enum interface_state new) | ||
1157 | { | ||
1158 | if (((interface_state == IST_EXTENDED) && (new == IST_STANDARD)) || | ||
1159 | ((interface_state == IST_STANDARD) && (new == IST_EXTENDED))) { | ||
1160 | if (audio_status() == AUDIO_STATUS_PLAY) | ||
1161 | { | ||
1162 | REMOTE_BUTTON(BUTTON_RC_PLAY); | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | interface_state = new; | ||
1167 | } | ||
1168 | |||
1169 | static void iap_handlepkt_mode5(const unsigned int len, const unsigned char *buf) | ||
1170 | { | ||
1171 | (void) len; | ||
1172 | unsigned int cmd = buf[1]; | ||
1173 | switch (cmd) | ||
1174 | { | ||
1175 | /* Sent from iPod Begin Transmission */ | ||
1176 | case 0x02: | ||
1177 | { | ||
1178 | /* RF Transmitter: Begin High Power transmission */ | ||
1179 | unsigned char data0[] = {0x05, 0x02}; | ||
1180 | iap_send_pkt(data0, sizeof(data0)); | ||
1181 | break; | ||
1182 | } | ||
1183 | |||
1184 | /* Sent from iPod End High Power Transmission */ | ||
1185 | case 0x03: | ||
1186 | { | ||
1187 | /* RF Transmitter: End High Power transmission */ | ||
1188 | unsigned char data1[] = {0x05, 0x03}; | ||
1189 | iap_send_pkt(data1, sizeof(data1)); | ||
1190 | break; | ||
1191 | } | ||
1192 | /* Return Version Number ?*/ | ||
1193 | case 0x04: | ||
1194 | { | ||
1195 | /* do nothing */ | ||
1196 | break; | ||
1197 | } | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | #if 0 | ||
1202 | static void iap_handlepkt_mode7(const unsigned int len, const unsigned char *buf) | ||
1203 | { | ||
1204 | unsigned int cmd = buf[1]; | ||
1205 | switch (cmd) | ||
1206 | { | ||
1207 | /* RetTunerCaps */ | ||
1208 | case 0x02: | ||
1209 | { | ||
1210 | /* do nothing */ | ||
1211 | |||
1212 | /* GetAccessoryInfo */ | ||
1213 | unsigned char data[] = {0x00, 0x27, 0x00}; | ||
1214 | iap_send_pkt(data, sizeof(data)); | ||
1215 | break; | ||
1216 | } | ||
1217 | |||
1218 | /* RetTunerFreq */ | ||
1219 | case 0x0A: | ||
1220 | /* fall through */ | ||
1221 | /* TunerSeekDone */ | ||
1222 | case 0x13: | ||
1223 | { | ||
1224 | rmt_tuner_freq(len, buf); | ||
1225 | break; | ||
1226 | } | ||
1227 | |||
1228 | /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/ | ||
1229 | case 0x21: | ||
1230 | { | ||
1231 | rmt_tuner_rds_data(len, buf); | ||
1232 | break; | ||
1233 | } | ||
1234 | } | ||
1235 | } | ||
1236 | #endif | ||
1237 | |||
1238 | void iap_handlepkt(void) | ||
1239 | { | ||
1240 | int level; | ||
1241 | int length; | ||
1242 | |||
1243 | if(!iap_setupflag) return; | ||
1244 | |||
1245 | /* if we are waiting for a remote button to go out, | ||
1246 | delay the handling of the new packet */ | ||
1247 | if(iap_repeatbtn) | ||
1248 | { | ||
1249 | queue_post(&iap_queue, IAP_EV_MSG_RCVD, 0); | ||
1250 | sleep(1); | ||
1251 | return; | ||
1252 | } | ||
1253 | |||
1254 | /* handle command by mode */ | ||
1255 | length = get_u16(iap_rxstart); | ||
1256 | #ifdef LOGF_ENABLE | ||
1257 | logf("R: %s", hexstring(iap_rxstart+2, (length))); | ||
1258 | #endif | ||
1259 | |||
1260 | unsigned char mode = *(iap_rxstart+2); | ||
1261 | switch (mode) { | ||
1262 | case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break; | ||
1263 | case 2: iap_handlepkt_mode2(length, iap_rxstart+2); break; | ||
1264 | case 3: iap_handlepkt_mode3(length, iap_rxstart+2); break; | ||
1265 | case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break; | ||
1266 | case 5: iap_handlepkt_mode5(length, iap_rxstart+2); break; | ||
1267 | /* case 7: iap_handlepkt_mode7(length, iap_rxstart+2); break; */ | ||
1268 | } | ||
1269 | |||
1270 | /* Remove the handled packet from the RX buffer | ||
1271 | * This needs to be done with interrupts disabled, to make | ||
1272 | * sure the buffer and the pointers into it are handled | ||
1273 | * cleanly | ||
1274 | */ | ||
1275 | level = disable_irq_save(); | ||
1276 | memmove(iap_rxstart, iap_rxstart+(length+2), (RX_BUFLEN+2)-(length+2)); | ||
1277 | iap_rxnext -= (length+2); | ||
1278 | iap_rxpayload -= (length+2); | ||
1279 | iap_rxlen += (length+2); | ||
1280 | restore_irq(level); | ||
1281 | |||
1282 | /* poke the poweroff timer */ | ||
1283 | reset_poweroff_timer(); | ||
1284 | } | ||
1285 | |||
1286 | int remote_control_rx(void) | ||
1287 | { | ||
1288 | int btn = iap_remotebtn; | ||
1289 | if(iap_repeatbtn) | ||
1290 | iap_repeatbtn--; | ||
1291 | |||
1292 | return btn; | ||
1293 | } | ||
1294 | |||
1295 | const unsigned char *iap_get_serbuf(void) | ||
1296 | { | ||
1297 | return iap_rxstart; | ||
1298 | } | ||
1299 | |||
1300 | #ifdef IAP_MALLOC_DYNAMIC | ||
1301 | static int iap_move_callback(int handle, void* current, void* new) | ||
1302 | { | ||
1303 | (void) handle; | ||
1304 | (void) current; | ||
1305 | |||
1306 | iap_txstart = new; | ||
1307 | iap_txpayload = iap_txstart+5; | ||
1308 | iap_txnext = iap_txpayload; | ||
1309 | iap_rxstart = iap_buffers+(TX_BUFLEN+6); | ||
1310 | |||
1311 | return BUFLIB_CB_OK; | ||
1312 | } | ||
1313 | #endif | ||
1314 | |||
1315 | /* Change the shuffle state */ | ||
1316 | void iap_shuffle_state(const bool state) | ||
1317 | { | ||
1318 | /* Set shuffle to enabled */ | ||
1319 | if(state && !global_settings.playlist_shuffle) | ||
1320 | { | ||
1321 | global_settings.playlist_shuffle = 1; | ||
1322 | settings_save(); | ||
1323 | if (audio_status() & AUDIO_STATUS_PLAY) | ||
1324 | playlist_randomise(NULL, current_tick, true); | ||
1325 | } | ||
1326 | /* Set shuffle to disabled */ | ||
1327 | else if(!state && global_settings.playlist_shuffle) | ||
1328 | { | ||
1329 | global_settings.playlist_shuffle = 0; | ||
1330 | settings_save(); | ||
1331 | if (audio_status() & AUDIO_STATUS_PLAY) | ||
1332 | playlist_sort(NULL, true); | ||
1333 | } | ||
1334 | } | ||
1335 | |||
1336 | /* Change the repeat state */ | ||
1337 | void iap_repeat_state(const unsigned char state) | ||
1338 | { | ||
1339 | if (state != global_settings.repeat_mode) | ||
1340 | { | ||
1341 | global_settings.repeat_mode = state; | ||
1342 | settings_save(); | ||
1343 | if (audio_status() & AUDIO_STATUS_PLAY) | ||
1344 | audio_flush_and_reload_tracks(); | ||
1345 | } | ||
1346 | } | ||
1347 | |||
1348 | void iap_repeat_next(void) | ||
1349 | { | ||
1350 | switch (global_settings.repeat_mode) | ||
1351 | { | ||
1352 | case REPEAT_OFF: | ||
1353 | { | ||
1354 | iap_repeat_state(REPEAT_ALL); | ||
1355 | break; | ||
1356 | } | ||
1357 | case REPEAT_ALL: | ||
1358 | { | ||
1359 | iap_repeat_state(REPEAT_ONE); | ||
1360 | break; | ||
1361 | } | ||
1362 | case REPEAT_ONE: | ||
1363 | { | ||
1364 | iap_repeat_state(REPEAT_OFF); | ||
1365 | break; | ||
1366 | } | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | /* This function puts the current power/battery state | ||
1371 | * into the TX buffer. The buffer is assumed to be initialized | ||
1372 | */ | ||
1373 | void iap_fill_power_state(void) | ||
1374 | { | ||
1375 | unsigned char power_state; | ||
1376 | unsigned char battery_l; | ||
1377 | |||
1378 | power_state = charger_input_state; | ||
1379 | battery_l = battery_level(); | ||
1380 | |||
1381 | if (power_state == NO_CHARGER) { | ||
1382 | if (battery_l < 30) { | ||
1383 | IAP_TX_PUT(0x00); | ||
1384 | } else { | ||
1385 | IAP_TX_PUT(0x01); | ||
1386 | } | ||
1387 | IAP_TX_PUT((char)((battery_l * 255)/100)); | ||
1388 | } else { | ||
1389 | IAP_TX_PUT(0x04); | ||
1390 | IAP_TX_PUT(0x00); | ||
1391 | } | ||
1392 | } | ||