FS#12079 - Support for new commands in iap.c, by Ophir Lojkine
[kugel-rb.git] / apps / iap.c
blobfa2c7d6beb6922605aa7105627f54628bba45edf
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Alan Korr & Nick Robinson
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #include "iap.h"
25 #include "button.h"
26 #include "config.h"
27 #include "cpu.h"
28 #include "system.h"
29 #include "kernel.h"
30 #include "serial.h"
31 #include "appevents.h"
33 #include "playlist.h"
34 #include "playback.h"
35 #include "audio.h"
36 #include "settings.h"
37 #include "metadata.h"
38 #include "wps.h"
39 #include "sound.h"
40 #include "action.h"
41 #include "powermgmt.h"
43 #include "tuner.h"
44 #include "ipod_remote_tuner.h"
46 #define MAX_NAME_LENGTH 20
47 #include "filetree.h"
48 #include "dir.h"
50 static volatile int iap_pollspeed = 0;
51 static volatile bool iap_remotetick = true;
52 static bool iap_setupflag = false, iap_updateflag = false;
53 static int iap_changedctr = 0;
55 static unsigned long iap_remotebtn = 0;
56 static int iap_repeatbtn = 0;
57 static bool iap_btnrepeat = false, iap_btnshuffle = false;
59 static unsigned char serbuf[RX_BUFLEN];
60 static int serbuf_i = 0;
62 static unsigned char response[TX_BUFLEN];
63 static int responselen;
65 static char cur_dbrecord[5] = {0};
67 static void iap_task(void)
69 static int count = 0;
71 count += iap_pollspeed;
72 if (count < (500/10)) return;
74 /* exec every 500ms if pollspeed == 1 */
75 count = 0;
76 queue_post(&button_queue, SYS_IAP_PERIODIC, 0);
79 /* called by playback when the next track starts */
80 static void iap_track_changed(void *ignored)
82 (void)ignored;
83 iap_changedctr = 1;
86 void iap_setup(int ratenum)
88 iap_bitrate_set(ratenum);
89 iap_pollspeed = 0;
90 iap_remotetick = true;
91 iap_updateflag = false;
92 iap_changedctr = 0;
93 iap_setupflag = true;
94 iap_remotebtn = BUTTON_NONE;
95 tick_add_task(iap_task);
96 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed);
99 void iap_bitrate_set(int ratenum)
101 switch(ratenum)
103 case 0:
104 serial_bitrate(0);
105 break;
106 case 1:
107 serial_bitrate(9600);
108 break;
109 case 2:
110 serial_bitrate(19200);
111 break;
112 case 3:
113 serial_bitrate(38400);
114 break;
115 case 4:
116 serial_bitrate(57600);
117 break;
121 /* Message format:
122 0xff
123 0x55
124 length
125 mode
126 command (2 bytes)
127 parameters (0-n bytes)
128 checksum (length+mode+parameters+checksum == 0)
131 void iap_send_pkt(const unsigned char * data, int len)
133 int i, chksum;
135 if(len > TX_BUFLEN-4) len = TX_BUFLEN-4;
136 responselen = len + 4;
138 response[0] = 0xFF;
139 response[1] = 0x55;
141 chksum = response[2] = len;
142 for(i = 0; i < len; i ++)
144 chksum += data[i];
145 response[i+3] = data[i];
148 response[i+3] = 0x100 - (chksum & 0xFF);
150 for(i = 0; i < responselen; i ++)
152 while (!tx_rdy()) ;
153 tx_writec(response[i]);
157 bool iap_getc(unsigned char x)
159 static unsigned char last_x = 0;
160 static bool newpkt = true;
161 static unsigned char chksum = 0;
163 /* Restart if the sync word is seen */
164 if(x == 0x55 && last_x == 0xff/* && newpkt*/)
166 serbuf[0] = 0;
167 serbuf_i = 0;
168 chksum = 0;
169 newpkt = false;
171 else
173 if(serbuf_i >= RX_BUFLEN)
174 serbuf_i = 0;
176 serbuf[serbuf_i++] = x;
177 chksum += x;
179 last_x = x;
181 /* Broadcast to queue if we have a complete message */
182 if(serbuf_i && (serbuf_i == serbuf[0]+2))
184 serbuf_i = 0;
185 newpkt = true;
186 if(chksum == 0)
187 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
189 return newpkt;
192 void iap_periodic(void)
194 if(!iap_setupflag) return;
195 if(!iap_pollspeed) return;
197 /* PlayStatusChangeNotification */
198 unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
199 unsigned long time_elapsed = audio_current_track()->elapsed;
201 time_elapsed += wps_get_ff_rewind_count();
203 data[3] = 0x04; /* playing */
205 /* If info has changed, don't flag it right away */
206 if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
208 /* track info has changed */
209 iap_changedctr = 0;
210 data[3] = 0x01; /* 0x02 has same effect? */
211 iap_updateflag = true;
214 data[4] = time_elapsed >> 24;
215 data[5] = time_elapsed >> 16;
216 data[6] = time_elapsed >> 8;
217 data[7] = time_elapsed;
218 iap_send_pkt(data, sizeof(data));
221 static void iap_set_remote_volume(void)
223 unsigned char data[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
224 data[4] = (char)((global_settings.volume+58) * 4);
225 iap_send_pkt(data, sizeof(data));
228 static void cmd_ok_mode0(unsigned char cmd)
230 unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
231 data[3] = cmd; /* respond with cmd */
232 iap_send_pkt(data, sizeof(data));
235 static void iap_handlepkt_mode0(void)
237 unsigned int cmd = serbuf[2];
238 switch (cmd) {
239 /* Identify */
240 case 0x01:
242 /* FM transmitter sends this: */
243 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
244 if(serbuf[3] == 0x05)
246 sleep(HZ/3);
247 /* RF Transmitter: Begin transmission */
248 unsigned char data[] = {0x05, 0x02};
249 iap_send_pkt(data, sizeof(data));
251 /* FM remote sends this: */
252 /* FF 55 03 00 01 02 FA (1st thing sent) */
253 else if(serbuf[3] == 0x02)
255 /* useful only for apple firmware */
257 break;
260 /* EnterRemoteUIMode, FM transmitter sends FF 55 02 00 05 F9 */
261 case 0x05:
263 /* ACK Pending (3000 ms) */
264 unsigned char data[] = {0x00, 0x02, 0x06,
265 0x05, 0x00, 0x00, 0x0B, 0xB8};
266 iap_send_pkt(data, sizeof(data));
267 cmd_ok_mode0(cmd);
268 break;
271 /* ExitRemoteUIMode */
272 case 0x06:
274 audio_stop();
275 cmd_ok_mode0(cmd);
276 break;
279 /* RequestiPodSoftwareVersion, Ipod FM remote sends FF 55 02 00 09 F5 */
280 case 0x09:
282 /* ReturniPodSoftwareVersion, ipod5G firmware version */
283 unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01};
284 iap_send_pkt(data, sizeof(data));
285 break;
288 /* RequestiPodModelNum */
289 case 0x0D:
291 /* ipod is supposed to work only with 5G and nano 2G */
292 /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34,
293 0x37, 0x4C, 0x4C, 0x00}; PA147LL (IPOD 5G 60 GO) */
294 /* ReturniPodModelNum */
295 unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
296 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
297 iap_send_pkt(data, sizeof(data));
298 break;
301 /* RequestLingoProtocolVersion */
302 case 0x0F:
304 /* ReturnLingoProtocolVersion */
305 unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
306 data[2] = serbuf[3];
307 iap_send_pkt(data, sizeof(data));
308 break;
311 /* IdentifyDeviceLingoes */
312 case 0x13:
314 cmd_ok_mode0(cmd);
316 if (serbuf[6] == 0x35)
317 /* FM transmitter sends this: */
318 /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
320 /* GetAccessoryInfo */
321 unsigned char data2[] = {0x00, 0x27, 0x00};
322 iap_send_pkt(data2, sizeof(data2));
323 /* RF Transmitter: Begin transmission */
324 unsigned char data3[] = {0x05, 0x02};
325 iap_send_pkt(data3, sizeof(data3));
328 else
330 /* ipod fm remote sends this: */
331 /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
332 if (serbuf[6] |= 0x80)
333 radio_present = 1;
334 /* GetDevAuthenticationInfo */
335 unsigned char data4[] = {0x00, 0x14};
336 iap_send_pkt(data4, sizeof(data4));
338 break;
341 /* RetDevAuthenticationInfo */
342 case 0x15:
344 /* AckDevAuthenticationInfo */
345 unsigned char data0[] = {0x00, 0x16, 0x00};
346 iap_send_pkt(data0, sizeof(data0));
347 /* GetAccessoryInfo */
348 unsigned char data1[] = {0x00, 0x27, 0x00};
349 iap_send_pkt(data1, sizeof(data1));
350 /* AckDevAuthenticationStatus, mandatory to enable some hardware */
351 unsigned char data2[] = {0x00, 0x19, 0x00};
352 iap_send_pkt(data2, sizeof(data2));
353 if (radio_present == 1)
355 /* GetTunerCaps */
356 unsigned char data3[] = {0x07, 0x01};
357 iap_send_pkt(data3, sizeof(data3));
359 iap_set_remote_volume();
360 break;
363 /* RetDevAuthenticationSignature */
364 case 0x18:
366 /* Isn't used since we don't send the 0x00 0x17 command */
367 break;
370 /* GetIpodOptions */
371 case 0x24:
373 /* RetIpodOptions (ipod video send this) */
374 unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x01};
376 iap_send_pkt(data, sizeof(data));
377 break;
380 /* default response is with cmd ok packet */
381 default:
383 cmd_ok_mode0(cmd);
384 break;
389 static void iap_handlepkt_mode2(void)
391 if(serbuf[2] != 0) return;
392 iap_remotebtn = BUTTON_NONE;
393 iap_remotetick = false;
395 if(serbuf[0] >= 3 && serbuf[3] != 0)
397 if(serbuf[3] & 1)
398 iap_remotebtn |= BUTTON_RC_PLAY;
399 if(serbuf[3] & 2)
400 iap_remotebtn |= BUTTON_RC_VOL_UP;
401 if(serbuf[3] & 4)
402 iap_remotebtn |= BUTTON_RC_VOL_DOWN;
403 if(serbuf[3] & 8)
404 iap_remotebtn |= BUTTON_RC_RIGHT;
405 if(serbuf[3] & 16)
406 iap_remotebtn |= BUTTON_RC_LEFT;
408 else if(serbuf[0] >= 4 && serbuf[4] != 0)
410 if(serbuf[4] & 1) /* play */
412 if (audio_status() != AUDIO_STATUS_PLAY)
414 iap_remotebtn |= BUTTON_RC_PLAY;
415 iap_repeatbtn = 2;
416 iap_remotetick = false;
417 iap_changedctr = 1;
420 if(serbuf[4] & 2) /* pause */
422 if (audio_status() == AUDIO_STATUS_PLAY)
424 iap_remotebtn |= BUTTON_RC_PLAY;
425 iap_repeatbtn = 2;
426 iap_remotetick = false;
427 iap_changedctr = 1;
430 if((serbuf[4] & 128) && !iap_btnshuffle) /* shuffle */
432 iap_btnshuffle = true;
433 if(!global_settings.playlist_shuffle)
435 global_settings.playlist_shuffle = 1;
436 settings_save();
437 if (audio_status() & AUDIO_STATUS_PLAY)
438 playlist_randomise(NULL, current_tick, true);
440 else if(global_settings.playlist_shuffle)
442 global_settings.playlist_shuffle = 0;
443 settings_save();
444 if (audio_status() & AUDIO_STATUS_PLAY)
445 playlist_sort(NULL, true);
448 else
449 iap_btnshuffle = false;
451 else if(serbuf[0] >= 5 && serbuf[5] != 0)
453 if((serbuf[5] & 1) && !iap_btnrepeat) /* repeat */
455 int oldmode = global_settings.repeat_mode;
456 iap_btnrepeat = true;
458 if (oldmode == REPEAT_ONE)
459 global_settings.repeat_mode = REPEAT_OFF;
460 else if (oldmode == REPEAT_ALL)
461 global_settings.repeat_mode = REPEAT_ONE;
462 else if (oldmode == REPEAT_OFF)
463 global_settings.repeat_mode = REPEAT_ALL;
465 settings_save();
466 if (audio_status() & AUDIO_STATUS_PLAY)
467 audio_flush_and_reload_tracks();
469 else
470 iap_btnrepeat = false;
472 if(serbuf[5] & 16) /* ffwd */
474 iap_remotebtn |= BUTTON_RC_RIGHT;
476 if(serbuf[5] & 32) /* frwd */
478 iap_remotebtn |= BUTTON_RC_LEFT;
483 static void iap_handlepkt_mode3(void)
485 unsigned int cmd = serbuf[2];
486 switch (cmd)
488 /* GetCurrentEQProfileIndex */
489 case 0x01:
491 /* RetCurrentEQProfileIndex */
492 unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
493 iap_send_pkt(data, sizeof(data));
494 break;
497 /* SetRemoteEventNotification */
498 case 0x08:
500 /* ACK */
501 unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
502 iap_send_pkt(data, sizeof(data));
503 break;
506 /* GetiPodStateInfo */
507 case 0x0C:
509 /* request ipod volume */
510 if (serbuf[3] == 0x04)
512 iap_set_remote_volume();
514 break;
517 /* SetiPodStateInfo */
518 case 0x0E:
520 if (serbuf[3] == 0x04)
521 global_settings.volume = (-58)+((int)serbuf[5]+1)/4;
522 sound_set_volume(global_settings.volume); /* indent BUG? */
523 break;
528 static void cmd_ok_mode4(unsigned int cmd)
530 unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
531 data[4] = (cmd >> 8) & 0xFF;
532 data[5] = (cmd >> 0) & 0xFF;
533 iap_send_pkt(data, sizeof(data));
536 static void get_playlist_name(unsigned char *dest,
537 unsigned long item_offset,
538 size_t max_length)
540 if (item_offset == 0) return;
541 DIR* dp;
542 struct dirent* playlist_file = NULL;
544 dp = opendir(global_settings.playlist_catalog_dir);
546 char *extension;
547 unsigned long nbr = 0;
548 while ((nbr < item_offset) && ((playlist_file = readdir(dp)) != NULL))
550 /*Increment only if there is a playlist extension*/
551 if ((extension=strrchr(playlist_file->d_name, '.')) != NULL){
552 if ((strcmp(extension, ".m3u") == 0 ||
553 strcmp(extension, ".m3u8") == 0))
554 nbr++;
557 if (playlist_file != NULL) {
558 strlcpy(dest, playlist_file->d_name, max_length);
560 closedir(dp);
563 static void iap_handlepkt_mode4(void)
565 unsigned int cmd = (serbuf[2] << 8) | serbuf[3];
566 switch (cmd)
568 /* GetAudioBookSpeed */
569 case 0x0009:
571 /* ReturnAudioBookSpeed */
572 unsigned char data[] = {0x04, 0x00, 0x0A, 0x00};
573 data[3] = iap_updateflag ? 0 : 1;
574 iap_send_pkt(data, sizeof(data));
575 break;
578 /* SetAudioBookSpeed */
579 case 0x000B:
581 iap_updateflag = serbuf[4] ? 0 : 1;
582 /* respond with cmd ok packet */
583 cmd_ok_mode4(cmd);
584 break;
587 /* RequestProtocolVersion */
588 case 0x0012:
590 /* ReturnProtocolVersion */
591 unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
592 iap_send_pkt(data, sizeof(data));
593 break;
596 /* SelectDBRecord */
597 case 0x0017:
599 memcpy(cur_dbrecord, serbuf + 4, 5);
600 cmd_ok_mode4(cmd);
601 break;
604 /* GetNumberCategorizedDBRecords */
605 case 0x0018:
607 /* ReturnNumberCategorizedDBRecords */
608 unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
609 unsigned long num = 0;
611 DIR* dp;
612 unsigned long nbr_total_playlists = 0;
613 struct dirent* playlist_file = NULL;
614 char *extension;
616 switch(serbuf[4]) /* type number */
618 case 0x01: /* total number of playlists */
619 dp = opendir(global_settings.playlist_catalog_dir);
620 while ((playlist_file = readdir(dp)) != NULL)
622 /*Increment only if there is a playlist extension*/
623 if ((extension=strrchr(playlist_file->d_name, '.'))
624 != NULL) {
625 if ((strcmp(extension, ".m3u") == 0 ||
626 strcmp(extension, ".m3u8") == 0))
627 nbr_total_playlists++;
630 closedir(dp);
631 /*Add 1 for the main playlist*/
632 num = nbr_total_playlists + 1;
633 break;
634 case 0x05: /* total number of songs */
635 num = 1;
636 break;
638 data[3] = num >> 24;
639 data[4] = num >> 16;
640 data[5] = num >> 8;
641 data[6] = num;
642 iap_send_pkt(data, sizeof(data));
643 break;
646 /* RetrieveCategorizedDatabaseRecords */
647 case 0x001A:
649 /* ReturnCategorizedDatabaseRecord */
650 unsigned char data[7 + MAX_NAME_LENGTH] =
651 {0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00,
652 'R', 'O', 'C', 'K', 'B', 'O', 'X', '\0'};
654 unsigned long item_offset = (serbuf[5] << 24) | (serbuf[6] << 16) |
655 (serbuf[7] << 8) | serbuf[8];
657 get_playlist_name(data + 7, item_offset, MAX_NAME_LENGTH);
658 /*Remove file extension*/
659 char *dot=NULL;
660 dot = (strrchr(data+7, '.'));
661 if (dot != NULL)
662 *dot = '\0';
663 iap_send_pkt(data, sizeof(data));
664 break;
667 /* GetPlayStatus */
668 case 0x001C:
670 /* ReturnPlayStatus */
671 unsigned char data[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
672 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
673 struct mp3entry *id3 = audio_current_track();
674 unsigned long time_total = id3->length;
675 unsigned long time_elapsed = id3->elapsed;
676 int status = audio_status();
677 data[3] = time_total >> 24;
678 data[4] = time_total >> 16;
679 data[5] = time_total >> 8;
680 data[6] = time_total;
681 data[7] = time_elapsed >> 24;
682 data[8] = time_elapsed >> 16;
683 data[9] = time_elapsed >> 8;
684 data[10] = time_elapsed;
685 if (status == AUDIO_STATUS_PLAY)
686 data[11] = 0x01; /* play */
687 else if (status & AUDIO_STATUS_PAUSE)
688 data[11] = 0x02; /* pause */
689 iap_send_pkt(data, sizeof(data));
690 break;
693 /* GetCurrentPlayingTrackIndex */
694 case 0x001E:
696 /* ReturnCurrentPlayingTrackIndex */
697 unsigned char data[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00};
698 long playlist_pos = playlist_next(0);
699 playlist_pos -= playlist_get_first_index(NULL);
700 if(playlist_pos < 0)
701 playlist_pos += playlist_amount();
702 data[3] = playlist_pos >> 24;
703 data[4] = playlist_pos >> 16;
704 data[5] = playlist_pos >> 8;
705 data[6] = playlist_pos;
706 iap_send_pkt(data, sizeof(data));
707 break;
710 /* GetIndexedPlayingTrackTitle */
711 case 0x0020:
712 /* GetIndexedPlayingTrackArtistName */
713 case 0x0022:
714 /* GetIndexedPlayingTrackAlbumName */
715 case 0x0024:
717 unsigned char data[70] = {0x04, 0x00, 0xFF};
718 struct mp3entry id3;
719 int fd;
720 size_t len;
721 long tracknum = (signed long)serbuf[4] << 24 |
722 (signed long)serbuf[5] << 16 |
723 (signed long)serbuf[6] << 8 | serbuf[7];
724 data[2] = cmd + 1;
725 memcpy(&id3, audio_current_track(), sizeof(id3));
726 tracknum += playlist_get_first_index(NULL);
727 if(tracknum >= playlist_amount())
728 tracknum -= playlist_amount();
730 /* If the tracknumber is not the current one,
731 read id3 from disk */
732 if(playlist_next(0) != tracknum)
734 struct playlist_track_info info;
735 playlist_get_track_info(NULL, tracknum, &info);
736 fd = open(info.filename, O_RDONLY);
737 memset(&id3, 0, sizeof(struct mp3entry));
738 get_metadata(&id3, fd, info.filename);
739 close(fd);
742 /* Return the requested track data */
743 switch(cmd)
745 case 0x20:
746 len = strlcpy((char *)&data[3], id3.title, 64);
747 iap_send_pkt(data, 4+len);
748 break;
749 case 0x22:
750 len = strlcpy((char *)&data[3], id3.artist, 64);
751 iap_send_pkt(data, 4+len);
752 break;
753 case 0x24:
754 len = strlcpy((char *)&data[3], id3.album, 64);
755 iap_send_pkt(data, 4+len);
756 break;
758 break;
761 /* SetPlayStatusChangeNotification */
762 case 0x0026:
764 iap_pollspeed = serbuf[4] ? 1 : 0;
765 /* respond with cmd ok packet */
766 cmd_ok_mode4(cmd);
767 break;
770 /* PlayCurrentSelection */
771 case 0x0028:
773 switch (cur_dbrecord[0])
775 case 0x01:
776 {/*Playlist*/
777 unsigned long item_offset = (cur_dbrecord[1] << 24)|
778 (cur_dbrecord[2] << 16)|
779 (cur_dbrecord[3] << 8) |
780 cur_dbrecord[4];
781 unsigned char selected_playlist
782 [sizeof(global_settings.playlist_catalog_dir) + 1 + MAX_NAME_LENGTH] = {0};
784 strcpy(selected_playlist,
785 global_settings.playlist_catalog_dir);
786 int len = strlen(selected_playlist);
787 selected_playlist[len] = '/';
788 get_playlist_name (selected_playlist + len + 1,
789 item_offset,
790 MAX_NAME_LENGTH);
791 ft_play_playlist(selected_playlist,
792 global_settings.playlist_catalog_dir,
793 strrchr(selected_playlist, '/') + 1);
794 break;
797 cmd_ok_mode4(cmd);
798 break;
801 /* PlayControl */
802 case 0x0029:
804 switch(serbuf[4])
806 case 0x01: /* play/pause */
807 iap_remotebtn = BUTTON_RC_PLAY;
808 iap_repeatbtn = 2;
809 iap_remotetick = false;
810 iap_changedctr = 1;
811 break;
812 case 0x02: /* stop */
813 iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT;
814 iap_repeatbtn = 2;
815 iap_remotetick = false;
816 iap_changedctr = 1;
817 break;
818 case 0x03: /* skip++ */
819 iap_remotebtn = BUTTON_RC_RIGHT;
820 iap_repeatbtn = 2;
821 iap_remotetick = false;
822 break;
823 case 0x04: /* skip-- */
824 iap_remotebtn = BUTTON_RC_LEFT;
825 iap_repeatbtn = 2;
826 iap_remotetick = false;
827 break;
828 case 0x05: /* ffwd */
829 iap_remotebtn = BUTTON_RC_RIGHT;
830 iap_remotetick = false;
831 if(iap_pollspeed) iap_pollspeed = 5;
832 break;
833 case 0x06: /* frwd */
834 iap_remotebtn = BUTTON_RC_LEFT;
835 iap_remotetick = false;
836 if(iap_pollspeed) iap_pollspeed = 5;
837 break;
838 case 0x07: /* end ffwd/frwd */
839 iap_remotebtn = BUTTON_NONE;
840 iap_remotetick = false;
841 if(iap_pollspeed) iap_pollspeed = 1;
842 break;
844 /* respond with cmd ok packet */
845 cmd_ok_mode4(cmd);
846 break;
849 /* GetShuffle */
850 case 0x002C:
852 /* ReturnShuffle */
853 unsigned char data[] = {0x04, 0x00, 0x2D, 0x00};
854 data[3] = global_settings.playlist_shuffle ? 1 : 0;
855 iap_send_pkt(data, sizeof(data));
856 break;
859 /* SetShuffle */
860 case 0x002E:
862 if(serbuf[4] && !global_settings.playlist_shuffle)
864 global_settings.playlist_shuffle = 1;
865 settings_save();
866 if (audio_status() & AUDIO_STATUS_PLAY)
867 playlist_randomise(NULL, current_tick, true);
869 else if(!serbuf[4] && global_settings.playlist_shuffle)
871 global_settings.playlist_shuffle = 0;
872 settings_save();
873 if (audio_status() & AUDIO_STATUS_PLAY)
874 playlist_sort(NULL, true);
877 /* respond with cmd ok packet */
878 cmd_ok_mode4(cmd);
879 break;
882 /* GetRepeat */
883 case 0x002F:
885 /* ReturnRepeat */
886 unsigned char data[] = {0x04, 0x00, 0x30, 0x00};
887 if(global_settings.repeat_mode == REPEAT_OFF)
888 data[3] = 0;
889 else if(global_settings.repeat_mode == REPEAT_ONE)
890 data[3] = 1;
891 else
892 data[3] = 2;
893 iap_send_pkt(data, sizeof(data));
894 break;
897 /* SetRepeat */
898 case 0x0031:
900 int oldmode = global_settings.repeat_mode;
901 if (serbuf[4] == 0)
902 global_settings.repeat_mode = REPEAT_OFF;
903 else if (serbuf[4] == 1)
904 global_settings.repeat_mode = REPEAT_ONE;
905 else if (serbuf[4] == 2)
906 global_settings.repeat_mode = REPEAT_ALL;
908 if (oldmode != global_settings.repeat_mode)
910 settings_save();
911 if (audio_status() & AUDIO_STATUS_PLAY)
912 audio_flush_and_reload_tracks();
915 /* respond with cmd ok packet */
916 cmd_ok_mode4(cmd);
917 break;
920 /* GetMonoDisplayImageLimits */
921 case 0x0033:
923 /* ReturnMonoDisplayImageLimits */
924 unsigned char data[] = {0x04, 0x00, 0x34,
925 LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
926 LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
927 0x01};
928 iap_send_pkt(data, sizeof(data));
929 break;
932 /* GetNumPlayingTracks */
933 case 0x0035:
935 /* ReturnNumPlayingTracks */
936 unsigned char data[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00};
937 unsigned long playlist_amt = playlist_amount();
938 data[3] = playlist_amt >> 24;
939 data[4] = playlist_amt >> 16;
940 data[5] = playlist_amt >> 8;
941 data[6] = playlist_amt;
942 iap_send_pkt(data, sizeof(data));
943 break;
946 /* SetCurrentPlayingTrack */
947 case 0x0037:
949 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
950 long tracknum = (signed long)serbuf[4] << 24 |
951 (signed long)serbuf[5] << 16 |
952 (signed long)serbuf[6] << 8 | serbuf[7];
953 audio_pause();
954 audio_skip(tracknum - playlist_next(0));
955 if (!paused)
956 audio_resume();
958 /* respond with cmd ok packet */
959 cmd_ok_mode4(cmd);
960 break;
963 default:
965 /* default response is with cmd ok packet */
966 cmd_ok_mode4(cmd);
967 break;
972 static void iap_handlepkt_mode7(void)
974 unsigned int cmd = serbuf[2];
975 switch (cmd)
977 /* RetTunerCaps */
978 case 0x02:
980 /* do nothing */
982 /* GetAccessoryInfo */
983 unsigned char data[] = {0x00, 0x27, 0x00};
984 iap_send_pkt(data, sizeof(data));
985 break;
988 /* RetTunerFreq */
989 case 0x0A:
990 /* fall through */
991 /* TunerSeekDone */
992 case 0x13:
994 rmt_tuner_freq(serbuf);
995 break;
998 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
999 case 0x21:
1001 rmt_tuner_rds_data(serbuf);
1002 break;
1007 void iap_handlepkt(void)
1010 if(!iap_setupflag) return;
1011 if(serbuf[0] == 0) return;
1013 /* if we are waiting for a remote button to go out,
1014 delay the handling of the new packet */
1015 if(!iap_remotetick)
1017 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
1018 return;
1021 unsigned char mode = serbuf[1];
1022 switch (mode) {
1023 case 0: iap_handlepkt_mode0(); break;
1024 case 2: iap_handlepkt_mode2(); break;
1025 case 3: iap_handlepkt_mode3(); break;
1026 case 4: iap_handlepkt_mode4(); break;
1027 case 7: iap_handlepkt_mode7(); break;
1030 serbuf[0] = 0;
1033 int remote_control_rx(void)
1035 int btn = iap_remotebtn;
1036 if(iap_repeatbtn)
1038 iap_repeatbtn--;
1039 if(!iap_repeatbtn)
1041 iap_remotebtn = BUTTON_NONE;
1042 iap_remotetick = true;
1045 else
1046 iap_remotetick = true;
1048 return btn;
1051 const unsigned char *iap_get_serbuf(void)
1053 return serbuf;