summaryrefslogtreecommitdiff
path: root/apps/iap/iap-lingo3.c
diff options
context:
space:
mode:
authorRalf Ertzinger <rockbox@camperquake.de>2013-06-22 10:08:23 +0100
committerFrank Gevaerts <frank@gevaerts.be>2013-11-10 18:41:24 +0100
commitb170c73f922e3457b923b4e7fcbec794a8885c77 (patch)
tree89fbdbd8c25af5101a29a1ede3b896332a4e205c /apps/iap/iap-lingo3.c
parent500b137308a6ee5c2aba873734a8956d70472f56 (diff)
downloadrockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.tar.gz
rockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.zip
Updated IAP commands.
Originally written and uploaded by Lalufu (Ralf Ertzinger) in Feb 2012. They have been condensed into a single patch and some further additions by Andy Potter. Currently includes Authentication V2 support from iPod to Accessory, RF/BlueTooth transmitter support, selecting a playlist and selecting a track from the current playlist. Does not support uploading Album Art or podcasts. Has been tested on the following iPods, 4th Gen Grayscale, 4th Gen Color/Photo, Mini 2nd Gen, Nano 1st Gen and Video 5.5Gen. Change-Id: Ie8fc098361844132f0228ecbe3c48da948726f5e Co-Authored by: Andy Potter <liveboxandy@gmail.com> Reviewed-on: http://gerrit.rockbox.org/533 Reviewed-by: Frank Gevaerts <frank@gevaerts.be>
Diffstat (limited to 'apps/iap/iap-lingo3.c')
-rw-r--r--apps/iap/iap-lingo3.c1508
1 files changed, 1508 insertions, 0 deletions
diff --git a/apps/iap/iap-lingo3.c b/apps/iap/iap-lingo3.c
new file mode 100644
index 0000000000..0ed3df118e
--- /dev/null
+++ b/apps/iap/iap-lingo3.c
@@ -0,0 +1,1508 @@
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
20/* Lingo 0x03: Display Remote Lingo
21 *
22 * A bit of a hodgepogde of odds and ends.
23 *
24 * Used to control the equalizer in version 1.00 of the Lingo, but later
25 * grew functions to control album art transfer and check the player
26 * status.
27 *
28 * TODO:
29 * - Actually support multiple equalizer profiles, currently only the
30 * profile 0 (equalizer disabled) is supported
31 */
32
33#include "iap-core.h"
34#include "iap-lingo.h"
35#include "system.h"
36#include "audio.h"
37#include "powermgmt.h"
38#include "settings.h"
39#include "metadata.h"
40#include "playback.h"
41
42/*
43 * This macro is meant to be used inside an IAP mode message handler.
44 * It is passed the expected minimum length of the message buffer.
45 * If the buffer does not have the required lenght an ACK
46 * packet with a Bad Parameter error is generated.
47 */
48#define CHECKLEN(x) do { \
49 if (len < (x)) { \
50 cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
51 return; \
52 }} while(0)
53
54/* Check for authenticated state, and return an ACK Not
55 * Authenticated on failure.
56 */
57#define CHECKAUTH do { \
58 if (!DEVICE_AUTHENTICATED) { \
59 cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \
60 return; \
61 }} while(0)
62
63static void cmd_ack(const unsigned char cmd, const unsigned char status)
64{
65 IAP_TX_INIT(0x03, 0x00);
66 IAP_TX_PUT(status);
67 IAP_TX_PUT(cmd);
68
69 iap_send_tx();
70}
71
72#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
73
74void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
75{
76 unsigned int cmd = buf[1];
77
78 /* We expect at least two bytes in the buffer, one for the
79 * state bits.
80 */
81 CHECKLEN(2);
82
83 /* Lingo 0x03 must have been negotiated */
84 if (!DEVICE_LINGO_SUPPORTED(0x03)) {
85 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
86 return;
87 }
88
89 switch (cmd)
90 {
91 /* ACK (0x00)
92 *
93 * Sent from the iPod to the device
94 */
95
96 /* GetCurrentEQProfileIndex (0x01)
97 *
98 * Return the index of the current equalizer profile.
99 *
100 * Packet format (offset in buf[]: Description)
101 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
102 * 0x01: Command, always 0x01
103 *
104 * Returns:
105 * RetCurrentEQProfileIndex
106 *
107 * Packet format (offset in data[]: Description)
108 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
109 * 0x01: Command, always 0x02
110 * 0x02-0x05: Index as an unsigned 32bit integer
111 */
112 case 0x01:
113 {
114 IAP_TX_INIT(0x03, 0x02);
115 IAP_TX_PUT_U32(0x00);
116
117 iap_send_tx();
118 break;
119 }
120
121 /* RetCurrentEQProfileIndex (0x02)
122 *
123 * Sent from the iPod to the device
124 */
125
126 /* SetCurrentEQProfileIndex (0x03)
127 *
128 * Set the active equalizer profile
129 *
130 * Packet format (offset in buf[]: Description)
131 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
132 * 0x01: Command, always 0x03
133 * 0x02-0x05: Profile index to activate
134 * 0x06: Whether to restore the previous profile on detach
135 *
136 * Returns on success:
137 * IAP_ACK_OK
138 *
139 * Returns on failure:
140 * IAP_ACK_CMD_FAILED
141 *
142 * TODO: Figure out return code for invalid index
143 */
144 case 0x03:
145 {
146 uint32_t index;
147
148 CHECKLEN(7);
149
150 index = get_u32(&buf[2]);
151
152 if (index > 0) {
153 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
154 break;
155 }
156
157 /* Currently, we just ignore the command and acknowledge it */
158 cmd_ok(cmd);
159 break;
160 }
161
162 /* GetNumEQProfiles (0x04)
163 *
164 * Get the number of available equalizer profiles
165 *
166 * Packet format (offset in buf[]: Description)
167 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
168 * 0x01: Command, always 0x04
169 *
170 * Returns:
171 * RetNumEQProfiles
172 *
173 * Packet format (offset in data[]: Description)
174 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
175 * 0x01: Command, always 0x05
176 * 0x02-0x05: Number as an unsigned 32bit integer
177 */
178 case 0x04:
179 {
180 IAP_TX_INIT(0x03, 0x05);
181 /* Return one profile (0, the disabled profile) */
182 IAP_TX_PUT_U32(0x01);
183
184 iap_send_tx();
185 break;
186 }
187
188 /* RetNumEQProfiles (0x05)
189 *
190 * Sent from the iPod to the device
191 */
192
193 /* GetIndexedEQProfileName (0x06)
194 *
195 * Return the name of the indexed equalizer profile
196 *
197 * Packet format (offset in buf[]: Description)
198 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
199 * 0x01: Command, always 0x06
200 * 0x02-0x05: Profile index to get the name of
201 *
202 * Returns on success:
203 * RetIndexedEQProfileName
204 *
205 * Packet format (offset in data[]: Description)
206 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
207 * 0x01: Command, always 0x06
208 * 0x02-0xNN: Name as an UTF-8 null terminated string
209 *
210 * Returns on failure:
211 * IAP_ACK_BAD_PARAM
212 *
213 * TODO: Figure out return code for out of range index
214 */
215 case 0x06:
216 {
217 uint32_t index;
218
219 CHECKLEN(6);
220
221 index = get_u32(&buf[2]);
222
223 if (index > 0) {
224 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
225 break;
226 }
227 IAP_TX_INIT(0x03, 0x07);
228 IAP_TX_PUT_STRING("Default");
229
230 iap_send_tx();
231 break;
232 }
233
234 /* RetIndexedQUProfileName (0x07)
235 *
236 * Sent from the iPod to the device
237 */
238
239 /* SetRemoteEventNotification (0x08)
240 *
241 * Set events the device would like to be notified about
242 *
243 * Packet format (offset in buf[]: Description)
244 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
245 * 0x01: Command, always 0x08
246 * 0x02-0x05: Event bitmask
247 *
248 * Returns:
249 * IAP_ACK_OK
250 */
251 case 0x08:
252 {
253 struct tm* tm;
254
255 CHECKLEN(6);
256 CHECKAUTH;
257
258 /* Save the current state of the various attributes we track */
259 device.trackpos_ms = iap_get_trackpos();
260 device.track_index = iap_get_trackindex();
261 device.chapter_index = 0;
262 device.play_status = audio_status();
263 /* TODO: Fix this */
264 device.mute = false;
265 device.volume = 0x80;
266 device.power_state = charger_input_state;
267 device.battery_level = battery_level();
268 /* TODO: Fix this */
269 device.equalizer_index = 0;
270 device.shuffle = global_settings.playlist_shuffle;
271 device.repeat = global_settings.repeat_mode;
272 tm = get_time();
273 memcpy(&(device.datetime), tm, sizeof(struct tm));
274 device.alarm_state = 0;
275 device.alarm_hour = 0;
276 device.alarm_minute = 0;
277 /* TODO: Fix this */
278 device.backlight = 0;
279 device.hold = button_hold();
280 device.soundcheck = 0;
281 device.audiobook = 0;
282 device.trackpos_s = (device.trackpos_ms/1000) & 0xFFFF;
283
284 /* Get the notification bits */
285 device.do_notify = false;
286 device.changed_notifications = 0;
287 device.notifications = get_u32(&buf[0x02]);
288 if (device.notifications)
289 device.do_notify = true;
290
291 cmd_ok(cmd);
292 break;
293 }
294
295 /* RemoteEventNotification (0x09)
296 *
297 * Sent from the iPod to the device
298 */
299
300 /* GetRemoteEventStatus (0x0A)
301 *
302 * Request the events changed since the last call to GetREmoteEventStatus
303 * or SetRemoteEventNotification
304 *
305 * Packet format (offset in buf[]: Description)
306 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
307 * 0x01: Command, always 0x0A
308 *
309 * This command requires authentication
310 *
311 * Returns:
312 * RetRemoteEventNotification
313 *
314 * Packet format (offset in data[]: Description)
315 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
316 * 0x01: Command, always 0x0B
317 * 0x02-0x05: Event status bits
318 */
319 case 0x0A:
320 {
321 CHECKAUTH;
322 IAP_TX_INIT(0x03, 0x0B);
323 IAP_TX_PUT_U32(device.changed_notifications);
324
325 iap_send_tx();
326
327 device.changed_notifications = 0;
328 break;
329 }
330
331 /* RetRemoteEventStatus (0x0B)
332 *
333 * Sent from the iPod to the device
334 */
335
336 /* GetiPodStateInfo (0x0C)
337 *
338 * Request state information from the iPod
339 *
340 * Packet format (offset in buf[]: Description)
341 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
342 * 0x01: Command, always 0x0C
343 * 0x02: Type information
344 *
345 * This command requires authentication
346 *
347 * Returns:
348 * RetiPodStateInfo
349 *
350 * Packet format (offset in data[]: Description)
351 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
352 * 0x01: Command, always 0x0D
353 * 0x02: Type information
354 * 0x03-0xNN: State information
355 */
356 case 0x0C:
357 {
358 struct mp3entry* id3;
359 struct playlist_info* playlist;
360 int play_status;
361 struct tm* tm;
362
363 CHECKLEN(3);
364 CHECKAUTH;
365
366 IAP_TX_INIT(0x03, 0x0D);
367 IAP_TX_PUT(buf[0x02]);
368
369 switch (buf[0x02])
370 {
371 /* 0x00: Track position
372 * Data length: 4
373 */
374 case 0x00:
375 {
376 id3 = audio_current_track();
377 IAP_TX_PUT_U32(id3->elapsed);
378
379 iap_send_tx();
380 break;
381 }
382
383 /* 0x01: Track index
384 * Data length: 4
385 */
386 case 0x01:
387 {
388 playlist = playlist_get_current();
389 IAP_TX_PUT_U32(playlist->index - playlist->first_index);
390
391 iap_send_tx();
392 break;
393 }
394
395 /* 0x02: Chapter information
396 * Data length: 8
397 */
398 case 0x02:
399 {
400 playlist = playlist_get_current();
401 IAP_TX_PUT_U32(playlist->index - playlist->first_index);
402 /* Indicate that track does not have chapters */
403 IAP_TX_PUT_U16(0x0000);
404 IAP_TX_PUT_U16(0xFFFF);
405
406 iap_send_tx();
407 break;
408 }
409
410 /* 0x03: Play status
411 * Data length: 1
412 */
413 case 0x03:
414 {
415 /* TODO: Handle FF/REW
416 */
417 play_status = audio_status();
418 if (play_status & AUDIO_STATUS_PLAY) {
419 if (play_status & AUDIO_STATUS_PAUSE) {
420 IAP_TX_PUT(0x02);
421 } else {
422 IAP_TX_PUT(0x01);
423 }
424 } else {
425 IAP_TX_PUT(0x00);
426 }
427
428 iap_send_tx();
429 break;
430 }
431
432 /* 0x04: Mute/UI/Volume
433 * Data length: 2
434 */
435 case 0x04:
436 {
437 /* Figuring out what the current volume is
438 * seems to be tricky.
439 * TODO: Fix.
440 */
441
442 /* Mute status */
443 IAP_TX_PUT(0x00);
444 /* Volume */
445 IAP_TX_PUT(0x80);
446
447 iap_send_tx();
448 break;
449 }
450
451 /* 0x05: Power/Battery
452 * Data length: 2
453 */
454 case 0x05:
455 {
456 iap_fill_power_state();
457
458 iap_send_tx();
459 break;
460 }
461
462 /* 0x06: Equalizer state
463 * Data length: 4
464 */
465 case 0x06:
466 {
467 /* Currently only one equalizer setting supported, 0 */
468 IAP_TX_PUT_U32(0x00);
469
470 iap_send_tx();
471 break;
472 }
473
474 /* 0x07: Shuffle
475 * Data length: 1
476 */
477 case 0x07:
478 {
479 IAP_TX_PUT(global_settings.playlist_shuffle?0x01:0x00);
480
481 iap_send_tx();
482 break;
483 }
484
485 /* 0x08: Repeat
486 * Data length: 1
487 */
488 case 0x08:
489 {
490 switch (global_settings.repeat_mode)
491 {
492 case REPEAT_OFF:
493 {
494 IAP_TX_PUT(0x00);
495 break;
496 }
497
498 case REPEAT_ONE:
499 {
500 IAP_TX_PUT(0x01);
501 break;
502 }
503
504 case REPEAT_ALL:
505 {
506 IAP_TX_PUT(0x02);
507 break;
508 }
509 }
510
511 iap_send_tx();
512 break;
513 }
514
515 /* 0x09: Data/Time
516 * Data length: 6
517 */
518 case 0x09:
519 {
520 tm = get_time();
521
522 /* Year */
523 IAP_TX_PUT_U16(tm->tm_year);
524
525 /* Month */
526 IAP_TX_PUT(tm->tm_mon+1);
527
528 /* Day */
529 IAP_TX_PUT(tm->tm_mday);
530
531 /* Hour */
532 IAP_TX_PUT(tm->tm_hour);
533
534 /* Minute */
535 IAP_TX_PUT(tm->tm_min);
536
537 iap_send_tx();
538 break;
539 }
540
541 /* 0x0A: Alarm
542 * Data length: 3
543 */
544 case 0x0A:
545 {
546 /* Alarm not supported, always off */
547 IAP_TX_PUT(0x00);
548 IAP_TX_PUT(0x00);
549 IAP_TX_PUT(0x00);
550
551 iap_send_tx();
552 break;
553 }
554
555 /* 0x0B: Backlight
556 * Data length: 1
557 */
558 case 0x0B:
559 {
560 /* TOOD: Find out how to do this */
561 IAP_TX_PUT(0x00);
562
563 iap_send_tx();
564 break;
565 }
566
567 /* 0x0C: Hold switch
568 * Data length: 1
569 */
570 case 0x0C:
571 {
572 IAP_TX_PUT(button_hold()?0x01:0x00);
573
574 iap_send_tx();
575 break;
576 }
577
578 /* 0x0D: Sound check
579 * Data length: 1
580 */
581 case 0x0D:
582 {
583 /* TODO: Find out what the hell this is. Default to off */
584 IAP_TX_PUT(0x00);
585
586 iap_send_tx();
587 break;
588 }
589
590 /* 0x0E: Audiobook
591 * Data length: 1
592 */
593 case 0x0E:
594 {
595 /* Default to normal */
596 IAP_TX_PUT(0x00);
597
598 iap_send_tx();
599 break;
600 }
601
602 /* 0x0F: Track position in seconds
603 * Data length: 2
604 */
605 case 0x0F:
606 {
607 unsigned int pos;
608
609 id3 = audio_current_track();
610 pos = id3->elapsed/1000;
611
612 IAP_TX_PUT_U16(pos);
613
614 iap_send_tx();
615 break;
616 }
617
618 /* 0x10: Mute/UI/Absolute volume
619 * Data length: 3
620 */
621 case 0x10:
622 {
623 /* TODO: See volume above */
624 IAP_TX_PUT(0x00);
625 IAP_TX_PUT(0x80);
626 IAP_TX_PUT(0x80);
627
628 iap_send_tx();
629 break;
630 }
631
632 default:
633 {
634 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
635 break;
636 }
637 }
638 break;
639 }
640
641 /* RetiPodStateInfo (0x0D)
642 *
643 * Sent from the iPod to the device
644 */
645
646 /* SetiPodStateInfo (0x0E)
647 *
648 * Set status information to new values
649 *
650 * Packet format (offset in buf[]: Description)
651 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
652 * 0x01: Command, always 0x0E
653 * 0x02: Type of information to change
654 * 0x03-0xNN: New information
655 *
656 * This command requires authentication
657 *
658 * Returns on success:
659 * IAP_ACK_OK
660 *
661 * Returns on failure:
662 * IAP_ACK_CMD_FAILED
663 * IAP_ACK_BAD_PARAM
664 */
665 case 0x0E:
666 {
667 CHECKLEN(3);
668 CHECKAUTH;
669 switch (buf[0x02])
670 {
671 /* Track position (ms)
672 * Data length: 4
673 */
674 case 0x00:
675 {
676 uint32_t pos;
677
678 CHECKLEN(7);
679 pos = get_u32(&buf[0x03]);
680 audio_ff_rewind(pos);
681
682 cmd_ok(cmd);
683 break;
684 }
685
686 /* Track index
687 * Data length: 4
688 */
689 case 0x01:
690 {
691 uint32_t index;
692
693 CHECKLEN(7);
694 index = get_u32(&buf[0x03]);
695 audio_skip(index-iap_get_trackindex());
696
697 cmd_ok(cmd);
698 break;
699 }
700
701 /* Chapter index
702 * Data length: 2
703 */
704 case 0x02:
705 {
706 /* This is not supported */
707 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
708 break;
709 }
710
711 /* Play status
712 * Data length: 1
713 */
714 case 0x03:
715 {
716 CHECKLEN(4);
717 switch(buf[0x03])
718 {
719 case 0x00:
720 {
721 audio_stop();
722 cmd_ok(cmd);
723 break;
724 }
725
726 case 0x01:
727 {
728 audio_resume();
729 cmd_ok(cmd);
730 break;
731 }
732
733 case 0x02:
734 {
735 audio_pause();
736 cmd_ok(cmd);
737 break;
738 }
739
740 default:
741 {
742 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
743 break;
744 }
745 }
746 break;
747 }
748
749 /* Volume/Mute
750 * Data length: 2
751 * TODO: Fix this
752 */
753 case 0x04:
754 {
755 CHECKLEN(5);
756 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
757 break;
758 }
759
760 /* Equalizer
761 * Data length: 5
762 */
763 case 0x06:
764 {
765 uint32_t index;
766
767 CHECKLEN(8);
768 index = get_u32(&buf[0x03]);
769 if (index == 0) {
770 cmd_ok(cmd);
771 } else {
772 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
773 }
774 break;
775 }
776
777 /* Shuffle
778 * Data length: 2
779 */
780 case 0x07:
781 {
782 CHECKLEN(5);
783
784 switch(buf[0x03])
785 {
786 case 0x00:
787 {
788 iap_shuffle_state(false);
789 cmd_ok(cmd);
790 break;
791 }
792 case 0x01:
793 case 0x02:
794 {
795 iap_shuffle_state(true);
796 cmd_ok(cmd);
797 break;
798 }
799
800 default:
801 {
802 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
803 break;
804 }
805 }
806 break;
807 }
808
809 /* Repeat
810 * Data length: 2
811 */
812 case 0x08:
813 {
814 CHECKLEN(5);
815
816 switch(buf[0x03])
817 {
818 case 0x00:
819 {
820 iap_repeat_state(REPEAT_OFF);
821 cmd_ok(cmd);
822 break;
823 }
824 case 0x01:
825 {
826 iap_repeat_state(REPEAT_ONE);
827 cmd_ok(cmd);
828 break;
829 }
830 case 0x02:
831 {
832 iap_repeat_state(REPEAT_ALL);
833 cmd_ok(cmd);
834 break;
835 }
836 default:
837 {
838 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
839 break;
840 }
841 }
842 break;
843 }
844
845 /* Date/Time
846 * Data length: 6
847 */
848 case 0x09:
849 {
850 CHECKLEN(9);
851
852 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
853 break;
854 }
855
856 /* Alarm
857 * Data length: 4
858 */
859 case 0x0A:
860 {
861 CHECKLEN(7);
862
863 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
864 break;
865 }
866
867 /* Backlight
868 * Data length: 2
869 */
870 case 0x0B:
871 {
872 CHECKLEN(5);
873
874 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
875 break;
876 }
877
878 /* Sound check
879 * Data length: 2
880 */
881 case 0x0D:
882 {
883 CHECKLEN(5);
884
885 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
886 break;
887 }
888
889 /* Audio book speed
890 * Data length: 1
891 */
892 case 0x0E:
893 {
894 CHECKLEN(4);
895
896 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
897 break;
898 }
899
900 /* Track position (s)
901 * Data length: 2
902 */
903 case 0x0F:
904 {
905 uint16_t pos;
906
907 CHECKLEN(5);
908 pos = get_u16(&buf[0x03]);
909 audio_ff_rewind(1000L * pos);
910
911 cmd_ok(cmd);
912 break;
913 }
914
915 /* Volume/Mute/Absolute
916 * Data length: 4
917 * TODO: Fix this
918 */
919 case 0x10:
920 {
921 CHECKLEN(7);
922 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
923 break;
924 }
925
926 default:
927 {
928 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
929 break;
930 }
931 }
932
933 break;
934 }
935
936 /* GetPlayStatus (0x0F)
937 *
938 * Request the current play status information
939 *
940 * Packet format (offset in buf[]: Description)
941 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
942 * 0x01: Command, always 0x0F
943 *
944 * This command requires authentication
945 *
946 * Returns:
947 * RetPlayStatus
948 *
949 * Packet format (offset in data[]: Description)
950 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
951 * 0x01: Command, always 0x10
952 * 0x02: Play state
953 * 0x03-0x06: Current track index
954 * 0x07-0x0A: Current track length (ms)
955 * 0x0B-0x0E: Current track position (ms)
956 */
957 case 0x0F:
958 {
959 int play_status;
960 struct mp3entry* id3;
961 struct playlist_info* playlist;
962
963 CHECKAUTH;
964
965 IAP_TX_INIT(0x03, 0x10);
966
967 play_status = audio_status();
968
969 if (play_status & AUDIO_STATUS_PLAY) {
970 /* Playing or paused */
971 if (play_status & AUDIO_STATUS_PAUSE) {
972 /* Paused */
973 IAP_TX_PUT(0x02);
974 } else {
975 /* Playing */
976 IAP_TX_PUT(0x01);
977 }
978 playlist = playlist_get_current();
979 IAP_TX_PUT_U32(playlist->index - playlist->first_index);
980 id3 = audio_current_track();
981 IAP_TX_PUT_U32(id3->length);
982 IAP_TX_PUT_U32(id3->elapsed);
983 } else {
984 /* Stopped, all values are 0x00 */
985 IAP_TX_PUT(0x00);
986 IAP_TX_PUT_U32(0x00);
987 IAP_TX_PUT_U32(0x00);
988 IAP_TX_PUT_U32(0x00);
989 }
990
991 iap_send_tx();
992 break;
993 }
994
995 /* RetPlayStatus (0x10)
996 *
997 * Sent from the iPod to the device
998 */
999
1000 /* SetCurrentPlayingTrack (0x11)
1001 *
1002 * Set the current playing track
1003 *
1004 * Packet format (offset in buf[]: Description)
1005 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1006 * 0x01: Command, always 0x11
1007 * 0x02-0x05: Index of track to play
1008 *
1009 * This command requires authentication
1010 *
1011 * Returns on success:
1012 * IAP_ACK_OK
1013 *
1014 * Returns on failure:
1015 * IAP_ACK_BAD_PARAM
1016 */
1017 case 0x11:
1018 {
1019 uint32_t index;
1020 uint32_t trackcount;
1021
1022 CHECKAUTH;
1023 CHECKLEN(6);
1024
1025 index = get_u32(&buf[0x02]);
1026 trackcount = playlist_amount();
1027
1028 if (index >= trackcount)
1029 {
1030 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1031 break;
1032 }
1033 audio_skip(index-iap_get_trackindex());
1034 cmd_ok(cmd);
1035
1036 break;
1037 }
1038
1039 /* GetIndexedPlayingTrackInfo (0x12)
1040 *
1041 * Request information about a given track
1042 *
1043 * Packet format (offset in buf[]: Description)
1044 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1045 * 0x01: Command, always 0x12
1046 * 0x02: Type of information to retrieve
1047 * 0x03-0x06: Track index
1048 * 0x07-0x08: Chapter index
1049 *
1050 * This command requires authentication.
1051 *
1052 * Returns:
1053 * RetIndexedPlayingTrackInfo
1054 *
1055 * Packet format (offset in data[]: Description)
1056 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1057 * 0x01: Command, always 0x13
1058 * 0x02: Type of information returned
1059 * 0x03-0xNN: Information
1060 */
1061 case 0x12:
1062 {
1063 /* NOTE:
1064 *
1065 * Retrieving the track information from a track which is not
1066 * the currently playing track can take a seriously long time,
1067 * in the order of several seconds.
1068 *
1069 * This most certainly violates the IAP spec, but there's no way
1070 * around this for now.
1071 */
1072 uint32_t track_index;
1073 struct playlist_track_info track;
1074 struct mp3entry id3;
1075
1076 CHECKLEN(0x09);
1077 CHECKAUTH;
1078
1079 track_index = get_u32(&buf[0x03]);
1080 if (-1 == playlist_get_track_info(NULL, track_index, &track)) {
1081 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1082 break;
1083 }
1084
1085 IAP_TX_INIT(0x03, 0x13);
1086 IAP_TX_PUT(buf[2]);
1087 switch (buf[2])
1088 {
1089 /* 0x00: Track caps/info
1090 * Information length: 10 bytes
1091 */
1092 case 0x00:
1093 {
1094 iap_get_trackinfo(track_index, &id3);
1095 /* Track capabilities. None of these are supported, yet */
1096 IAP_TX_PUT_U32(0x00);
1097
1098 /* Track length in ms */
1099 IAP_TX_PUT_U32(id3.length);
1100
1101 /* Chapter count, stays at 0 */
1102 IAP_TX_PUT_U16(0x00);
1103
1104 iap_send_tx();
1105 break;
1106 }
1107
1108 /* 0x01: Chapter time/name
1109 * Information length: 4+variable
1110 */
1111 case 0x01:
1112 {
1113 /* Chapter length, set at 0 (no chapters) */
1114 IAP_TX_PUT_U32(0x00);
1115
1116 /* Chapter name, empty */
1117 IAP_TX_PUT_STRING("");
1118
1119 iap_send_tx();
1120 break;
1121 }
1122
1123 /* 0x02, Artist name
1124 * Information length: variable
1125 */
1126 case 0x02:
1127 {
1128 /* Artist name */
1129 iap_get_trackinfo(track_index, &id3);
1130 IAP_TX_PUT_STRLCPY(id3.artist);
1131
1132 iap_send_tx();
1133 break;
1134 }
1135
1136 /* 0x03, Album name
1137 * Information length: variable
1138 */
1139 case 0x03:
1140 {
1141 /* Album name */
1142 iap_get_trackinfo(track_index, &id3);
1143 IAP_TX_PUT_STRLCPY(id3.album);
1144
1145 iap_send_tx();
1146 break;
1147 }
1148
1149 /* 0x04, Genre name
1150 * Information length: variable
1151 */
1152 case 0x04:
1153 {
1154 /* Genre name */
1155 iap_get_trackinfo(track_index, &id3);
1156 IAP_TX_PUT_STRLCPY(id3.genre_string);
1157
1158 iap_send_tx();
1159 break;
1160 }
1161
1162 /* 0x05, Track title
1163 * Information length: variable
1164 */
1165 case 0x05:
1166 {
1167 /* Track title */
1168 iap_get_trackinfo(track_index, &id3);
1169 IAP_TX_PUT_STRLCPY(id3.title);
1170
1171 iap_send_tx();
1172 break;
1173 }
1174
1175 /* 0x06, Composer name
1176 * Information length: variable
1177 */
1178 case 0x06:
1179 {
1180 /* Track Composer */
1181 iap_get_trackinfo(track_index, &id3);
1182 IAP_TX_PUT_STRLCPY(id3.composer);
1183
1184 iap_send_tx();
1185 break;
1186 }
1187
1188 /* 0x07, Lyrics
1189 * Information length: variable
1190 */
1191 case 0x07:
1192 {
1193 /* Packet information bits. All 0 (single packet) */
1194 IAP_TX_PUT(0x00);
1195
1196 /* Packet index */
1197 IAP_TX_PUT_U16(0x00);
1198
1199 /* Lyrics */
1200 IAP_TX_PUT_STRING("");
1201
1202 iap_send_tx();
1203 break;
1204 }
1205
1206 /* 0x08, Artwork count
1207 * Information length: variable
1208 */
1209 case 0x08:
1210 {
1211 /* No artwork, return packet containing just the type byte */
1212 iap_send_tx();
1213 break;
1214 }
1215
1216 default:
1217 {
1218 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1219 break;
1220 }
1221 }
1222
1223 break;
1224 }
1225
1226 /* RetIndexedPlayingTrackInfo (0x13)
1227 *
1228 * Sent from the iPod to the device
1229 */
1230
1231 /* GetNumPlayingTracks (0x14)
1232 *
1233 * Request the number of tracks in the current playlist
1234 *
1235 * Packet format (offset in buf[]: Description)
1236 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1237 * 0x01: Command, always 0x14
1238 *
1239 * This command requires authentication.
1240 *
1241 * Returns:
1242 * RetNumPlayingTracks
1243 *
1244 * Packet format (offset in data[]: Description)
1245 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1246 * 0x01: Command, always 0x15
1247 * 0x02-0xNN: Number of tracks
1248 */
1249 case 0x14:
1250 {
1251 CHECKAUTH;
1252
1253 IAP_TX_INIT(0x03, 0x15);
1254 IAP_TX_PUT_U32(playlist_amount());
1255
1256 iap_send_tx();
1257 }
1258
1259 /* RetNumPlayingTracks (0x15)
1260 *
1261 * Sent from the iPod to the device
1262 */
1263
1264 /* GetArtworkFormats (0x16)
1265 *
1266 * Request a list of supported artwork formats
1267 *
1268 * Packet format (offset in buf[]: Description)
1269 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1270 * 0x01: Command, always 0x16
1271 *
1272 * This command requires authentication.
1273 *
1274 * Returns:
1275 * RetArtworkFormats
1276 *
1277 * Packet format (offset in data[]: Description)
1278 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1279 * 0x01: Command, always 0x17
1280 * 0x02-0xNN: list of 7 byte format descriptors
1281 */
1282 case 0x16:
1283 {
1284 CHECKAUTH;
1285
1286 /* We return the empty list, meaning no artwork
1287 * TODO: Fix to return actual artwork formats
1288 */
1289 IAP_TX_INIT(0x03, 0x17);
1290
1291 iap_send_tx();
1292 break;
1293 }
1294
1295 /* RetArtworkFormats (0x17)
1296 *
1297 * Sent from the iPod to the device
1298 */
1299
1300 /* GetTrackArtworkData (0x18)
1301 *
1302 * Request artwork for the given track
1303 *
1304 * Packet format (offset in buf[]: Description)
1305 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1306 * 0x01: Command, always 0x18
1307 * 0x02-0x05: Track index
1308 * 0x06-0x07: Format ID
1309 * 0x08-0x0B: Track offset in ms
1310 *
1311 * This command requires authentication.
1312 *
1313 * Returns:
1314 * RetTrackArtworkData
1315 *
1316 * Packet format (offset in data[]: Description)
1317 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1318 * 0x01: Command, always 0x19
1319 * 0x02-0x03: Descriptor index
1320 * 0x04: Pixel format code
1321 * 0x05-0x06: Image width in pixels
1322 * 0x07-0x08: Image height in pixels
1323 * 0x09-0x0A: Inset rectangle, top left x
1324 * 0x0B-0x0C: Inset rectangle, top left y
1325 * 0x0D-0x0E: Inset rectangle, bottom right x
1326 * 0x0F-0x10: Inset rectangle, bottom right y
1327 * 0x11-0x14: Row size in bytes
1328 * 0x15-0xNN: Image data
1329 *
1330 * If the image data does not fit in a single packet, subsequent
1331 * packets omit bytes 0x04-0x14.
1332 */
1333 case 0x18:
1334 {
1335 CHECKAUTH;
1336 CHECKLEN(0x0C);
1337
1338 /* No artwork support currently */
1339 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1340 break;
1341 }
1342
1343 /* RetTrackArtworkFormat (0x19)
1344 *
1345 * Sent from the iPod to the device
1346 */
1347
1348 /* GetPowerBatteryState (0x1A)
1349 *
1350 * Request the current power state
1351 *
1352 * Packet format (offset in buf[]: Description)
1353 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1354 * 0x01: Command, always 0x1A
1355 *
1356 * This command requires authentication.
1357 *
1358 * Returns:
1359 * RetPowerBatteryState
1360 *
1361 * Packet format (offset in data[]: Description)
1362 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1363 * 0x01: Command, always 0x1B
1364 * 0x02: Power state
1365 * 0x03: Battery state
1366 */
1367 case 0x1A:
1368 {
1369 IAP_TX_INIT(0x03, 0x1B);
1370
1371 iap_fill_power_state();
1372 iap_send_tx();
1373 break;
1374 }
1375
1376 /* RetPowerBatteryState (0x1B)
1377 *
1378 * Sent from the iPod to the device
1379 */
1380
1381 /* GetSoundCheckState (0x1C)
1382 *
1383 * Request the current sound check state
1384 *
1385 * Packet format (offset in buf[]: Description)
1386 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1387 * 0x01: Command, always 0x1C
1388 *
1389 * This command requires authentication.
1390 *
1391 * Returns:
1392 * RetSoundCheckState
1393 *
1394 * Packet format (offset in data[]: Description)
1395 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1396 * 0x01: Command, always 0x1D
1397 * 0x02: Sound check state
1398 */
1399 case 0x1C:
1400 {
1401 CHECKAUTH;
1402
1403 IAP_TX_INIT(0x03, 0x1D);
1404 IAP_TX_PUT(0x00); /* Always off */
1405
1406 iap_send_tx();
1407 break;
1408 }
1409
1410 /* RetSoundCheckState (0x1D)
1411 *
1412 * Sent from the iPod to the device
1413 */
1414
1415 /* SetSoundCheckState (0x1E)
1416 *
1417 * Set the sound check state
1418 *
1419 * Packet format (offset in buf[]: Description)
1420 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1421 * 0x01: Command, always 0x1E
1422 * 0x02: Sound check state
1423 * 0x03: Restore on exit
1424 *
1425 * This command requires authentication.
1426 *
1427 * Returns on success
1428 * IAP_ACK_OK
1429 *
1430 * Returns on failure
1431 * IAP_ACK_CMD_FAILED
1432 */
1433 case 0x1E:
1434 {
1435 CHECKAUTH;
1436 CHECKLEN(4);
1437
1438 /* Sound check is not supported right now
1439 * TODO: Fix
1440 */
1441
1442 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
1443 break;
1444 }
1445
1446 /* GetTrackArtworkTimes (0x1F)
1447 *
1448 * Request a list of timestamps at which artwork exists in a track
1449 *
1450 * Packet format (offset in buf[]: Description)
1451 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1452 * 0x01: Command, always 0x1F
1453 * 0x02-0x05: Track index
1454 * 0x06-0x07: Format ID
1455 * 0x08-0x09: Artwork Index
1456 * 0x0A-0x0B: Artwork count
1457 *
1458 * This command requires authentication.
1459 *
1460 * Returns:
1461 * RetTrackArtworkTimes
1462 *
1463 * Packet format (offset in data[]: Description)
1464 * 0x00: Lingo ID: Display Remote Lingo, always 0x03
1465 * 0x01: Command, always 0x20
1466 * 0x02-0x05: Offset in ms
1467 *
1468 * Bytes 0x02-0x05 can be repeated multiple times
1469 */
1470 case 0x1F:
1471 {
1472 uint32_t index;
1473 uint32_t trackcount;
1474
1475 CHECKAUTH;
1476 CHECKLEN(0x0C);
1477
1478 /* Artwork is currently unsuported, just check for a valid
1479 * track index
1480 */
1481 index = get_u32(&buf[0x02]);
1482 trackcount = playlist_amount();
1483
1484 if (index >= trackcount)
1485 {
1486 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1487 break;
1488 }
1489
1490 /* Send an empty list */
1491 IAP_TX_INIT(0x03, 0x20);
1492
1493 iap_send_tx();
1494 break;
1495 }
1496
1497 /* The default response is IAP_ACK_BAD_PARAM */
1498 default:
1499 {
1500#ifdef LOGF_ENABLE
1501 logf("iap: Unsupported Mode03 Command");
1502#else
1503 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1504#endif
1505 break;
1506 }
1507 }
1508}