summaryrefslogtreecommitdiff
path: root/apps/iap/iap-core.c
diff options
context:
space:
mode:
authorRalf Ertzinger <rockbox@camperquake.de>2013-06-22 10:08:23 +0100
committerFrank Gevaerts <frank@gevaerts.be>2013-11-10 18:41:24 +0100
commitb170c73f922e3457b923b4e7fcbec794a8885c77 (patch)
tree89fbdbd8c25af5101a29a1ede3b896332a4e205c /apps/iap/iap-core.c
parent500b137308a6ee5c2aba873734a8956d70472f56 (diff)
downloadrockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.tar.gz
rockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.zip
Updated IAP commands.
Originally written and uploaded by Lalufu (Ralf Ertzinger) in Feb 2012. They have been condensed into a single patch and some further additions by Andy Potter. Currently includes Authentication V2 support from iPod to Accessory, RF/BlueTooth transmitter support, selecting a playlist and selecting a track from the current playlist. Does not support uploading Album Art or podcasts. Has been tested on the following iPods, 4th Gen Grayscale, 4th Gen Color/Photo, Mini 2nd Gen, Nano 1st Gen and Video 5.5Gen. Change-Id: Ie8fc098361844132f0228ecbe3c48da948726f5e Co-Authored by: Andy Potter <liveboxandy@gmail.com> Reviewed-on: http://gerrit.rockbox.org/533 Reviewed-by: Frank Gevaerts <frank@gevaerts.be>
Diffstat (limited to 'apps/iap/iap-core.c')
-rw-r--r--apps/iap/iap-core.c1392
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
70static bool iap_started = false;
71static 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 */
75static bool iap_shutdown = false;
76static struct timeout iap_task_tmo;
77
78unsigned 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 */
83int 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 */
94unsigned int iap_timeoutbtn = 0;
95bool iap_btnrepeat = false, iap_btnshuffle = false;
96
97static long thread_stack[(DEFAULT_STACK_SIZE*6)/sizeof(long)];
98static 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
141static int iap_buffer_handle;
142#endif
143static unsigned char* iap_buffers;
144static unsigned char* iap_rxstart;
145static unsigned char* iap_rxpayload;
146static unsigned char* iap_rxnext;
147static uint32_t iap_rxlen;
148static unsigned char* iap_txstart;
149unsigned char* iap_txpayload;
150unsigned char* iap_txnext;
151
152/* The versions of the various Lingoes we support. A major version
153 * of 0 means unsupported
154 */
155unsigned 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 */
166enum 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
176static 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
185enum interface_state interface_state = IST_STANDARD;
186
187struct device_t device;
188
189#ifdef IAP_MALLOC_DYNAMIC
190static int iap_move_callback(int handle, void* current, void* new);
191
192static struct buflib_callbacks iap_buflib_callbacks = {
193 iap_move_callback,
194 NULL
195};
196#endif
197
198static void iap_malloc(void);
199
200void put_u16(unsigned char *buf, const uint16_t data)
201{
202 buf[0] = (data >> 8) & 0xFF;
203 buf[1] = (data >> 0) & 0xFF;
204}
205
206void 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
214uint32_t get_u32(const unsigned char *buf)
215{
216 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
217}
218
219uint16_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 */
231static 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
258void 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
278void 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
285void 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
298static 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 */
309static 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 */
352static 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 */
374void 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 */
388static 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
413static 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
440void 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 */
477void 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 */
527void 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
537bool 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
652void 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
678uint32_t iap_get_trackpos(void)
679{
680 struct mp3entry *id3 = audio_current_track();
681
682 return id3->elapsed;
683}
684
685uint32_t iap_get_trackindex(void)
686{
687 struct playlist_info* playlist = playlist_get_current();
688
689 return (playlist->index - playlist->first_index);
690}
691
692void 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 */
1156void 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
1169static 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
1202static 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
1238void 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
1286int remote_control_rx(void)
1287{
1288 int btn = iap_remotebtn;
1289 if(iap_repeatbtn)
1290 iap_repeatbtn--;
1291
1292 return btn;
1293}
1294
1295const unsigned char *iap_get_serbuf(void)
1296{
1297 return iap_rxstart;
1298}
1299
1300#ifdef IAP_MALLOC_DYNAMIC
1301static 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 */
1316void 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 */
1337void 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
1348void 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 */
1373void 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}