1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
31 #include "appevents.h"
41 #include "powermgmt.h"
44 #include "ipod_remote_tuner.h"
46 static volatile int iap_pollspeed
= 0;
47 static volatile bool iap_remotetick
= true;
48 static bool iap_setupflag
= false, iap_updateflag
= false;
49 static int iap_changedctr
= 0;
51 static unsigned long iap_remotebtn
= 0;
52 static int iap_repeatbtn
= 0;
53 static bool iap_btnrepeat
= false, iap_btnshuffle
= false;
55 static unsigned char serbuf
[RX_BUFLEN
];
56 static int serbuf_i
= 0;
58 static unsigned char response
[TX_BUFLEN
];
59 static int responselen
;
61 static void iap_task(void)
65 count
+= iap_pollspeed
;
66 if (count
< (500/10)) return;
68 /* exec every 500ms if pollspeed == 1 */
70 queue_post(&button_queue
, SYS_IAP_PERIODIC
, 0);
73 /* called by playback when the next track starts */
74 static void iap_track_changed(void *ignored
)
80 void iap_setup(int ratenum
)
82 iap_bitrate_set(ratenum
);
84 iap_remotetick
= true;
85 iap_updateflag
= false;
88 iap_remotebtn
= BUTTON_NONE
;
89 tick_add_task(iap_task
);
90 add_event(PLAYBACK_EVENT_TRACK_CHANGE
, false, iap_track_changed
);
93 void iap_bitrate_set(int ratenum
)
101 serial_bitrate(9600);
104 serial_bitrate(19200);
107 serial_bitrate(38400);
110 serial_bitrate(57600);
121 parameters (0-n bytes)
122 checksum (length+mode+parameters+checksum == 0)
125 void iap_send_pkt(const unsigned char * data
, int len
)
129 if(len
> TX_BUFLEN
-4) len
= TX_BUFLEN
-4;
130 responselen
= len
+ 4;
135 chksum
= response
[2] = len
;
136 for(i
= 0; i
< len
; i
++)
139 response
[i
+3] = data
[i
];
142 response
[i
+3] = 0x100 - (chksum
& 0xFF);
144 for(i
= 0; i
< responselen
; i
++)
147 tx_writec(response
[i
]);
151 bool iap_getc(unsigned char x
)
153 static unsigned char last_x
= 0;
154 static bool newpkt
= true;
155 static unsigned char chksum
= 0;
157 /* Restart if the sync word is seen */
158 if(x
== 0x55 && last_x
== 0xff/* && newpkt*/)
167 if(serbuf_i
>= RX_BUFLEN
)
170 serbuf
[serbuf_i
++] = x
;
175 /* Broadcast to queue if we have a complete message */
176 if(serbuf_i
&& (serbuf_i
== serbuf
[0]+2))
181 queue_post(&button_queue
, SYS_IAP_HANDLEPKT
, 0);
186 void iap_periodic(void)
188 if(!iap_setupflag
) return;
189 if(!iap_pollspeed
) return;
191 /* PlayStatusChangeNotification */
192 unsigned char data
[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
193 unsigned long time_elapsed
= audio_current_track()->elapsed
;
195 time_elapsed
+= wps_get_ff_rewind_count();
197 data
[3] = 0x04; /* playing */
199 /* If info has changed, don't flag it right away */
200 if(iap_changedctr
&& iap_changedctr
++ >= iap_pollspeed
* 2)
202 /* track info has changed */
204 data
[3] = 0x01; /* 0x02 has same effect? */
205 iap_updateflag
= true;
208 data
[4] = time_elapsed
>> 24;
209 data
[5] = time_elapsed
>> 16;
210 data
[6] = time_elapsed
>> 8;
211 data
[7] = time_elapsed
;
212 iap_send_pkt(data
, sizeof(data
));
215 static void iap_set_remote_volume(void)
217 unsigned char data
[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
218 data
[4] = (char)((global_settings
.volume
+58) * 4);
219 iap_send_pkt(data
, sizeof(data
));
222 static void cmd_ok_mode0(unsigned char cmd
)
224 unsigned char data
[] = {0x00, 0x02, 0x00, 0x00};
225 data
[3] = cmd
; /* respond with cmd */
226 iap_send_pkt(data
, sizeof(data
));
229 static void iap_handlepkt_mode0(void)
231 unsigned int cmd
= serbuf
[2];
236 /* FM transmitter sends this: */
237 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
238 if(serbuf
[3] == 0x05)
241 /* RF Transmitter: Begin transmission */
242 unsigned char data
[] = {0x05, 0x02};
243 iap_send_pkt(data
, sizeof(data
));
245 /* FM remote sends this: */
246 /* FF 55 03 00 01 02 FA (1st thing sent) */
247 else if(serbuf
[3] == 0x02)
249 /* useful only for apple firmware */
254 /* EnterRemoteUIMode, FM transmitter sends FF 55 02 00 05 F9 */
257 /* ACK Pending (3000 ms) */
258 unsigned char data
[] = {0x00, 0x02, 0x06,
259 0x05, 0x00, 0x00, 0x0B, 0xB8};
260 iap_send_pkt(data
, sizeof(data
));
265 /* ExitRemoteUIMode */
273 /* RequestiPodSoftwareVersion, Ipod FM remote sends FF 55 02 00 09 F5 */
276 /* ReturniPodSoftwareVersion, ipod5G firmware version */
277 unsigned char data
[] = {0x00, 0x0A, 0x01, 0x02, 0x01};
278 iap_send_pkt(data
, sizeof(data
));
282 /* RequestiPodModelNum */
285 /* ipod is supposed to work only with 5G and nano 2G */
286 /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34,
287 0x37, 0x4C, 0x4C, 0x00}; PA147LL (IPOD 5G 60 GO) */
288 /* ReturniPodModelNum */
289 unsigned char data
[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
290 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
291 iap_send_pkt(data
, sizeof(data
));
295 /* RequestLingoProtocolVersion */
298 /* ReturnLingoProtocolVersion */
299 unsigned char data
[] = {0x00, 0x10, 0x00, 0x01, 0x05};
301 iap_send_pkt(data
, sizeof(data
));
305 /* IdentifyDeviceLingoes */
310 if (serbuf
[6] == 0x35)
311 /* FM transmitter sends this: */
312 /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
314 /* GetAccessoryInfo */
315 unsigned char data2
[] = {0x00, 0x27, 0x00};
316 iap_send_pkt(data2
, sizeof(data2
));
317 /* RF Transmitter: Begin transmission */
318 unsigned char data3
[] = {0x05, 0x02};
319 iap_send_pkt(data3
, sizeof(data3
));
324 /* ipod fm remote sends this: */
325 /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
326 if (serbuf
[6] |= 0x80)
328 /* GetDevAuthenticationInfo */
329 unsigned char data4
[] = {0x00, 0x14};
330 iap_send_pkt(data4
, sizeof(data4
));
335 /* RetDevAuthenticationInfo */
338 /* AckDevAuthenticationInfo */
339 unsigned char data0
[] = {0x00, 0x16, 0x00};
340 iap_send_pkt(data0
, sizeof(data0
));
341 /* GetAccessoryInfo */
342 unsigned char data1
[] = {0x00, 0x27, 0x00};
343 iap_send_pkt(data1
, sizeof(data1
));
344 /* AckDevAuthenticationStatus, mandatory to enable some hardware */
345 unsigned char data2
[] = {0x00, 0x19, 0x00};
346 iap_send_pkt(data2
, sizeof(data2
));
347 if (radio_present
== 1)
350 unsigned char data3
[] = {0x07, 0x01};
351 iap_send_pkt(data3
, sizeof(data3
));
353 iap_set_remote_volume();
357 /* RetDevAuthenticationSignature */
360 /* Isn't used since we don't send the 0x00 0x17 command */
367 /* RetIpodOptions (ipod video send this) */
368 unsigned char data
[] = {0x00, 0x25, 0x00, 0x00, 0x00,
369 0x00, 0x00, 0x00, 0x00, 0x01};
370 iap_send_pkt(data
, sizeof(data
));
374 /* default response is with cmd ok packet */
383 static void iap_handlepkt_mode2(void)
385 if(serbuf
[2] != 0) return;
386 iap_remotebtn
= BUTTON_NONE
;
387 iap_remotetick
= false;
389 if(serbuf
[0] >= 3 && serbuf
[3] != 0)
392 iap_remotebtn
|= BUTTON_RC_PLAY
;
394 iap_remotebtn
|= BUTTON_RC_VOL_UP
;
396 iap_remotebtn
|= BUTTON_RC_VOL_DOWN
;
398 iap_remotebtn
|= BUTTON_RC_RIGHT
;
400 iap_remotebtn
|= BUTTON_RC_LEFT
;
402 else if(serbuf
[0] >= 4 && serbuf
[4] != 0)
404 if(serbuf
[4] & 1) /* play */
406 if (audio_status() != AUDIO_STATUS_PLAY
)
408 iap_remotebtn
|= BUTTON_RC_PLAY
;
410 iap_remotetick
= false;
414 if(serbuf
[4] & 2) /* pause */
416 if (audio_status() == AUDIO_STATUS_PLAY
)
418 iap_remotebtn
|= BUTTON_RC_PLAY
;
420 iap_remotetick
= false;
424 if((serbuf
[4] & 128) && !iap_btnshuffle
) /* shuffle */
426 iap_btnshuffle
= true;
427 if(!global_settings
.playlist_shuffle
)
429 global_settings
.playlist_shuffle
= 1;
431 if (audio_status() & AUDIO_STATUS_PLAY
)
432 playlist_randomise(NULL
, current_tick
, true);
434 else if(global_settings
.playlist_shuffle
)
436 global_settings
.playlist_shuffle
= 0;
438 if (audio_status() & AUDIO_STATUS_PLAY
)
439 playlist_sort(NULL
, true);
443 iap_btnshuffle
= false;
445 else if(serbuf
[0] >= 5 && serbuf
[5] != 0)
447 if((serbuf
[5] & 1) && !iap_btnrepeat
) /* repeat */
449 int oldmode
= global_settings
.repeat_mode
;
450 iap_btnrepeat
= true;
452 if (oldmode
== REPEAT_ONE
)
453 global_settings
.repeat_mode
= REPEAT_OFF
;
454 else if (oldmode
== REPEAT_ALL
)
455 global_settings
.repeat_mode
= REPEAT_ONE
;
456 else if (oldmode
== REPEAT_OFF
)
457 global_settings
.repeat_mode
= REPEAT_ALL
;
460 if (audio_status() & AUDIO_STATUS_PLAY
)
461 audio_flush_and_reload_tracks();
464 iap_btnrepeat
= false;
466 if(serbuf
[5] & 16) /* ffwd */
468 iap_remotebtn
|= BUTTON_RC_RIGHT
;
470 if(serbuf
[5] & 32) /* frwd */
472 iap_remotebtn
|= BUTTON_RC_LEFT
;
477 static void iap_handlepkt_mode3(void)
479 unsigned int cmd
= serbuf
[2];
482 /* GetCurrentEQProfileIndex */
485 /* RetCurrentEQProfileIndex */
486 unsigned char data
[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
487 iap_send_pkt(data
, sizeof(data
));
491 /* SetRemoteEventNotification */
495 unsigned char data
[] = {0x03, 0x00, 0x00, 0x08};
496 iap_send_pkt(data
, sizeof(data
));
500 /* GetiPodStateInfo */
503 /* request ipod volume */
504 if (serbuf
[3] == 0x04)
506 iap_set_remote_volume();
511 /* SetiPodStateInfo */
514 if (serbuf
[3] == 0x04)
515 global_settings
.volume
= (-58)+((int)serbuf
[5]+1)/4;
516 sound_set_volume(global_settings
.volume
); /* indent BUG? */
522 static void cmd_ok_mode4(unsigned int cmd
)
524 unsigned char data
[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
525 data
[4] = (cmd
>> 8) & 0xFF;
526 data
[5] = (cmd
>> 0) & 0xFF;
527 iap_send_pkt(data
, sizeof(data
));
530 static void iap_handlepkt_mode4(void)
532 unsigned int cmd
= (serbuf
[2] << 8) | serbuf
[3];
535 /* GetAudioBookSpeed */
538 /* ReturnAudioBookSpeed */
539 unsigned char data
[] = {0x04, 0x00, 0x0A, 0x00};
540 data
[3] = iap_updateflag
? 0 : 1;
541 iap_send_pkt(data
, sizeof(data
));
545 /* SetAudioBookSpeed */
548 iap_updateflag
= serbuf
[4] ? 0 : 1;
549 /* respond with cmd ok packet */
554 /* RequestProtocolVersion */
557 /* ReturnProtocolVersion */
558 unsigned char data
[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
559 iap_send_pkt(data
, sizeof(data
));
563 /* GetNumberCategorizedDBRecords */
566 /* ReturnNumberCategorizedDBRecords */
567 unsigned char data
[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
568 unsigned long num
= 0;
569 switch(serbuf
[4]) /* type number */
571 case 0x01: /* total number of playlists */
574 case 0x05: /* total number of songs */
581 iap_send_pkt(data
, sizeof(data
));
588 /* ReturnPlayStatus */
589 unsigned char data
[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
591 struct mp3entry
*id3
= audio_current_track();
592 unsigned long time_total
= id3
->length
;
593 unsigned long time_elapsed
= id3
->elapsed
;
594 int status
= audio_status();
595 data
[3] = time_total
>> 24;
596 data
[4] = time_total
>> 16;
597 data
[5] = time_total
>> 8;
598 data
[6] = time_total
;
599 data
[7] = time_elapsed
>> 24;
600 data
[8] = time_elapsed
>> 16;
601 data
[9] = time_elapsed
>> 8;
602 data
[10] = time_elapsed
;
603 if (status
== AUDIO_STATUS_PLAY
)
604 data
[11] = 0x01; /* play */
605 else if (status
& AUDIO_STATUS_PAUSE
)
606 data
[11] = 0x02; /* pause */
607 iap_send_pkt(data
, sizeof(data
));
611 /* GetCurrentPlayingTrackIndex */
614 /* ReturnCurrentPlayingTrackIndex */
615 unsigned char data
[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00};
616 long playlist_pos
= playlist_next(0);
617 playlist_pos
-= playlist_get_first_index(NULL
);
619 playlist_pos
+= playlist_amount();
620 data
[3] = playlist_pos
>> 24;
621 data
[4] = playlist_pos
>> 16;
622 data
[5] = playlist_pos
>> 8;
623 data
[6] = playlist_pos
;
624 iap_send_pkt(data
, sizeof(data
));
628 /* GetIndexedPlayingTrackTitle */
630 /* GetIndexedPlayingTrackArtistName */
632 /* GetIndexedPlayingTrackAlbumName */
635 unsigned char data
[70] = {0x04, 0x00, 0xFF};
639 long tracknum
= (signed long)serbuf
[4] << 24 |
640 (signed long)serbuf
[5] << 16 |
641 (signed long)serbuf
[6] << 8 | serbuf
[7];
643 memcpy(&id3
, audio_current_track(), sizeof(id3
));
644 tracknum
+= playlist_get_first_index(NULL
);
645 if(tracknum
>= playlist_amount())
646 tracknum
-= playlist_amount();
648 /* If the tracknumber is not the current one,
649 read id3 from disk */
650 if(playlist_next(0) != tracknum
)
652 struct playlist_track_info info
;
653 playlist_get_track_info(NULL
, tracknum
, &info
);
654 fd
= open(info
.filename
, O_RDONLY
);
655 memset(&id3
, 0, sizeof(struct mp3entry
));
656 get_metadata(&id3
, fd
, info
.filename
);
660 /* Return the requested track data */
664 len
= strlcpy((char *)&data
[3], id3
.title
, 64);
665 iap_send_pkt(data
, 4+len
);
668 len
= strlcpy((char *)&data
[3], id3
.artist
, 64);
669 iap_send_pkt(data
, 4+len
);
672 len
= strlcpy((char *)&data
[3], id3
.album
, 64);
673 iap_send_pkt(data
, 4+len
);
679 /* SetPlayStatusChangeNotification */
682 iap_pollspeed
= serbuf
[4] ? 1 : 0;
683 /* respond with cmd ok packet */
693 case 0x01: /* play/pause */
694 iap_remotebtn
= BUTTON_RC_PLAY
;
696 iap_remotetick
= false;
699 case 0x02: /* stop */
700 iap_remotebtn
= BUTTON_RC_PLAY
|BUTTON_REPEAT
;
702 iap_remotetick
= false;
705 case 0x03: /* skip++ */
706 iap_remotebtn
= BUTTON_RC_RIGHT
;
708 iap_remotetick
= false;
710 case 0x04: /* skip-- */
711 iap_remotebtn
= BUTTON_RC_LEFT
;
713 iap_remotetick
= false;
715 case 0x05: /* ffwd */
716 iap_remotebtn
= BUTTON_RC_RIGHT
;
717 iap_remotetick
= false;
718 if(iap_pollspeed
) iap_pollspeed
= 5;
720 case 0x06: /* frwd */
721 iap_remotebtn
= BUTTON_RC_LEFT
;
722 iap_remotetick
= false;
723 if(iap_pollspeed
) iap_pollspeed
= 5;
725 case 0x07: /* end ffwd/frwd */
726 iap_remotebtn
= BUTTON_NONE
;
727 iap_remotetick
= false;
728 if(iap_pollspeed
) iap_pollspeed
= 1;
731 /* respond with cmd ok packet */
740 unsigned char data
[] = {0x04, 0x00, 0x2D, 0x00};
741 data
[3] = global_settings
.playlist_shuffle
? 1 : 0;
742 iap_send_pkt(data
, sizeof(data
));
749 if(serbuf
[4] && !global_settings
.playlist_shuffle
)
751 global_settings
.playlist_shuffle
= 1;
753 if (audio_status() & AUDIO_STATUS_PLAY
)
754 playlist_randomise(NULL
, current_tick
, true);
756 else if(!serbuf
[4] && global_settings
.playlist_shuffle
)
758 global_settings
.playlist_shuffle
= 0;
760 if (audio_status() & AUDIO_STATUS_PLAY
)
761 playlist_sort(NULL
, true);
764 /* respond with cmd ok packet */
773 unsigned char data
[] = {0x04, 0x00, 0x30, 0x00};
774 if(global_settings
.repeat_mode
== REPEAT_OFF
)
776 else if(global_settings
.repeat_mode
== REPEAT_ONE
)
780 iap_send_pkt(data
, sizeof(data
));
787 int oldmode
= global_settings
.repeat_mode
;
789 global_settings
.repeat_mode
= REPEAT_OFF
;
790 else if (serbuf
[4] == 1)
791 global_settings
.repeat_mode
= REPEAT_ONE
;
792 else if (serbuf
[4] == 2)
793 global_settings
.repeat_mode
= REPEAT_ALL
;
795 if (oldmode
!= global_settings
.repeat_mode
)
798 if (audio_status() & AUDIO_STATUS_PLAY
)
799 audio_flush_and_reload_tracks();
802 /* respond with cmd ok packet */
807 /* GetMonoDisplayImageLimits */
810 /* ReturnMonoDisplayImageLimits */
811 unsigned char data
[] = {0x04, 0x00, 0x34,
812 LCD_WIDTH
>> 8, LCD_WIDTH
& 0xff,
813 LCD_HEIGHT
>> 8, LCD_HEIGHT
& 0xff,
815 iap_send_pkt(data
, sizeof(data
));
819 /* GetNumPlayingTracks */
822 /* ReturnNumPlayingTracks */
823 unsigned char data
[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00};
824 unsigned long playlist_amt
= playlist_amount();
825 data
[3] = playlist_amt
>> 24;
826 data
[4] = playlist_amt
>> 16;
827 data
[5] = playlist_amt
>> 8;
828 data
[6] = playlist_amt
;
829 iap_send_pkt(data
, sizeof(data
));
833 /* SetCurrentPlayingTrack */
836 int paused
= (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE
));
837 long tracknum
= (signed long)serbuf
[4] << 24 |
838 (signed long)serbuf
[5] << 16 |
839 (signed long)serbuf
[6] << 8 | serbuf
[7];
841 audio_skip(tracknum
- playlist_next(0));
845 /* respond with cmd ok packet */
852 /* default response is with cmd ok packet */
859 static void iap_handlepkt_mode7(void)
861 unsigned int cmd
= serbuf
[2];
869 /* GetAccessoryInfo */
870 unsigned char data
[] = {0x00, 0x27, 0x00};
871 iap_send_pkt(data
, sizeof(data
));
881 rmt_tuner_freq(serbuf
);
885 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
888 rmt_tuner_rds_data(serbuf
);
894 void iap_handlepkt(void)
897 if(!iap_setupflag
) return;
898 if(serbuf
[0] == 0) return;
900 /* if we are waiting for a remote button to go out,
901 delay the handling of the new packet */
904 queue_post(&button_queue
, SYS_IAP_HANDLEPKT
, 0);
908 unsigned char mode
= serbuf
[1];
910 case 0: iap_handlepkt_mode0(); break;
911 case 2: iap_handlepkt_mode2(); break;
912 case 3: iap_handlepkt_mode3(); break;
913 case 4: iap_handlepkt_mode4(); break;
914 case 7: iap_handlepkt_mode7(); break;
920 int remote_control_rx(void)
922 int btn
= iap_remotebtn
;
928 iap_remotebtn
= BUTTON_NONE
;
929 iap_remotetick
= true;
933 iap_remotetick
= true;
938 const unsigned char *iap_get_serbuf(void)