Merge tag 'v3.13-final' into maemo-port
[maemo-rb.git] / apps / iap.c
blob6fe0a032814b930755aa202f8245691c5b37e623
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 "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"
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"
44 #include "tuner.h"
45 #include "ipod_remote_tuner.h"
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];
61 static unsigned char response[TX_BUFLEN];
63 static char cur_dbrecord[5] = {0};
65 /* states of the iap de-framing state machine */
66 enum 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 */
76 static 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
86 static void put_u32(unsigned char *buf, uint32_t data)
88 buf[0] = (data >> 24) & 0xFF;
89 buf[1] = (data >> 16) & 0xFF;
90 buf[2] = (data >> 8) & 0xFF;
91 buf[3] = (data >> 0) & 0xFF;
94 static uint32_t get_u32(const unsigned char *buf)
96 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
99 static void iap_task(void)
101 static int count = 0;
103 count += iap_pollspeed;
104 if (count < (500/10)) return;
106 /* exec every 500ms if pollspeed == 1 */
107 count = 0;
108 queue_post(&button_queue, SYS_IAP_PERIODIC, 0);
111 /* called by playback when the next track starts */
112 static void iap_track_changed(void *ignored)
114 (void)ignored;
115 iap_changedctr = 1;
118 void iap_setup(int ratenum)
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);
131 void iap_bitrate_set(int ratenum)
133 switch(ratenum)
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;
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)
163 void iap_send_pkt(const unsigned char * data, int len)
165 int i, chksum, responselen;
167 if(len > TX_BUFLEN-4) len = TX_BUFLEN-4;
168 responselen = len + 4;
170 response[0] = 0xFF;
171 response[1] = 0x55;
173 chksum = response[2] = len;
174 for(i = 0; i < len; i ++)
176 chksum += data[i];
177 response[i+3] = data[i];
180 response[i+3] = 0x100 - (chksum & 0xFF);
182 for(i = 0; i < responselen; i ++)
184 while (!tx_rdy()) ;
185 tx_writec(response[i]);
189 bool iap_getc(unsigned char x)
191 struct state_t *s = &frame_state;
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;
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);
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;
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;
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;
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);
251 s->state = ST_SYNC;
252 break;
253 default:
254 panicf("Unhandled iap state %d", (int) s->state);
255 break;
258 /* return true while still hunting for the sync and start-of-frame byte */
259 return (s->state == ST_SYNC) || (s->state == ST_SOF);
262 void iap_periodic(void)
264 if(!iap_setupflag) return;
265 if(!iap_pollspeed) return;
267 /* PlayStatusChangeNotification */
268 unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
269 unsigned long time_elapsed = audio_current_track()->elapsed;
271 time_elapsed += wps_get_ff_rewind_count();
273 data[3] = 0x04; /* playing */
275 /* If info has changed, don't flag it right away */
276 if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
278 /* track info has changed */
279 iap_changedctr = 0;
280 data[3] = 0x01; /* 0x02 has same effect? */
281 iap_updateflag = true;
284 put_u32(&data[4], time_elapsed);
285 iap_send_pkt(data, sizeof(data));
288 static void iap_set_remote_volume(void)
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));
295 static void cmd_ok_mode0(unsigned char cmd)
297 unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
298 data[3] = cmd; /* respond with cmd */
299 iap_send_pkt(data, sizeof(data));
302 static void iap_handlepkt_mode0(unsigned int len, const unsigned char *buf)
304 (void)len; /* len currently unused */
306 unsigned int cmd = buf[1];
307 switch (cmd) {
308 /* Identify */
309 case 0x01:
311 /* FM transmitter sends this: */
312 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
313 if(buf[2] == 0x05)
315 sleep(HZ/3);
316 /* RF Transmitter: Begin transmission */
317 unsigned char data[] = {0x05, 0x02};
318 iap_send_pkt(data, sizeof(data));
320 /* FM remote sends this: */
321 /* FF 55 03 00 01 02 FA (1st thing sent) */
322 else if (buf[2] == 0x02)
324 /* useful only for apple firmware */
326 break;
329 /* EnterRemoteUIMode, FM transmitter sends FF 55 02 00 05 F9 */
330 case 0x05:
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;
340 /* ExitRemoteUIMode */
341 case 0x06:
343 audio_stop();
344 cmd_ok_mode0(cmd);
345 break;
348 /* RequestiPodSoftwareVersion, Ipod FM remote sends FF 55 02 00 09 F5 */
349 case 0x09:
351 /* ReturniPodSoftwareVersion, ipod5G firmware version */
352 unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01};
353 iap_send_pkt(data, sizeof(data));
354 break;
357 /* RequestiPodModelNum */
358 case 0x0D:
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;
370 /* RequestLingoProtocolVersion */
371 case 0x0F:
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;
380 /* IdentifyDeviceLingoes */
381 case 0x13:
383 cmd_ok_mode0(cmd);
385 uint32_t lingoes = get_u32(&buf[2]);
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 (??)*/
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));
398 else
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));
408 break;
411 /* RetDevAuthenticationInfo */
412 case 0x15:
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)
425 /* GetTunerCaps */
426 unsigned char data3[] = {0x07, 0x01};
427 iap_send_pkt(data3, sizeof(data3));
429 iap_set_remote_volume();
430 break;
433 /* RetDevAuthenticationSignature */
434 case 0x18:
436 /* Isn't used since we don't send the 0x00 0x17 command */
437 break;
440 /* GetIpodOptions */
441 case 0x24:
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;
450 /* default response is with cmd ok packet */
451 default:
453 cmd_ok_mode0(cmd);
454 break;
459 static void iap_handlepkt_mode2(unsigned int len, const unsigned char *buf)
461 if(buf[1] != 0) return;
462 iap_remotebtn = BUTTON_NONE;
463 iap_remotetick = false;
465 if(len >= 3 && buf[2] != 0)
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;
478 else if(len >= 4 && buf[3] != 0)
480 if(buf[3] & 1) /* play */
482 if (audio_status() != AUDIO_STATUS_PLAY)
484 iap_remotebtn |= BUTTON_RC_PLAY;
485 iap_repeatbtn = 2;
486 iap_remotetick = false;
487 iap_changedctr = 1;
490 if(buf[3] & 2) /* pause */
492 if (audio_status() == AUDIO_STATUS_PLAY)
494 iap_remotebtn |= BUTTON_RC_PLAY;
495 iap_repeatbtn = 2;
496 iap_remotetick = false;
497 iap_changedctr = 1;
500 if((buf[3] & 128) && !iap_btnshuffle) /* shuffle */
502 iap_btnshuffle = true;
503 if(!global_settings.playlist_shuffle)
505 global_settings.playlist_shuffle = 1;
506 settings_save();
507 if (audio_status() & AUDIO_STATUS_PLAY)
508 playlist_randomise(NULL, current_tick, true);
510 else if(global_settings.playlist_shuffle)
512 global_settings.playlist_shuffle = 0;
513 settings_save();
514 if (audio_status() & AUDIO_STATUS_PLAY)
515 playlist_sort(NULL, true);
518 else
519 iap_btnshuffle = false;
521 else if(len >= 5 && buf[4] != 0)
523 if((buf[4] & 1) && !iap_btnrepeat) /* repeat */
525 int oldmode = global_settings.repeat_mode;
526 iap_btnrepeat = true;
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;
535 settings_save();
536 if (audio_status() & AUDIO_STATUS_PLAY)
537 audio_flush_and_reload_tracks();
539 else
540 iap_btnrepeat = false;
542 if(buf[4] & 16) /* ffwd */
544 iap_remotebtn |= BUTTON_RC_RIGHT;
546 if(buf[4] & 32) /* frwd */
548 iap_remotebtn |= BUTTON_RC_LEFT;
553 static void iap_handlepkt_mode3(unsigned int len, const unsigned char *buf)
555 (void)len; /* len currently unused */
557 unsigned int cmd = buf[1];
558 switch (cmd)
560 /* GetCurrentEQProfileIndex */
561 case 0x01:
563 /* RetCurrentEQProfileIndex */
564 unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
565 iap_send_pkt(data, sizeof(data));
566 break;
569 /* SetRemoteEventNotification */
570 case 0x08:
572 /* ACK */
573 unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
574 iap_send_pkt(data, sizeof(data));
575 break;
578 /* GetiPodStateInfo */
579 case 0x0C:
581 /* request ipod volume */
582 if (buf[2] == 0x04)
584 iap_set_remote_volume();
586 break;
589 /* SetiPodStateInfo */
590 case 0x0E:
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;
600 static void cmd_ok_mode4(unsigned int cmd)
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));
608 static void get_playlist_name(unsigned char *dest,
609 unsigned long item_offset,
610 size_t max_length)
612 if (item_offset == 0) return;
613 DIR* dp;
614 struct dirent* playlist_file = NULL;
616 dp = opendir(global_settings.playlist_catalog_dir);
618 char *extension;
619 unsigned long nbr = 0;
620 while ((nbr < item_offset) && ((playlist_file = readdir(dp)) != NULL))
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++;
629 if (playlist_file != NULL) {
630 strlcpy(dest, playlist_file->d_name, max_length);
632 closedir(dp);
635 static void iap_handlepkt_mode4(unsigned int len, const unsigned char *buf)
637 (void)len; /* len currently unused */
639 unsigned int cmd = (buf[1] << 8) | buf[2];
640 switch (cmd)
642 /* GetAudioBookSpeed */
643 case 0x0009:
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;
652 /* SetAudioBookSpeed */
653 case 0x000B:
655 iap_updateflag = buf[3] ? 0 : 1;
656 /* respond with cmd ok packet */
657 cmd_ok_mode4(cmd);
658 break;
661 /* RequestProtocolVersion */
662 case 0x0012:
664 /* ReturnProtocolVersion */
665 unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
666 iap_send_pkt(data, sizeof(data));
667 break;
670 /* SelectDBRecord */
671 case 0x0017:
673 memcpy(cur_dbrecord, buf + 3, 5);
674 cmd_ok_mode4(cmd);
675 break;
678 /* GetNumberCategorizedDBRecords */
679 case 0x0018:
681 /* ReturnNumberCategorizedDBRecords */
682 unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
683 unsigned long num = 0;
685 DIR* dp;
686 unsigned long nbr_total_playlists = 0;
687 struct dirent* playlist_file = NULL;
688 char *extension;
690 switch(buf[3]) /* type number */
692 case 0x01: /* total number of playlists */
693 dp = opendir(global_settings.playlist_catalog_dir);
694 while ((playlist_file = readdir(dp)) != NULL)
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++;
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;
712 put_u32(&data[3], num);
713 iap_send_pkt(data, sizeof(data));
714 break;
717 /* RetrieveCategorizedDatabaseRecords */
718 case 0x001A:
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'};
725 unsigned long item_offset = get_u32(&buf[4]);
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;
737 /* GetPlayStatus */
738 case 0x001C:
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;
757 /* GetCurrentPlayingTrackIndex */
758 case 0x001E:
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;
771 /* GetIndexedPlayingTrackTitle */
772 case 0x0020:
773 /* GetIndexedPlayingTrackArtistName */
774 case 0x0022:
775 /* GetIndexedPlayingTrackAlbumName */
776 case 0x0024:
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]);
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();
790 /* If the tracknumber is not the current one,
791 read id3 from disk */
792 if(playlist_next(0) != tracknum)
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);
802 /* Return the requested track data */
803 switch(cmd)
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;
818 break;
821 /* SetPlayStatusChangeNotification */
822 case 0x0026:
824 iap_pollspeed = buf[3] ? 1 : 0;
825 /* respond with cmd ok packet */
826 cmd_ok_mode4(cmd);
827 break;
830 /* PlayCurrentSelection */
831 case 0x0028:
833 switch (cur_dbrecord[0])
835 case 0x01:
836 {/*Playlist*/
837 unsigned long item_offset = get_u32(&cur_dbrecord[1]);
839 unsigned char selected_playlist
840 [sizeof(global_settings.playlist_catalog_dir)
842 + MAX_PATH] = {0};
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;
857 cmd_ok_mode4(cmd);
858 break;
861 /* PlayControl */
862 case 0x0029:
864 switch(buf[3])
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;
904 /* respond with cmd ok packet */
905 cmd_ok_mode4(cmd);
906 break;
909 /* GetShuffle */
910 case 0x002C:
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;
919 /* SetShuffle */
920 case 0x002E:
922 if(buf[3] && !global_settings.playlist_shuffle)
924 global_settings.playlist_shuffle = 1;
925 settings_save();
926 if (audio_status() & AUDIO_STATUS_PLAY)
927 playlist_randomise(NULL, current_tick, true);
929 else if(!buf[3] && global_settings.playlist_shuffle)
931 global_settings.playlist_shuffle = 0;
932 settings_save();
933 if (audio_status() & AUDIO_STATUS_PLAY)
934 playlist_sort(NULL, true);
937 /* respond with cmd ok packet */
938 cmd_ok_mode4(cmd);
939 break;
942 /* GetRepeat */
943 case 0x002F:
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;
957 /* SetRepeat */
958 case 0x0031:
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;
968 if (oldmode != global_settings.repeat_mode)
970 settings_save();
971 if (audio_status() & AUDIO_STATUS_PLAY)
972 audio_flush_and_reload_tracks();
975 /* respond with cmd ok packet */
976 cmd_ok_mode4(cmd);
977 break;
980 /* GetMonoDisplayImageLimits */
981 case 0x0033:
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;
992 /* GetNumPlayingTracks */
993 case 0x0035:
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;
1003 /* SetCurrentPlayingTrack */
1004 case 0x0037:
1006 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
1007 long tracknum = get_u32(&buf[3]);
1009 audio_pause();
1010 audio_skip(tracknum - playlist_next(0));
1011 if (!paused)
1012 audio_resume();
1014 /* respond with cmd ok packet */
1015 cmd_ok_mode4(cmd);
1016 break;
1019 default:
1021 /* default response is with cmd ok packet */
1022 cmd_ok_mode4(cmd);
1023 break;
1028 static void iap_handlepkt_mode7(unsigned int len, const unsigned char *buf)
1030 unsigned int cmd = buf[1];
1031 switch (cmd)
1033 /* RetTunerCaps */
1034 case 0x02:
1036 /* do nothing */
1038 /* GetAccessoryInfo */
1039 unsigned char data[] = {0x00, 0x27, 0x00};
1040 iap_send_pkt(data, sizeof(data));
1041 break;
1044 /* RetTunerFreq */
1045 case 0x0A:
1046 /* fall through */
1047 /* TunerSeekDone */
1048 case 0x13:
1050 rmt_tuner_freq(len, buf);
1051 break;
1054 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
1055 case 0x21:
1057 rmt_tuner_rds_data(len, buf);
1058 break;
1063 void iap_handlepkt(void)
1065 struct state_t *s = &frame_state;
1067 if(!iap_setupflag) return;
1069 /* if we are waiting for a remote button to go out,
1070 delay the handling of the new packet */
1071 if(!iap_remotetick)
1073 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
1074 return;
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;
1088 int remote_control_rx(void)
1090 int btn = iap_remotebtn;
1091 if(iap_repeatbtn)
1093 iap_repeatbtn--;
1094 if(!iap_repeatbtn)
1096 iap_remotebtn = BUTTON_NONE;
1097 iap_remotetick = true;
1100 else
1101 iap_remotetick = true;
1103 return btn;
1106 const unsigned char *iap_get_serbuf(void)
1108 return serbuf;