summaryrefslogtreecommitdiff
path: root/apps/iap.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/iap.c')
-rw-r--r--apps/iap.c1110
1 files changed, 0 insertions, 1110 deletions
diff --git a/apps/iap.c b/apps/iap.c
deleted file mode 100644
index 6fe0a03281..0000000000
--- a/apps/iap.c
+++ /dev/null
@@ -1,1110 +0,0 @@
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.h"
26#include "button.h"
27#include "config.h"
28#include "cpu.h"
29#include "system.h"
30#include "kernel.h"
31#include "serial.h"
32#include "appevents.h"
33
34#include "playlist.h"
35#include "playback.h"
36#include "audio.h"
37#include "settings.h"
38#include "metadata.h"
39#include "wps.h"
40#include "sound.h"
41#include "action.h"
42#include "powermgmt.h"
43
44#include "tuner.h"
45#include "ipod_remote_tuner.h"
46
47#include "filetree.h"
48#include "dir.h"
49
50static volatile int iap_pollspeed = 0;
51static volatile bool iap_remotetick = true;
52static bool iap_setupflag = false, iap_updateflag = false;
53static int iap_changedctr = 0;
54
55static unsigned long iap_remotebtn = 0;
56static int iap_repeatbtn = 0;
57static bool iap_btnrepeat = false, iap_btnshuffle = false;
58
59static unsigned char serbuf[RX_BUFLEN];
60
61static unsigned char response[TX_BUFLEN];
62
63static char cur_dbrecord[5] = {0};
64
65/* states of the iap de-framing state machine */
66enum fsm_state {
67 ST_SYNC, /* wait for 0xFF sync byte */
68 ST_SOF, /* wait for 0x55 start-of-frame byte */
69 ST_LEN, /* receive length byte (small packet) */
70 ST_LENH, /* receive length high byte (large packet) */
71 ST_LENL, /* receive length low byte (large packet) */
72 ST_DATA, /* receive data */
73 ST_CHECK /* verify checksum */
74};
75
76static struct state_t {
77 enum fsm_state state; /* current fsm state */
78 unsigned int len; /* payload data length */
79 unsigned char *payload; /* payload data pointer */
80 unsigned int check; /* running checksum over [len,payload,check] */
81 unsigned int count; /* playload bytes counter */
82} frame_state = {
83 .state = ST_SYNC
84};
85
86static void put_u32(unsigned char *buf, uint32_t data)
87{
88 buf[0] = (data >> 24) & 0xFF;
89 buf[1] = (data >> 16) & 0xFF;
90 buf[2] = (data >> 8) & 0xFF;
91 buf[3] = (data >> 0) & 0xFF;
92}
93
94static uint32_t get_u32(const unsigned char *buf)
95{
96 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
97}
98
99static void iap_task(void)
100{
101 static int count = 0;
102
103 count += iap_pollspeed;
104 if (count < (500/10)) return;
105
106 /* exec every 500ms if pollspeed == 1 */
107 count = 0;
108 queue_post(&button_queue, SYS_IAP_PERIODIC, 0);
109}
110
111/* called by playback when the next track starts */
112static void iap_track_changed(void *ignored)
113{
114 (void)ignored;
115 iap_changedctr = 1;
116}
117
118void iap_setup(int ratenum)
119{
120 iap_bitrate_set(ratenum);
121 iap_pollspeed = 0;
122 iap_remotetick = true;
123 iap_updateflag = false;
124 iap_changedctr = 0;
125 iap_setupflag = true;
126 iap_remotebtn = BUTTON_NONE;
127 tick_add_task(iap_task);
128 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed);
129}
130
131void iap_bitrate_set(int ratenum)
132{
133 switch(ratenum)
134 {
135 case 0:
136 serial_bitrate(0);
137 break;
138 case 1:
139 serial_bitrate(9600);
140 break;
141 case 2:
142 serial_bitrate(19200);
143 break;
144 case 3:
145 serial_bitrate(38400);
146 break;
147 case 4:
148 serial_bitrate(57600);
149 break;
150 }
151}
152
153/* Message format:
154 0xff
155 0x55
156 length
157 mode
158 command (2 bytes)
159 parameters (0-n bytes)
160 checksum (length+mode+parameters+checksum == 0)
161*/
162
163void iap_send_pkt(const unsigned char * data, int len)
164{
165 int i, chksum, responselen;
166
167 if(len > TX_BUFLEN-4) len = TX_BUFLEN-4;
168 responselen = len + 4;
169
170 response[0] = 0xFF;
171 response[1] = 0x55;
172
173 chksum = response[2] = len;
174 for(i = 0; i < len; i ++)
175 {
176 chksum += data[i];
177 response[i+3] = data[i];
178 }
179
180 response[i+3] = 0x100 - (chksum & 0xFF);
181
182 for(i = 0; i < responselen; i ++)
183 {
184 while (!tx_rdy()) ;
185 tx_writec(response[i]);
186 }
187}
188
189bool iap_getc(unsigned char x)
190{
191 struct state_t *s = &frame_state;
192
193 /* run state machine to detect and extract a valid frame */
194 switch (s->state) {
195 case ST_SYNC:
196 if (x == 0xFF) {
197 s->state = ST_SOF;
198 }
199 break;
200 case ST_SOF:
201 if (x == 0x55) {
202 /* received a valid sync/SOF pair */
203 s->state = ST_LEN;
204 } else {
205 s->state = ST_SYNC;
206 return iap_getc(x);
207 }
208 break;
209 case ST_LEN:
210 s->check = x;
211 s->count = 0;
212 s->payload = serbuf;
213 if (x == 0) {
214 /* large packet */
215 s->state = ST_LENH;
216 } else {
217 /* small packet */
218 s->len = x;
219 s->state = ST_DATA;
220 }
221 break;
222 case ST_LENH:
223 s->check += x;
224 s->len = x << 8;
225 s->state = ST_LENL;
226 break;
227 case ST_LENL:
228 s->check += x;
229 s->len += x;
230 if ((s->len == 0) || (s->len > RX_BUFLEN)) {
231 /* invalid length */
232 s->state = ST_SYNC;
233 return iap_getc(x);
234 } else {
235 s->state = ST_DATA;
236 }
237 break;
238 case ST_DATA:
239 s->check += x;
240 s->payload[s->count++] = x;
241 if (s->count == s->len) {
242 s->state = ST_CHECK;
243 }
244 break;
245 case ST_CHECK:
246 s->check += x;
247 if ((s->check & 0xFF) == 0) {
248 /* done, received a valid frame */
249 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
250 }
251 s->state = ST_SYNC;
252 break;
253 default:
254 panicf("Unhandled iap state %d", (int) s->state);
255 break;
256 }
257
258 /* return true while still hunting for the sync and start-of-frame byte */
259 return (s->state == ST_SYNC) || (s->state == ST_SOF);
260}
261
262void iap_periodic(void)
263{
264 if(!iap_setupflag) return;
265 if(!iap_pollspeed) return;
266
267 /* PlayStatusChangeNotification */
268 unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
269 unsigned long time_elapsed = audio_current_track()->elapsed;
270
271 time_elapsed += wps_get_ff_rewind_count();
272
273 data[3] = 0x04; /* playing */
274
275 /* If info has changed, don't flag it right away */
276 if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
277 {
278 /* track info has changed */
279 iap_changedctr = 0;
280 data[3] = 0x01; /* 0x02 has same effect? */
281 iap_updateflag = true;
282 }
283
284 put_u32(&data[4], time_elapsed);
285 iap_send_pkt(data, sizeof(data));
286}
287
288static void iap_set_remote_volume(void)
289{
290 unsigned char data[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
291 data[4] = (char)((global_settings.volume+58) * 4);
292 iap_send_pkt(data, sizeof(data));
293}
294
295static void cmd_ok_mode0(unsigned char cmd)
296{
297 unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
298 data[3] = cmd; /* respond with cmd */
299 iap_send_pkt(data, sizeof(data));
300}
301
302static void iap_handlepkt_mode0(unsigned int len, const unsigned char *buf)
303{
304 (void)len; /* len currently unused */
305
306 unsigned int cmd = buf[1];
307 switch (cmd) {
308 /* Identify */
309 case 0x01:
310 {
311 /* FM transmitter sends this: */
312 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
313 if(buf[2] == 0x05)
314 {
315 sleep(HZ/3);
316 /* RF Transmitter: Begin transmission */
317 unsigned char data[] = {0x05, 0x02};
318 iap_send_pkt(data, sizeof(data));
319 }
320 /* FM remote sends this: */
321 /* FF 55 03 00 01 02 FA (1st thing sent) */
322 else if (buf[2] == 0x02)
323 {
324 /* useful only for apple firmware */
325 }
326 break;
327 }
328
329 /* EnterRemoteUIMode, FM transmitter sends FF 55 02 00 05 F9 */
330 case 0x05:
331 {
332 /* ACK Pending (3000 ms) */
333 unsigned char data[] = {0x00, 0x02, 0x06,
334 0x05, 0x00, 0x00, 0x0B, 0xB8};
335 iap_send_pkt(data, sizeof(data));
336 cmd_ok_mode0(cmd);
337 break;
338 }
339
340 /* ExitRemoteUIMode */
341 case 0x06:
342 {
343 audio_stop();
344 cmd_ok_mode0(cmd);
345 break;
346 }
347
348 /* RequestiPodSoftwareVersion, Ipod FM remote sends FF 55 02 00 09 F5 */
349 case 0x09:
350 {
351 /* ReturniPodSoftwareVersion, ipod5G firmware version */
352 unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01};
353 iap_send_pkt(data, sizeof(data));
354 break;
355 }
356
357 /* RequestiPodModelNum */
358 case 0x0D:
359 {
360 /* ipod is supposed to work only with 5G and nano 2G */
361 /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34,
362 0x37, 0x4C, 0x4C, 0x00}; PA147LL (IPOD 5G 60 GO) */
363 /* ReturniPodModelNum */
364 unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
365 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
366 iap_send_pkt(data, sizeof(data));
367 break;
368 }
369
370 /* RequestLingoProtocolVersion */
371 case 0x0F:
372 {
373 /* ReturnLingoProtocolVersion */
374 unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
375 data[2] = buf[2];
376 iap_send_pkt(data, sizeof(data));
377 break;
378 }
379
380 /* IdentifyDeviceLingoes */
381 case 0x13:
382 {
383 cmd_ok_mode0(cmd);
384
385 uint32_t lingoes = get_u32(&buf[2]);
386
387 if (lingoes == 0x35)
388 /* FM transmitter sends this: */
389 /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
390 {
391 /* GetAccessoryInfo */
392 unsigned char data2[] = {0x00, 0x27, 0x00};
393 iap_send_pkt(data2, sizeof(data2));
394 /* RF Transmitter: Begin transmission */
395 unsigned char data3[] = {0x05, 0x02};
396 iap_send_pkt(data3, sizeof(data3));
397 }
398 else
399 {
400 /* ipod fm remote sends this: */
401 /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
402 if (lingoes & (1 << 7)) /* bit 7 = RF tuner lingo */
403 radio_present = 1;
404 /* GetDevAuthenticationInfo */
405 unsigned char data4[] = {0x00, 0x14};
406 iap_send_pkt(data4, sizeof(data4));
407 }
408 break;
409 }
410
411 /* RetDevAuthenticationInfo */
412 case 0x15:
413 {
414 /* AckDevAuthenticationInfo */
415 unsigned char data0[] = {0x00, 0x16, 0x00};
416 iap_send_pkt(data0, sizeof(data0));
417 /* GetAccessoryInfo */
418 unsigned char data1[] = {0x00, 0x27, 0x00};
419 iap_send_pkt(data1, sizeof(data1));
420 /* AckDevAuthenticationStatus, mandatory to enable some hardware */
421 unsigned char data2[] = {0x00, 0x19, 0x00};
422 iap_send_pkt(data2, sizeof(data2));
423 if (radio_present == 1)
424 {
425 /* GetTunerCaps */
426 unsigned char data3[] = {0x07, 0x01};
427 iap_send_pkt(data3, sizeof(data3));
428 }
429 iap_set_remote_volume();
430 break;
431 }
432
433 /* RetDevAuthenticationSignature */
434 case 0x18:
435 {
436 /* Isn't used since we don't send the 0x00 0x17 command */
437 break;
438 }
439
440 /* GetIpodOptions */
441 case 0x24:
442 {
443 /* RetIpodOptions (ipod video send this) */
444 unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
445 0x00, 0x00, 0x00, 0x00, 0x01};
446 iap_send_pkt(data, sizeof(data));
447 break;
448 }
449
450 /* default response is with cmd ok packet */
451 default:
452 {
453 cmd_ok_mode0(cmd);
454 break;
455 }
456 }
457}
458
459static void iap_handlepkt_mode2(unsigned int len, const unsigned char *buf)
460{
461 if(buf[1] != 0) return;
462 iap_remotebtn = BUTTON_NONE;
463 iap_remotetick = false;
464
465 if(len >= 3 && buf[2] != 0)
466 {
467 if(buf[2] & 1)
468 iap_remotebtn |= BUTTON_RC_PLAY;
469 if(buf[2] & 2)
470 iap_remotebtn |= BUTTON_RC_VOL_UP;
471 if(buf[2] & 4)
472 iap_remotebtn |= BUTTON_RC_VOL_DOWN;
473 if(buf[2] & 8)
474 iap_remotebtn |= BUTTON_RC_RIGHT;
475 if(buf[2] & 16)
476 iap_remotebtn |= BUTTON_RC_LEFT;
477 }
478 else if(len >= 4 && buf[3] != 0)
479 {
480 if(buf[3] & 1) /* play */
481 {
482 if (audio_status() != AUDIO_STATUS_PLAY)
483 {
484 iap_remotebtn |= BUTTON_RC_PLAY;
485 iap_repeatbtn = 2;
486 iap_remotetick = false;
487 iap_changedctr = 1;
488 }
489 }
490 if(buf[3] & 2) /* pause */
491 {
492 if (audio_status() == AUDIO_STATUS_PLAY)
493 {
494 iap_remotebtn |= BUTTON_RC_PLAY;
495 iap_repeatbtn = 2;
496 iap_remotetick = false;
497 iap_changedctr = 1;
498 }
499 }
500 if((buf[3] & 128) && !iap_btnshuffle) /* shuffle */
501 {
502 iap_btnshuffle = true;
503 if(!global_settings.playlist_shuffle)
504 {
505 global_settings.playlist_shuffle = 1;
506 settings_save();
507 if (audio_status() & AUDIO_STATUS_PLAY)
508 playlist_randomise(NULL, current_tick, true);
509 }
510 else if(global_settings.playlist_shuffle)
511 {
512 global_settings.playlist_shuffle = 0;
513 settings_save();
514 if (audio_status() & AUDIO_STATUS_PLAY)
515 playlist_sort(NULL, true);
516 }
517 }
518 else
519 iap_btnshuffle = false;
520 }
521 else if(len >= 5 && buf[4] != 0)
522 {
523 if((buf[4] & 1) && !iap_btnrepeat) /* repeat */
524 {
525 int oldmode = global_settings.repeat_mode;
526 iap_btnrepeat = true;
527
528 if (oldmode == REPEAT_ONE)
529 global_settings.repeat_mode = REPEAT_OFF;
530 else if (oldmode == REPEAT_ALL)
531 global_settings.repeat_mode = REPEAT_ONE;
532 else if (oldmode == REPEAT_OFF)
533 global_settings.repeat_mode = REPEAT_ALL;
534
535 settings_save();
536 if (audio_status() & AUDIO_STATUS_PLAY)
537 audio_flush_and_reload_tracks();
538 }
539 else
540 iap_btnrepeat = false;
541
542 if(buf[4] & 16) /* ffwd */
543 {
544 iap_remotebtn |= BUTTON_RC_RIGHT;
545 }
546 if(buf[4] & 32) /* frwd */
547 {
548 iap_remotebtn |= BUTTON_RC_LEFT;
549 }
550 }
551}
552
553static void iap_handlepkt_mode3(unsigned int len, const unsigned char *buf)
554{
555 (void)len; /* len currently unused */
556
557 unsigned int cmd = buf[1];
558 switch (cmd)
559 {
560 /* GetCurrentEQProfileIndex */
561 case 0x01:
562 {
563 /* RetCurrentEQProfileIndex */
564 unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
565 iap_send_pkt(data, sizeof(data));
566 break;
567 }
568
569 /* SetRemoteEventNotification */
570 case 0x08:
571 {
572 /* ACK */
573 unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
574 iap_send_pkt(data, sizeof(data));
575 break;
576 }
577
578 /* GetiPodStateInfo */
579 case 0x0C:
580 {
581 /* request ipod volume */
582 if (buf[2] == 0x04)
583 {
584 iap_set_remote_volume();
585 }
586 break;
587 }
588
589 /* SetiPodStateInfo */
590 case 0x0E:
591 {
592 if (buf[2] == 0x04)
593 global_settings.volume = (-58)+((int)buf[4]+1)/4;
594 sound_set_volume(global_settings.volume); /* indent BUG? */
595 break;
596 }
597 }
598}
599
600static void cmd_ok_mode4(unsigned int cmd)
601{
602 unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
603 data[4] = (cmd >> 8) & 0xFF;
604 data[5] = (cmd >> 0) & 0xFF;
605 iap_send_pkt(data, sizeof(data));
606}
607
608static void get_playlist_name(unsigned char *dest,
609 unsigned long item_offset,
610 size_t max_length)
611{
612 if (item_offset == 0) return;
613 DIR* dp;
614 struct dirent* playlist_file = NULL;
615
616 dp = opendir(global_settings.playlist_catalog_dir);
617
618 char *extension;
619 unsigned long nbr = 0;
620 while ((nbr < item_offset) && ((playlist_file = readdir(dp)) != NULL))
621 {
622 /*Increment only if there is a playlist extension*/
623 if ((extension=strrchr(playlist_file->d_name, '.')) != NULL){
624 if ((strcmp(extension, ".m3u") == 0 ||
625 strcmp(extension, ".m3u8") == 0))
626 nbr++;
627 }
628 }
629 if (playlist_file != NULL) {
630 strlcpy(dest, playlist_file->d_name, max_length);
631 }
632 closedir(dp);
633}
634
635static void iap_handlepkt_mode4(unsigned int len, const unsigned char *buf)
636{
637 (void)len; /* len currently unused */
638
639 unsigned int cmd = (buf[1] << 8) | buf[2];
640 switch (cmd)
641 {
642 /* GetAudioBookSpeed */
643 case 0x0009:
644 {
645 /* ReturnAudioBookSpeed */
646 unsigned char data[] = {0x04, 0x00, 0x0A, 0x00};
647 data[3] = iap_updateflag ? 0 : 1;
648 iap_send_pkt(data, sizeof(data));
649 break;
650 }
651
652 /* SetAudioBookSpeed */
653 case 0x000B:
654 {
655 iap_updateflag = buf[3] ? 0 : 1;
656 /* respond with cmd ok packet */
657 cmd_ok_mode4(cmd);
658 break;
659 }
660
661 /* RequestProtocolVersion */
662 case 0x0012:
663 {
664 /* ReturnProtocolVersion */
665 unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
666 iap_send_pkt(data, sizeof(data));
667 break;
668 }
669
670 /* SelectDBRecord */
671 case 0x0017:
672 {
673 memcpy(cur_dbrecord, buf + 3, 5);
674 cmd_ok_mode4(cmd);
675 break;
676 }
677
678 /* GetNumberCategorizedDBRecords */
679 case 0x0018:
680 {
681 /* ReturnNumberCategorizedDBRecords */
682 unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
683 unsigned long num = 0;
684
685 DIR* dp;
686 unsigned long nbr_total_playlists = 0;
687 struct dirent* playlist_file = NULL;
688 char *extension;
689
690 switch(buf[3]) /* type number */
691 {
692 case 0x01: /* total number of playlists */
693 dp = opendir(global_settings.playlist_catalog_dir);
694 while ((playlist_file = readdir(dp)) != NULL)
695 {
696 /*Increment only if there is a playlist extension*/
697 if ((extension=strrchr(playlist_file->d_name, '.'))
698 != NULL) {
699 if ((strcmp(extension, ".m3u") == 0 ||
700 strcmp(extension, ".m3u8") == 0))
701 nbr_total_playlists++;
702 }
703 }
704 closedir(dp);
705 /*Add 1 for the main playlist*/
706 num = nbr_total_playlists + 1;
707 break;
708 case 0x05: /* total number of songs */
709 num = 1;
710 break;
711 }
712 put_u32(&data[3], num);
713 iap_send_pkt(data, sizeof(data));
714 break;
715 }
716
717 /* RetrieveCategorizedDatabaseRecords */
718 case 0x001A:
719 {
720 /* ReturnCategorizedDatabaseRecord */
721 unsigned char data[7 + MAX_PATH] =
722 {0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00,
723 'R', 'O', 'C', 'K', 'B', 'O', 'X', '\0'};
724
725 unsigned long item_offset = get_u32(&buf[4]);
726
727 get_playlist_name(data + 7, item_offset, MAX_PATH);
728 /*Remove file extension*/
729 char *dot=NULL;
730 dot = (strrchr(data+7, '.'));
731 if (dot != NULL)
732 *dot = '\0';
733 iap_send_pkt(data, 7 + strlen(data+7) + 1);
734 break;
735 }
736
737 /* GetPlayStatus */
738 case 0x001C:
739 {
740 /* ReturnPlayStatus */
741 unsigned char data[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
742 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
743 struct mp3entry *id3 = audio_current_track();
744 unsigned long time_total = id3->length;
745 unsigned long time_elapsed = id3->elapsed;
746 int status = audio_status();
747 put_u32(&data[3], time_total);
748 put_u32(&data[7], time_elapsed);
749 if (status == AUDIO_STATUS_PLAY)
750 data[11] = 0x01; /* play */
751 else if (status & AUDIO_STATUS_PAUSE)
752 data[11] = 0x02; /* pause */
753 iap_send_pkt(data, sizeof(data));
754 break;
755 }
756
757 /* GetCurrentPlayingTrackIndex */
758 case 0x001E:
759 {
760 /* ReturnCurrentPlayingTrackIndex */
761 unsigned char data[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00};
762 long playlist_pos = playlist_next(0);
763 playlist_pos -= playlist_get_first_index(NULL);
764 if(playlist_pos < 0)
765 playlist_pos += playlist_amount();
766 put_u32(&data[3], playlist_pos);
767 iap_send_pkt(data, sizeof(data));
768 break;
769 }
770
771 /* GetIndexedPlayingTrackTitle */
772 case 0x0020:
773 /* GetIndexedPlayingTrackArtistName */
774 case 0x0022:
775 /* GetIndexedPlayingTrackAlbumName */
776 case 0x0024:
777 {
778 unsigned char data[70] = {0x04, 0x00, 0xFF};
779 struct mp3entry id3;
780 int fd;
781 size_t len;
782 long tracknum = get_u32(&buf[3]);
783
784 data[2] = cmd + 1;
785 memcpy(&id3, audio_current_track(), sizeof(id3));
786 tracknum += playlist_get_first_index(NULL);
787 if(tracknum >= playlist_amount())
788 tracknum -= playlist_amount();
789
790 /* If the tracknumber is not the current one,
791 read id3 from disk */
792 if(playlist_next(0) != tracknum)
793 {
794 struct playlist_track_info info;
795 playlist_get_track_info(NULL, tracknum, &info);
796 fd = open(info.filename, O_RDONLY);
797 memset(&id3, 0, sizeof(struct mp3entry));
798 get_metadata(&id3, fd, info.filename);
799 close(fd);
800 }
801
802 /* Return the requested track data */
803 switch(cmd)
804 {
805 case 0x20:
806 len = strlcpy((char *)&data[3], id3.title, 64);
807 iap_send_pkt(data, 4+len);
808 break;
809 case 0x22:
810 len = strlcpy((char *)&data[3], id3.artist, 64);
811 iap_send_pkt(data, 4+len);
812 break;
813 case 0x24:
814 len = strlcpy((char *)&data[3], id3.album, 64);
815 iap_send_pkt(data, 4+len);
816 break;
817 }
818 break;
819 }
820
821 /* SetPlayStatusChangeNotification */
822 case 0x0026:
823 {
824 iap_pollspeed = buf[3] ? 1 : 0;
825 /* respond with cmd ok packet */
826 cmd_ok_mode4(cmd);
827 break;
828 }
829
830 /* PlayCurrentSelection */
831 case 0x0028:
832 {
833 switch (cur_dbrecord[0])
834 {
835 case 0x01:
836 {/*Playlist*/
837 unsigned long item_offset = get_u32(&cur_dbrecord[1]);
838
839 unsigned char selected_playlist
840 [sizeof(global_settings.playlist_catalog_dir)
841 + 1
842 + MAX_PATH] = {0};
843
844 strcpy(selected_playlist,
845 global_settings.playlist_catalog_dir);
846 int len = strlen(selected_playlist);
847 selected_playlist[len] = '/';
848 get_playlist_name (selected_playlist + len + 1,
849 item_offset,
850 MAX_PATH);
851 ft_play_playlist(selected_playlist,
852 global_settings.playlist_catalog_dir,
853 strrchr(selected_playlist, '/') + 1);
854 break;
855 }
856 }
857 cmd_ok_mode4(cmd);
858 break;
859 }
860
861 /* PlayControl */
862 case 0x0029:
863 {
864 switch(buf[3])
865 {
866 case 0x01: /* play/pause */
867 iap_remotebtn = BUTTON_RC_PLAY;
868 iap_repeatbtn = 2;
869 iap_remotetick = false;
870 iap_changedctr = 1;
871 break;
872 case 0x02: /* stop */
873 iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT;
874 iap_repeatbtn = 2;
875 iap_remotetick = false;
876 iap_changedctr = 1;
877 break;
878 case 0x03: /* skip++ */
879 iap_remotebtn = BUTTON_RC_RIGHT;
880 iap_repeatbtn = 2;
881 iap_remotetick = false;
882 break;
883 case 0x04: /* skip-- */
884 iap_remotebtn = BUTTON_RC_LEFT;
885 iap_repeatbtn = 2;
886 iap_remotetick = false;
887 break;
888 case 0x05: /* ffwd */
889 iap_remotebtn = BUTTON_RC_RIGHT;
890 iap_remotetick = false;
891 if(iap_pollspeed) iap_pollspeed = 5;
892 break;
893 case 0x06: /* frwd */
894 iap_remotebtn = BUTTON_RC_LEFT;
895 iap_remotetick = false;
896 if(iap_pollspeed) iap_pollspeed = 5;
897 break;
898 case 0x07: /* end ffwd/frwd */
899 iap_remotebtn = BUTTON_NONE;
900 iap_remotetick = false;
901 if(iap_pollspeed) iap_pollspeed = 1;
902 break;
903 }
904 /* respond with cmd ok packet */
905 cmd_ok_mode4(cmd);
906 break;
907 }
908
909 /* GetShuffle */
910 case 0x002C:
911 {
912 /* ReturnShuffle */
913 unsigned char data[] = {0x04, 0x00, 0x2D, 0x00};
914 data[3] = global_settings.playlist_shuffle ? 1 : 0;
915 iap_send_pkt(data, sizeof(data));
916 break;
917 }
918
919 /* SetShuffle */
920 case 0x002E:
921 {
922 if(buf[3] && !global_settings.playlist_shuffle)
923 {
924 global_settings.playlist_shuffle = 1;
925 settings_save();
926 if (audio_status() & AUDIO_STATUS_PLAY)
927 playlist_randomise(NULL, current_tick, true);
928 }
929 else if(!buf[3] && global_settings.playlist_shuffle)
930 {
931 global_settings.playlist_shuffle = 0;
932 settings_save();
933 if (audio_status() & AUDIO_STATUS_PLAY)
934 playlist_sort(NULL, true);
935 }
936
937 /* respond with cmd ok packet */
938 cmd_ok_mode4(cmd);
939 break;
940 }
941
942 /* GetRepeat */
943 case 0x002F:
944 {
945 /* ReturnRepeat */
946 unsigned char data[] = {0x04, 0x00, 0x30, 0x00};
947 if(global_settings.repeat_mode == REPEAT_OFF)
948 data[3] = 0;
949 else if(global_settings.repeat_mode == REPEAT_ONE)
950 data[3] = 1;
951 else
952 data[3] = 2;
953 iap_send_pkt(data, sizeof(data));
954 break;
955 }
956
957 /* SetRepeat */
958 case 0x0031:
959 {
960 int oldmode = global_settings.repeat_mode;
961 if (buf[3] == 0)
962 global_settings.repeat_mode = REPEAT_OFF;
963 else if (buf[3] == 1)
964 global_settings.repeat_mode = REPEAT_ONE;
965 else if (buf[3] == 2)
966 global_settings.repeat_mode = REPEAT_ALL;
967
968 if (oldmode != global_settings.repeat_mode)
969 {
970 settings_save();
971 if (audio_status() & AUDIO_STATUS_PLAY)
972 audio_flush_and_reload_tracks();
973 }
974
975 /* respond with cmd ok packet */
976 cmd_ok_mode4(cmd);
977 break;
978 }
979
980 /* GetMonoDisplayImageLimits */
981 case 0x0033:
982 {
983 /* ReturnMonoDisplayImageLimits */
984 unsigned char data[] = {0x04, 0x00, 0x34,
985 LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
986 LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
987 0x01};
988 iap_send_pkt(data, sizeof(data));
989 break;
990 }
991
992 /* GetNumPlayingTracks */
993 case 0x0035:
994 {
995 /* ReturnNumPlayingTracks */
996 unsigned char data[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00};
997 unsigned long playlist_amt = playlist_amount();
998 put_u32(&data[3], playlist_amt);
999 iap_send_pkt(data, sizeof(data));
1000 break;
1001 }
1002
1003 /* SetCurrentPlayingTrack */
1004 case 0x0037:
1005 {
1006 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
1007 long tracknum = get_u32(&buf[3]);
1008
1009 audio_pause();
1010 audio_skip(tracknum - playlist_next(0));
1011 if (!paused)
1012 audio_resume();
1013
1014 /* respond with cmd ok packet */
1015 cmd_ok_mode4(cmd);
1016 break;
1017 }
1018
1019 default:
1020 {
1021 /* default response is with cmd ok packet */
1022 cmd_ok_mode4(cmd);
1023 break;
1024 }
1025 }
1026}
1027
1028static void iap_handlepkt_mode7(unsigned int len, const unsigned char *buf)
1029{
1030 unsigned int cmd = buf[1];
1031 switch (cmd)
1032 {
1033 /* RetTunerCaps */
1034 case 0x02:
1035 {
1036 /* do nothing */
1037
1038 /* GetAccessoryInfo */
1039 unsigned char data[] = {0x00, 0x27, 0x00};
1040 iap_send_pkt(data, sizeof(data));
1041 break;
1042 }
1043
1044 /* RetTunerFreq */
1045 case 0x0A:
1046 /* fall through */
1047 /* TunerSeekDone */
1048 case 0x13:
1049 {
1050 rmt_tuner_freq(len, buf);
1051 break;
1052 }
1053
1054 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
1055 case 0x21:
1056 {
1057 rmt_tuner_rds_data(len, buf);
1058 break;
1059 }
1060 }
1061}
1062
1063void iap_handlepkt(void)
1064{
1065 struct state_t *s = &frame_state;
1066
1067 if(!iap_setupflag) return;
1068
1069 /* if we are waiting for a remote button to go out,
1070 delay the handling of the new packet */
1071 if(!iap_remotetick)
1072 {
1073 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
1074 return;
1075 }
1076
1077 /* handle command by mode */
1078 unsigned char mode = s->payload[0];
1079 switch (mode) {
1080 case 0: iap_handlepkt_mode0(s->len, s->payload); break;
1081 case 2: iap_handlepkt_mode2(s->len, s->payload); break;
1082 case 3: iap_handlepkt_mode3(s->len, s->payload); break;
1083 case 4: iap_handlepkt_mode4(s->len, s->payload); break;
1084 case 7: iap_handlepkt_mode7(s->len, s->payload); break;
1085 }
1086}
1087
1088int remote_control_rx(void)
1089{
1090 int btn = iap_remotebtn;
1091 if(iap_repeatbtn)
1092 {
1093 iap_repeatbtn--;
1094 if(!iap_repeatbtn)
1095 {
1096 iap_remotebtn = BUTTON_NONE;
1097 iap_remotetick = true;
1098 }
1099 }
1100 else
1101 iap_remotetick = true;
1102
1103 return btn;
1104}
1105
1106const unsigned char *iap_get_serbuf(void)
1107{
1108 return serbuf;
1109}
1110