summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES6
-rw-r--r--apps/iap.c1110
-rw-r--r--apps/iap/iap-core.c1392
-rw-r--r--apps/iap/iap-core.h250
-rw-r--r--apps/iap/iap-lingo.h23
-rw-r--r--apps/iap/iap-lingo0.c1035
-rw-r--r--apps/iap/iap-lingo2.c278
-rw-r--r--apps/iap/iap-lingo3.c1508
-rw-r--r--apps/iap/iap-lingo4.c3153
-rw-r--r--apps/misc.c8
-rw-r--r--firmware/export/iap.h4
-rw-r--r--firmware/export/kernel.h2
-rw-r--r--firmware/target/arm/pp/debug-pp.c9
-rw-r--r--tools/iap/Device/iPod.pm386
-rw-r--r--tools/iap/Makefile7
-rw-r--r--tools/iap/README23
-rw-r--r--tools/iap/device-ipod.t74
-rw-r--r--tools/iap/iap-verbose.pl1856
-rw-r--r--tools/iap/ipod-001-general.t133
-rw-r--r--tools/iap/ipod-002-lingo0.t277
-rw-r--r--tools/iap/ipod-003-lingo2.t220
21 files changed, 10629 insertions, 1125 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 8fa1a7ed40..3968666d98 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -62,7 +62,11 @@ tagtree.c
62filetree.c 62filetree.c
63scrobbler.c 63scrobbler.c
64#ifdef IPOD_ACCESSORY_PROTOCOL 64#ifdef IPOD_ACCESSORY_PROTOCOL
65iap.c 65iap/iap-core.c
66iap/iap-lingo0.c
67iap/iap-lingo2.c
68iap/iap-lingo3.c
69iap/iap-lingo4.c
66#endif 70#endif
67 71
68screen_access.c 72screen_access.c
diff --git a/apps/iap.c b/apps/iap.c
deleted file mode 100644
index 6fe0a03281..0000000000
--- a/apps/iap.c
+++ /dev/null
@@ -1,1110 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Alan Korr & Nick Robinson
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdio.h>
20#include <stdlib.h>
21#include <stdarg.h>
22#include <string.h>
23
24#include "panic.h"
25#include "iap.h"
26#include "button.h"
27#include "config.h"
28#include "cpu.h"
29#include "system.h"
30#include "kernel.h"
31#include "serial.h"
32#include "appevents.h"
33
34#include "playlist.h"
35#include "playback.h"
36#include "audio.h"
37#include "settings.h"
38#include "metadata.h"
39#include "wps.h"
40#include "sound.h"
41#include "action.h"
42#include "powermgmt.h"
43
44#include "tuner.h"
45#include "ipod_remote_tuner.h"
46
47#include "filetree.h"
48#include "dir.h"
49
50static volatile int iap_pollspeed = 0;
51static volatile bool iap_remotetick = true;
52static bool iap_setupflag = false, iap_updateflag = false;
53static int iap_changedctr = 0;
54
55static unsigned long iap_remotebtn = 0;
56static int iap_repeatbtn = 0;
57static bool iap_btnrepeat = false, iap_btnshuffle = false;
58
59static unsigned char serbuf[RX_BUFLEN];
60
61static unsigned char response[TX_BUFLEN];
62
63static char cur_dbrecord[5] = {0};
64
65/* states of the iap de-framing state machine */
66enum fsm_state {
67 ST_SYNC, /* wait for 0xFF sync byte */
68 ST_SOF, /* wait for 0x55 start-of-frame byte */
69 ST_LEN, /* receive length byte (small packet) */
70 ST_LENH, /* receive length high byte (large packet) */
71 ST_LENL, /* receive length low byte (large packet) */
72 ST_DATA, /* receive data */
73 ST_CHECK /* verify checksum */
74};
75
76static struct state_t {
77 enum fsm_state state; /* current fsm state */
78 unsigned int len; /* payload data length */
79 unsigned char *payload; /* payload data pointer */
80 unsigned int check; /* running checksum over [len,payload,check] */
81 unsigned int count; /* playload bytes counter */
82} frame_state = {
83 .state = ST_SYNC
84};
85
86static void put_u32(unsigned char *buf, uint32_t data)
87{
88 buf[0] = (data >> 24) & 0xFF;
89 buf[1] = (data >> 16) & 0xFF;
90 buf[2] = (data >> 8) & 0xFF;
91 buf[3] = (data >> 0) & 0xFF;
92}
93
94static uint32_t get_u32(const unsigned char *buf)
95{
96 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
97}
98
99static void iap_task(void)
100{
101 static int count = 0;
102
103 count += iap_pollspeed;
104 if (count < (500/10)) return;
105
106 /* exec every 500ms if pollspeed == 1 */
107 count = 0;
108 queue_post(&button_queue, SYS_IAP_PERIODIC, 0);
109}
110
111/* called by playback when the next track starts */
112static void iap_track_changed(void *ignored)
113{
114 (void)ignored;
115 iap_changedctr = 1;
116}
117
118void iap_setup(int ratenum)
119{
120 iap_bitrate_set(ratenum);
121 iap_pollspeed = 0;
122 iap_remotetick = true;
123 iap_updateflag = false;
124 iap_changedctr = 0;
125 iap_setupflag = true;
126 iap_remotebtn = BUTTON_NONE;
127 tick_add_task(iap_task);
128 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed);
129}
130
131void iap_bitrate_set(int ratenum)
132{
133 switch(ratenum)
134 {
135 case 0:
136 serial_bitrate(0);
137 break;
138 case 1:
139 serial_bitrate(9600);
140 break;
141 case 2:
142 serial_bitrate(19200);
143 break;
144 case 3:
145 serial_bitrate(38400);
146 break;
147 case 4:
148 serial_bitrate(57600);
149 break;
150 }
151}
152
153/* Message format:
154 0xff
155 0x55
156 length
157 mode
158 command (2 bytes)
159 parameters (0-n bytes)
160 checksum (length+mode+parameters+checksum == 0)
161*/
162
163void iap_send_pkt(const unsigned char * data, int len)
164{
165 int i, chksum, responselen;
166
167 if(len > TX_BUFLEN-4) len = TX_BUFLEN-4;
168 responselen = len + 4;
169
170 response[0] = 0xFF;
171 response[1] = 0x55;
172
173 chksum = response[2] = len;
174 for(i = 0; i < len; i ++)
175 {
176 chksum += data[i];
177 response[i+3] = data[i];
178 }
179
180 response[i+3] = 0x100 - (chksum & 0xFF);
181
182 for(i = 0; i < responselen; i ++)
183 {
184 while (!tx_rdy()) ;
185 tx_writec(response[i]);
186 }
187}
188
189bool iap_getc(unsigned char x)
190{
191 struct state_t *s = &frame_state;
192
193 /* run state machine to detect and extract a valid frame */
194 switch (s->state) {
195 case ST_SYNC:
196 if (x == 0xFF) {
197 s->state = ST_SOF;
198 }
199 break;
200 case ST_SOF:
201 if (x == 0x55) {
202 /* received a valid sync/SOF pair */
203 s->state = ST_LEN;
204 } else {
205 s->state = ST_SYNC;
206 return iap_getc(x);
207 }
208 break;
209 case ST_LEN:
210 s->check = x;
211 s->count = 0;
212 s->payload = serbuf;
213 if (x == 0) {
214 /* large packet */
215 s->state = ST_LENH;
216 } else {
217 /* small packet */
218 s->len = x;
219 s->state = ST_DATA;
220 }
221 break;
222 case ST_LENH:
223 s->check += x;
224 s->len = x << 8;
225 s->state = ST_LENL;
226 break;
227 case ST_LENL:
228 s->check += x;
229 s->len += x;
230 if ((s->len == 0) || (s->len > RX_BUFLEN)) {
231 /* invalid length */
232 s->state = ST_SYNC;
233 return iap_getc(x);
234 } else {
235 s->state = ST_DATA;
236 }
237 break;
238 case ST_DATA:
239 s->check += x;
240 s->payload[s->count++] = x;
241 if (s->count == s->len) {
242 s->state = ST_CHECK;
243 }
244 break;
245 case ST_CHECK:
246 s->check += x;
247 if ((s->check & 0xFF) == 0) {
248 /* done, received a valid frame */
249 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
250 }
251 s->state = ST_SYNC;
252 break;
253 default:
254 panicf("Unhandled iap state %d", (int) s->state);
255 break;
256 }
257
258 /* return true while still hunting for the sync and start-of-frame byte */
259 return (s->state == ST_SYNC) || (s->state == ST_SOF);
260}
261
262void iap_periodic(void)
263{
264 if(!iap_setupflag) return;
265 if(!iap_pollspeed) return;
266
267 /* PlayStatusChangeNotification */
268 unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
269 unsigned long time_elapsed = audio_current_track()->elapsed;
270
271 time_elapsed += wps_get_ff_rewind_count();
272
273 data[3] = 0x04; /* playing */
274
275 /* If info has changed, don't flag it right away */
276 if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
277 {
278 /* track info has changed */
279 iap_changedctr = 0;
280 data[3] = 0x01; /* 0x02 has same effect? */
281 iap_updateflag = true;
282 }
283
284 put_u32(&data[4], time_elapsed);
285 iap_send_pkt(data, sizeof(data));
286}
287
288static void iap_set_remote_volume(void)
289{
290 unsigned char data[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
291 data[4] = (char)((global_settings.volume+58) * 4);
292 iap_send_pkt(data, sizeof(data));
293}
294
295static void cmd_ok_mode0(unsigned char cmd)
296{
297 unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
298 data[3] = cmd; /* respond with cmd */
299 iap_send_pkt(data, sizeof(data));
300}
301
302static void iap_handlepkt_mode0(unsigned int len, const unsigned char *buf)
303{
304 (void)len; /* len currently unused */
305
306 unsigned int cmd = buf[1];
307 switch (cmd) {
308 /* Identify */
309 case 0x01:
310 {
311 /* FM transmitter sends this: */
312 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
313 if(buf[2] == 0x05)
314 {
315 sleep(HZ/3);
316 /* RF Transmitter: Begin transmission */
317 unsigned char data[] = {0x05, 0x02};
318 iap_send_pkt(data, sizeof(data));
319 }
320 /* FM remote sends this: */
321 /* FF 55 03 00 01 02 FA (1st thing sent) */
322 else if (buf[2] == 0x02)
323 {
324 /* useful only for apple firmware */
325 }
326 break;
327 }
328
329 /* EnterRemoteUIMode, FM transmitter sends FF 55 02 00 05 F9 */
330 case 0x05:
331 {
332 /* ACK Pending (3000 ms) */
333 unsigned char data[] = {0x00, 0x02, 0x06,
334 0x05, 0x00, 0x00, 0x0B, 0xB8};
335 iap_send_pkt(data, sizeof(data));
336 cmd_ok_mode0(cmd);
337 break;
338 }
339
340 /* ExitRemoteUIMode */
341 case 0x06:
342 {
343 audio_stop();
344 cmd_ok_mode0(cmd);
345 break;
346 }
347
348 /* RequestiPodSoftwareVersion, Ipod FM remote sends FF 55 02 00 09 F5 */
349 case 0x09:
350 {
351 /* ReturniPodSoftwareVersion, ipod5G firmware version */
352 unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01};
353 iap_send_pkt(data, sizeof(data));
354 break;
355 }
356
357 /* RequestiPodModelNum */
358 case 0x0D:
359 {
360 /* ipod is supposed to work only with 5G and nano 2G */
361 /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34,
362 0x37, 0x4C, 0x4C, 0x00}; PA147LL (IPOD 5G 60 GO) */
363 /* ReturniPodModelNum */
364 unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
365 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
366 iap_send_pkt(data, sizeof(data));
367 break;
368 }
369
370 /* RequestLingoProtocolVersion */
371 case 0x0F:
372 {
373 /* ReturnLingoProtocolVersion */
374 unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
375 data[2] = buf[2];
376 iap_send_pkt(data, sizeof(data));
377 break;
378 }
379
380 /* IdentifyDeviceLingoes */
381 case 0x13:
382 {
383 cmd_ok_mode0(cmd);
384
385 uint32_t lingoes = get_u32(&buf[2]);
386
387 if (lingoes == 0x35)
388 /* FM transmitter sends this: */
389 /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
390 {
391 /* GetAccessoryInfo */
392 unsigned char data2[] = {0x00, 0x27, 0x00};
393 iap_send_pkt(data2, sizeof(data2));
394 /* RF Transmitter: Begin transmission */
395 unsigned char data3[] = {0x05, 0x02};
396 iap_send_pkt(data3, sizeof(data3));
397 }
398 else
399 {
400 /* ipod fm remote sends this: */
401 /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
402 if (lingoes & (1 << 7)) /* bit 7 = RF tuner lingo */
403 radio_present = 1;
404 /* GetDevAuthenticationInfo */
405 unsigned char data4[] = {0x00, 0x14};
406 iap_send_pkt(data4, sizeof(data4));
407 }
408 break;
409 }
410
411 /* RetDevAuthenticationInfo */
412 case 0x15:
413 {
414 /* AckDevAuthenticationInfo */
415 unsigned char data0[] = {0x00, 0x16, 0x00};
416 iap_send_pkt(data0, sizeof(data0));
417 /* GetAccessoryInfo */
418 unsigned char data1[] = {0x00, 0x27, 0x00};
419 iap_send_pkt(data1, sizeof(data1));
420 /* AckDevAuthenticationStatus, mandatory to enable some hardware */
421 unsigned char data2[] = {0x00, 0x19, 0x00};
422 iap_send_pkt(data2, sizeof(data2));
423 if (radio_present == 1)
424 {
425 /* GetTunerCaps */
426 unsigned char data3[] = {0x07, 0x01};
427 iap_send_pkt(data3, sizeof(data3));
428 }
429 iap_set_remote_volume();
430 break;
431 }
432
433 /* RetDevAuthenticationSignature */
434 case 0x18:
435 {
436 /* Isn't used since we don't send the 0x00 0x17 command */
437 break;
438 }
439
440 /* GetIpodOptions */
441 case 0x24:
442 {
443 /* RetIpodOptions (ipod video send this) */
444 unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
445 0x00, 0x00, 0x00, 0x00, 0x01};
446 iap_send_pkt(data, sizeof(data));
447 break;
448 }
449
450 /* default response is with cmd ok packet */
451 default:
452 {
453 cmd_ok_mode0(cmd);
454 break;
455 }
456 }
457}
458
459static void iap_handlepkt_mode2(unsigned int len, const unsigned char *buf)
460{
461 if(buf[1] != 0) return;
462 iap_remotebtn = BUTTON_NONE;
463 iap_remotetick = false;
464
465 if(len >= 3 && buf[2] != 0)
466 {
467 if(buf[2] & 1)
468 iap_remotebtn |= BUTTON_RC_PLAY;
469 if(buf[2] & 2)
470 iap_remotebtn |= BUTTON_RC_VOL_UP;
471 if(buf[2] & 4)
472 iap_remotebtn |= BUTTON_RC_VOL_DOWN;
473 if(buf[2] & 8)
474 iap_remotebtn |= BUTTON_RC_RIGHT;
475 if(buf[2] & 16)
476 iap_remotebtn |= BUTTON_RC_LEFT;
477 }
478 else if(len >= 4 && buf[3] != 0)
479 {
480 if(buf[3] & 1) /* play */
481 {
482 if (audio_status() != AUDIO_STATUS_PLAY)
483 {
484 iap_remotebtn |= BUTTON_RC_PLAY;
485 iap_repeatbtn = 2;
486 iap_remotetick = false;
487 iap_changedctr = 1;
488 }
489 }
490 if(buf[3] & 2) /* pause */
491 {
492 if (audio_status() == AUDIO_STATUS_PLAY)
493 {
494 iap_remotebtn |= BUTTON_RC_PLAY;
495 iap_repeatbtn = 2;
496 iap_remotetick = false;
497 iap_changedctr = 1;
498 }
499 }
500 if((buf[3] & 128) && !iap_btnshuffle) /* shuffle */
501 {
502 iap_btnshuffle = true;
503 if(!global_settings.playlist_shuffle)
504 {
505 global_settings.playlist_shuffle = 1;
506 settings_save();
507 if (audio_status() & AUDIO_STATUS_PLAY)
508 playlist_randomise(NULL, current_tick, true);
509 }
510 else if(global_settings.playlist_shuffle)
511 {
512 global_settings.playlist_shuffle = 0;
513 settings_save();
514 if (audio_status() & AUDIO_STATUS_PLAY)
515 playlist_sort(NULL, true);
516 }
517 }
518 else
519 iap_btnshuffle = false;
520 }
521 else if(len >= 5 && buf[4] != 0)
522 {
523 if((buf[4] & 1) && !iap_btnrepeat) /* repeat */
524 {
525 int oldmode = global_settings.repeat_mode;
526 iap_btnrepeat = true;
527
528 if (oldmode == REPEAT_ONE)
529 global_settings.repeat_mode = REPEAT_OFF;
530 else if (oldmode == REPEAT_ALL)
531 global_settings.repeat_mode = REPEAT_ONE;
532 else if (oldmode == REPEAT_OFF)
533 global_settings.repeat_mode = REPEAT_ALL;
534
535 settings_save();
536 if (audio_status() & AUDIO_STATUS_PLAY)
537 audio_flush_and_reload_tracks();
538 }
539 else
540 iap_btnrepeat = false;
541
542 if(buf[4] & 16) /* ffwd */
543 {
544 iap_remotebtn |= BUTTON_RC_RIGHT;
545 }
546 if(buf[4] & 32) /* frwd */
547 {
548 iap_remotebtn |= BUTTON_RC_LEFT;
549 }
550 }
551}
552
553static void iap_handlepkt_mode3(unsigned int len, const unsigned char *buf)
554{
555 (void)len; /* len currently unused */
556
557 unsigned int cmd = buf[1];
558 switch (cmd)
559 {
560 /* GetCurrentEQProfileIndex */
561 case 0x01:
562 {
563 /* RetCurrentEQProfileIndex */
564 unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
565 iap_send_pkt(data, sizeof(data));
566 break;
567 }
568
569 /* SetRemoteEventNotification */
570 case 0x08:
571 {
572 /* ACK */
573 unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
574 iap_send_pkt(data, sizeof(data));
575 break;
576 }
577
578 /* GetiPodStateInfo */
579 case 0x0C:
580 {
581 /* request ipod volume */
582 if (buf[2] == 0x04)
583 {
584 iap_set_remote_volume();
585 }
586 break;
587 }
588
589 /* SetiPodStateInfo */
590 case 0x0E:
591 {
592 if (buf[2] == 0x04)
593 global_settings.volume = (-58)+((int)buf[4]+1)/4;
594 sound_set_volume(global_settings.volume); /* indent BUG? */
595 break;
596 }
597 }
598}
599
600static void cmd_ok_mode4(unsigned int cmd)
601{
602 unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
603 data[4] = (cmd >> 8) & 0xFF;
604 data[5] = (cmd >> 0) & 0xFF;
605 iap_send_pkt(data, sizeof(data));
606}
607
608static void get_playlist_name(unsigned char *dest,
609 unsigned long item_offset,
610 size_t max_length)
611{
612 if (item_offset == 0) return;
613 DIR* dp;
614 struct dirent* playlist_file = NULL;
615
616 dp = opendir(global_settings.playlist_catalog_dir);
617
618 char *extension;
619 unsigned long nbr = 0;
620 while ((nbr < item_offset) && ((playlist_file = readdir(dp)) != NULL))
621 {
622 /*Increment only if there is a playlist extension*/
623 if ((extension=strrchr(playlist_file->d_name, '.')) != NULL){
624 if ((strcmp(extension, ".m3u") == 0 ||
625 strcmp(extension, ".m3u8") == 0))
626 nbr++;
627 }
628 }
629 if (playlist_file != NULL) {
630 strlcpy(dest, playlist_file->d_name, max_length);
631 }
632 closedir(dp);
633}
634
635static void iap_handlepkt_mode4(unsigned int len, const unsigned char *buf)
636{
637 (void)len; /* len currently unused */
638
639 unsigned int cmd = (buf[1] << 8) | buf[2];
640 switch (cmd)
641 {
642 /* GetAudioBookSpeed */
643 case 0x0009:
644 {
645 /* ReturnAudioBookSpeed */
646 unsigned char data[] = {0x04, 0x00, 0x0A, 0x00};
647 data[3] = iap_updateflag ? 0 : 1;
648 iap_send_pkt(data, sizeof(data));
649 break;
650 }
651
652 /* SetAudioBookSpeed */
653 case 0x000B:
654 {
655 iap_updateflag = buf[3] ? 0 : 1;
656 /* respond with cmd ok packet */
657 cmd_ok_mode4(cmd);
658 break;
659 }
660
661 /* RequestProtocolVersion */
662 case 0x0012:
663 {
664 /* ReturnProtocolVersion */
665 unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
666 iap_send_pkt(data, sizeof(data));
667 break;
668 }
669
670 /* SelectDBRecord */
671 case 0x0017:
672 {
673 memcpy(cur_dbrecord, buf + 3, 5);
674 cmd_ok_mode4(cmd);
675 break;
676 }
677
678 /* GetNumberCategorizedDBRecords */
679 case 0x0018:
680 {
681 /* ReturnNumberCategorizedDBRecords */
682 unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
683 unsigned long num = 0;
684
685 DIR* dp;
686 unsigned long nbr_total_playlists = 0;
687 struct dirent* playlist_file = NULL;
688 char *extension;
689
690 switch(buf[3]) /* type number */
691 {
692 case 0x01: /* total number of playlists */
693 dp = opendir(global_settings.playlist_catalog_dir);
694 while ((playlist_file = readdir(dp)) != NULL)
695 {
696 /*Increment only if there is a playlist extension*/
697 if ((extension=strrchr(playlist_file->d_name, '.'))
698 != NULL) {
699 if ((strcmp(extension, ".m3u") == 0 ||
700 strcmp(extension, ".m3u8") == 0))
701 nbr_total_playlists++;
702 }
703 }
704 closedir(dp);
705 /*Add 1 for the main playlist*/
706 num = nbr_total_playlists + 1;
707 break;
708 case 0x05: /* total number of songs */
709 num = 1;
710 break;
711 }
712 put_u32(&data[3], num);
713 iap_send_pkt(data, sizeof(data));
714 break;
715 }
716
717 /* RetrieveCategorizedDatabaseRecords */
718 case 0x001A:
719 {
720 /* ReturnCategorizedDatabaseRecord */
721 unsigned char data[7 + MAX_PATH] =
722 {0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00,
723 'R', 'O', 'C', 'K', 'B', 'O', 'X', '\0'};
724
725 unsigned long item_offset = get_u32(&buf[4]);
726
727 get_playlist_name(data + 7, item_offset, MAX_PATH);
728 /*Remove file extension*/
729 char *dot=NULL;
730 dot = (strrchr(data+7, '.'));
731 if (dot != NULL)
732 *dot = '\0';
733 iap_send_pkt(data, 7 + strlen(data+7) + 1);
734 break;
735 }
736
737 /* GetPlayStatus */
738 case 0x001C:
739 {
740 /* ReturnPlayStatus */
741 unsigned char data[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
742 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
743 struct mp3entry *id3 = audio_current_track();
744 unsigned long time_total = id3->length;
745 unsigned long time_elapsed = id3->elapsed;
746 int status = audio_status();
747 put_u32(&data[3], time_total);
748 put_u32(&data[7], time_elapsed);
749 if (status == AUDIO_STATUS_PLAY)
750 data[11] = 0x01; /* play */
751 else if (status & AUDIO_STATUS_PAUSE)
752 data[11] = 0x02; /* pause */
753 iap_send_pkt(data, sizeof(data));
754 break;
755 }
756
757 /* GetCurrentPlayingTrackIndex */
758 case 0x001E:
759 {
760 /* ReturnCurrentPlayingTrackIndex */
761 unsigned char data[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00};
762 long playlist_pos = playlist_next(0);
763 playlist_pos -= playlist_get_first_index(NULL);
764 if(playlist_pos < 0)
765 playlist_pos += playlist_amount();
766 put_u32(&data[3], playlist_pos);
767 iap_send_pkt(data, sizeof(data));
768 break;
769 }
770
771 /* GetIndexedPlayingTrackTitle */
772 case 0x0020:
773 /* GetIndexedPlayingTrackArtistName */
774 case 0x0022:
775 /* GetIndexedPlayingTrackAlbumName */
776 case 0x0024:
777 {
778 unsigned char data[70] = {0x04, 0x00, 0xFF};
779 struct mp3entry id3;
780 int fd;
781 size_t len;
782 long tracknum = get_u32(&buf[3]);
783
784 data[2] = cmd + 1;
785 memcpy(&id3, audio_current_track(), sizeof(id3));
786 tracknum += playlist_get_first_index(NULL);
787 if(tracknum >= playlist_amount())
788 tracknum -= playlist_amount();
789
790 /* If the tracknumber is not the current one,
791 read id3 from disk */
792 if(playlist_next(0) != tracknum)
793 {
794 struct playlist_track_info info;
795 playlist_get_track_info(NULL, tracknum, &info);
796 fd = open(info.filename, O_RDONLY);
797 memset(&id3, 0, sizeof(struct mp3entry));
798 get_metadata(&id3, fd, info.filename);
799 close(fd);
800 }
801
802 /* Return the requested track data */
803 switch(cmd)
804 {
805 case 0x20:
806 len = strlcpy((char *)&data[3], id3.title, 64);
807 iap_send_pkt(data, 4+len);
808 break;
809 case 0x22:
810 len = strlcpy((char *)&data[3], id3.artist, 64);
811 iap_send_pkt(data, 4+len);
812 break;
813 case 0x24:
814 len = strlcpy((char *)&data[3], id3.album, 64);
815 iap_send_pkt(data, 4+len);
816 break;
817 }
818 break;
819 }
820
821 /* SetPlayStatusChangeNotification */
822 case 0x0026:
823 {
824 iap_pollspeed = buf[3] ? 1 : 0;
825 /* respond with cmd ok packet */
826 cmd_ok_mode4(cmd);
827 break;
828 }
829
830 /* PlayCurrentSelection */
831 case 0x0028:
832 {
833 switch (cur_dbrecord[0])
834 {
835 case 0x01:
836 {/*Playlist*/
837 unsigned long item_offset = get_u32(&cur_dbrecord[1]);
838
839 unsigned char selected_playlist
840 [sizeof(global_settings.playlist_catalog_dir)
841 + 1
842 + MAX_PATH] = {0};
843
844 strcpy(selected_playlist,
845 global_settings.playlist_catalog_dir);
846 int len = strlen(selected_playlist);
847 selected_playlist[len] = '/';
848 get_playlist_name (selected_playlist + len + 1,
849 item_offset,
850 MAX_PATH);
851 ft_play_playlist(selected_playlist,
852 global_settings.playlist_catalog_dir,
853 strrchr(selected_playlist, '/') + 1);
854 break;
855 }
856 }
857 cmd_ok_mode4(cmd);
858 break;
859 }
860
861 /* PlayControl */
862 case 0x0029:
863 {
864 switch(buf[3])
865 {
866 case 0x01: /* play/pause */
867 iap_remotebtn = BUTTON_RC_PLAY;
868 iap_repeatbtn = 2;
869 iap_remotetick = false;
870 iap_changedctr = 1;
871 break;
872 case 0x02: /* stop */
873 iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT;
874 iap_repeatbtn = 2;
875 iap_remotetick = false;
876 iap_changedctr = 1;
877 break;
878 case 0x03: /* skip++ */
879 iap_remotebtn = BUTTON_RC_RIGHT;
880 iap_repeatbtn = 2;
881 iap_remotetick = false;
882 break;
883 case 0x04: /* skip-- */
884 iap_remotebtn = BUTTON_RC_LEFT;
885 iap_repeatbtn = 2;
886 iap_remotetick = false;
887 break;
888 case 0x05: /* ffwd */
889 iap_remotebtn = BUTTON_RC_RIGHT;
890 iap_remotetick = false;
891 if(iap_pollspeed) iap_pollspeed = 5;
892 break;
893 case 0x06: /* frwd */
894 iap_remotebtn = BUTTON_RC_LEFT;
895 iap_remotetick = false;
896 if(iap_pollspeed) iap_pollspeed = 5;
897 break;
898 case 0x07: /* end ffwd/frwd */
899 iap_remotebtn = BUTTON_NONE;
900 iap_remotetick = false;
901 if(iap_pollspeed) iap_pollspeed = 1;
902 break;
903 }
904 /* respond with cmd ok packet */
905 cmd_ok_mode4(cmd);
906 break;
907 }
908
909 /* GetShuffle */
910 case 0x002C:
911 {
912 /* ReturnShuffle */
913 unsigned char data[] = {0x04, 0x00, 0x2D, 0x00};
914 data[3] = global_settings.playlist_shuffle ? 1 : 0;
915 iap_send_pkt(data, sizeof(data));
916 break;
917 }
918
919 /* SetShuffle */
920 case 0x002E:
921 {
922 if(buf[3] && !global_settings.playlist_shuffle)
923 {
924 global_settings.playlist_shuffle = 1;
925 settings_save();
926 if (audio_status() & AUDIO_STATUS_PLAY)
927 playlist_randomise(NULL, current_tick, true);
928 }
929 else if(!buf[3] && global_settings.playlist_shuffle)
930 {
931 global_settings.playlist_shuffle = 0;
932 settings_save();
933 if (audio_status() & AUDIO_STATUS_PLAY)
934 playlist_sort(NULL, true);
935 }
936
937 /* respond with cmd ok packet */
938 cmd_ok_mode4(cmd);
939 break;
940 }
941
942 /* GetRepeat */
943 case 0x002F:
944 {
945 /* ReturnRepeat */
946 unsigned char data[] = {0x04, 0x00, 0x30, 0x00};
947 if(global_settings.repeat_mode == REPEAT_OFF)
948 data[3] = 0;
949 else if(global_settings.repeat_mode == REPEAT_ONE)
950 data[3] = 1;
951 else
952 data[3] = 2;
953 iap_send_pkt(data, sizeof(data));
954 break;
955 }
956
957 /* SetRepeat */
958 case 0x0031:
959 {
960 int oldmode = global_settings.repeat_mode;
961 if (buf[3] == 0)
962 global_settings.repeat_mode = REPEAT_OFF;
963 else if (buf[3] == 1)
964 global_settings.repeat_mode = REPEAT_ONE;
965 else if (buf[3] == 2)
966 global_settings.repeat_mode = REPEAT_ALL;
967
968 if (oldmode != global_settings.repeat_mode)
969 {
970 settings_save();
971 if (audio_status() & AUDIO_STATUS_PLAY)
972 audio_flush_and_reload_tracks();
973 }
974
975 /* respond with cmd ok packet */
976 cmd_ok_mode4(cmd);
977 break;
978 }
979
980 /* GetMonoDisplayImageLimits */
981 case 0x0033:
982 {
983 /* ReturnMonoDisplayImageLimits */
984 unsigned char data[] = {0x04, 0x00, 0x34,
985 LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
986 LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
987 0x01};
988 iap_send_pkt(data, sizeof(data));
989 break;
990 }
991
992 /* GetNumPlayingTracks */
993 case 0x0035:
994 {
995 /* ReturnNumPlayingTracks */
996 unsigned char data[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00};
997 unsigned long playlist_amt = playlist_amount();
998 put_u32(&data[3], playlist_amt);
999 iap_send_pkt(data, sizeof(data));
1000 break;
1001 }
1002
1003 /* SetCurrentPlayingTrack */
1004 case 0x0037:
1005 {
1006 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
1007 long tracknum = get_u32(&buf[3]);
1008
1009 audio_pause();
1010 audio_skip(tracknum - playlist_next(0));
1011 if (!paused)
1012 audio_resume();
1013
1014 /* respond with cmd ok packet */
1015 cmd_ok_mode4(cmd);
1016 break;
1017 }
1018
1019 default:
1020 {
1021 /* default response is with cmd ok packet */
1022 cmd_ok_mode4(cmd);
1023 break;
1024 }
1025 }
1026}
1027
1028static void iap_handlepkt_mode7(unsigned int len, const unsigned char *buf)
1029{
1030 unsigned int cmd = buf[1];
1031 switch (cmd)
1032 {
1033 /* RetTunerCaps */
1034 case 0x02:
1035 {
1036 /* do nothing */
1037
1038 /* GetAccessoryInfo */
1039 unsigned char data[] = {0x00, 0x27, 0x00};
1040 iap_send_pkt(data, sizeof(data));
1041 break;
1042 }
1043
1044 /* RetTunerFreq */
1045 case 0x0A:
1046 /* fall through */
1047 /* TunerSeekDone */
1048 case 0x13:
1049 {
1050 rmt_tuner_freq(len, buf);
1051 break;
1052 }
1053
1054 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
1055 case 0x21:
1056 {
1057 rmt_tuner_rds_data(len, buf);
1058 break;
1059 }
1060 }
1061}
1062
1063void iap_handlepkt(void)
1064{
1065 struct state_t *s = &frame_state;
1066
1067 if(!iap_setupflag) return;
1068
1069 /* if we are waiting for a remote button to go out,
1070 delay the handling of the new packet */
1071 if(!iap_remotetick)
1072 {
1073 queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
1074 return;
1075 }
1076
1077 /* handle command by mode */
1078 unsigned char mode = s->payload[0];
1079 switch (mode) {
1080 case 0: iap_handlepkt_mode0(s->len, s->payload); break;
1081 case 2: iap_handlepkt_mode2(s->len, s->payload); break;
1082 case 3: iap_handlepkt_mode3(s->len, s->payload); break;
1083 case 4: iap_handlepkt_mode4(s->len, s->payload); break;
1084 case 7: iap_handlepkt_mode7(s->len, s->payload); break;
1085 }
1086}
1087
1088int remote_control_rx(void)
1089{
1090 int btn = iap_remotebtn;
1091 if(iap_repeatbtn)
1092 {
1093 iap_repeatbtn--;
1094 if(!iap_repeatbtn)
1095 {
1096 iap_remotebtn = BUTTON_NONE;
1097 iap_remotetick = true;
1098 }
1099 }
1100 else
1101 iap_remotetick = true;
1102
1103 return btn;
1104}
1105
1106const unsigned char *iap_get_serbuf(void)
1107{
1108 return serbuf;
1109}
1110
diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c
new file mode 100644
index 0000000000..ddcb22853a
--- /dev/null
+++ b/apps/iap/iap-core.c
@@ -0,0 +1,1392 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Alan Korr & Nick Robinson
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdio.h>
20#include <stdlib.h>
21#include <stdarg.h>
22#include <string.h>
23
24#include "panic.h"
25#include "iap-core.h"
26#include "iap-lingo.h"
27#include "button.h"
28#include "config.h"
29#include "cpu.h"
30#include "system.h"
31#include "kernel.h"
32#include "thread.h"
33#include "serial.h"
34#include "appevents.h"
35#include "core_alloc.h"
36
37#include "playlist.h"
38#include "playback.h"
39#include "audio.h"
40#include "settings.h"
41#include "metadata.h"
42#include "sound.h"
43#include "action.h"
44#include "powermgmt.h"
45
46#include "tuner.h"
47#include "ipod_remote_tuner.h"
48
49
50/* MS_TO_TICKS converts a milisecond time period into the
51 * corresponding amount of ticks. If the time period cannot
52 * be accurately measured in ticks it will round up.
53 */
54#if (HZ>1000)
55#error "HZ is >1000, please fix MS_TO_TICKS"
56#endif
57#define MS_PER_HZ (1000/HZ)
58#define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ)
59/* IAP specifies a timeout of 25ms for traffic from a device to the iPod.
60 * Depending on HZ this cannot be accurately measured. Find out the next
61 * best thing.
62 */
63#define IAP_PKT_TIMEOUT (MS_TO_TICKS(25))
64
65/* Events in the iap_queue */
66#define IAP_EV_TICK (1) /* The regular task timeout */
67#define IAP_EV_MSG_RCVD (2) /* A complete message has been received from the device */
68#define IAP_EV_MALLOC (3) /* Allocate memory for the RX/TX buffers */
69
70static bool iap_started = false;
71static bool iap_setupflag = false, iap_running = false;
72/* This is set to true if a SYS_POWEROFF message is received,
73 * signalling impending power off
74 */
75static bool iap_shutdown = false;
76static struct timeout iap_task_tmo;
77
78unsigned long iap_remotebtn = 0;
79/* Used to make sure a button press is delivered to the processing
80 * backend. While this is !0, no new incoming messasges are processed.
81 * Counted down by remote_control_rx()
82 */
83int iap_repeatbtn = 0;
84/* Used to time out button down events in case we miss the button up event
85 * from the device somehow.
86 * If a device sends a button down event it's required to repeat that event
87 * every 30 to 100ms as long as the button is pressed, and send an explicit
88 * button up event if the button is released.
89 * In case the button up event is lost any down events will time out after
90 * ~200ms.
91 * iap_periodic() will count down this variable and reset all buttons if
92 * it reaches 0
93 */
94unsigned int iap_timeoutbtn = 0;
95bool iap_btnrepeat = false, iap_btnshuffle = false;
96
97static long thread_stack[(DEFAULT_STACK_SIZE*6)/sizeof(long)];
98static struct event_queue iap_queue;
99
100/* These are pointer used to manage a dynamically allocated buffer which
101 * will hold both the RX and TX side of things.
102 *
103 * iap_buffer_handle is the handle returned from core_alloc()
104 * iap_buffers points to the start of the complete buffer
105 *
106 * The buffer is partitioned as follows:
107 * - TX_BUFLEN+6 bytes for the TX buffer
108 * The 6 extra bytes are for the sync byte, the SOP byte, the length indicators
109 * (3 bytes) and the checksum byte.
110 * iap_txstart points to the beginning of the TX buffer
111 * iap_txpayload points to the beginning of the payload portion of the TX buffer
112 * iap_txnext points to the position where the next byte will be placed
113 *
114 * - RX_BUFLEN+2 bytes for the RX buffer
115 * The RX buffer can hold multiple packets at once, up to it's
116 * maximum capacity. Every packet consists of a two byte length
117 * indicator followed by the actual payload. The length indicator
118 * is two bytes for every length, even for packets with a length <256
119 * bytes.
120 *
121 * Once a packet has been processed from the RX buffer the rest
122 * of the buffer (and the pointers below) are shifted to the front
123 * so that the next packet again starts at the beginning of the
124 * buffer. This happens with interrupts disabled, to prevent
125 * writing into the buffer during the move.
126 *
127 * iap_rxstart points to the beginning of the RX buffer
128 * iap_rxpayload starts to the beginning of the currently recieved
129 * packet
130 * iap_rxnext points to the position where the next incoming byte
131 * will be placed
132 * iap_rxlen is not a pointer, but an indicator of the free
133 * space left in the RX buffer.
134 *
135 * The RX buffer is placed behind the TX buffer so that an eventual TX
136 * buffer overflow has some place to spill into where it will not cause
137 * immediate damage. See the comments for IAP_TX_* and iap_send_tx()
138 */
139#define IAP_MALLOC_SIZE (TX_BUFLEN+6+RX_BUFLEN+2)
140#ifdef IAP_MALLOC_DYNAMIC
141static int iap_buffer_handle;
142#endif
143static unsigned char* iap_buffers;
144static unsigned char* iap_rxstart;
145static unsigned char* iap_rxpayload;
146static unsigned char* iap_rxnext;
147static uint32_t iap_rxlen;
148static unsigned char* iap_txstart;
149unsigned char* iap_txpayload;
150unsigned char* iap_txnext;
151
152/* The versions of the various Lingoes we support. A major version
153 * of 0 means unsupported
154 */
155unsigned char lingo_versions[32][2] = {
156 {1, 9}, /* General lingo, 0x00 */
157 {0, 0}, /* Microphone lingo, 0x01, unsupported */
158 {1, 2}, /* Simple remote lingo, 0x02 */
159 {1, 5}, /* Display remote lingo, 0x03 */
160 {1, 12}, /* Extended Interface lingo, 0x04 */
161 {1, 1}, /* RF/BT Transmitter lingo, 0x05 */
162 {} /* All others are unsupported */
163};
164
165/* states of the iap de-framing state machine */
166enum fsm_state {
167 ST_SYNC, /* wait for 0xFF sync byte */
168 ST_SOF, /* wait for 0x55 start-of-frame byte */
169 ST_LEN, /* receive length byte (small packet) */
170 ST_LENH, /* receive length high byte (large packet) */
171 ST_LENL, /* receive length low byte (large packet) */
172 ST_DATA, /* receive data */
173 ST_CHECK /* verify checksum */
174};
175
176static struct state_t {
177 enum fsm_state state; /* current fsm state */
178 unsigned int len; /* payload data length */
179 unsigned int check; /* running checksum over [len,payload,check] */
180 unsigned int count; /* playload bytes counter */
181} frame_state = {
182 .state = ST_SYNC
183};
184
185enum interface_state interface_state = IST_STANDARD;
186
187struct device_t device;
188
189#ifdef IAP_MALLOC_DYNAMIC
190static int iap_move_callback(int handle, void* current, void* new);
191
192static struct buflib_callbacks iap_buflib_callbacks = {
193 iap_move_callback,
194 NULL
195};
196#endif
197
198static void iap_malloc(void);
199
200void put_u16(unsigned char *buf, const uint16_t data)
201{
202 buf[0] = (data >> 8) & 0xFF;
203 buf[1] = (data >> 0) & 0xFF;
204}
205
206void put_u32(unsigned char *buf, const uint32_t data)
207{
208 buf[0] = (data >> 24) & 0xFF;
209 buf[1] = (data >> 16) & 0xFF;
210 buf[2] = (data >> 8) & 0xFF;
211 buf[3] = (data >> 0) & 0xFF;
212}
213
214uint32_t get_u32(const unsigned char *buf)
215{
216 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
217}
218
219uint16_t get_u16(const unsigned char *buf)
220{
221 return (buf[0] << 8) | buf[1];
222}
223
224#if defined(LOGF_ENABLE) && defined(ROCKBOX_HAS_LOGF)
225/* Convert a buffer into a printable string, perl style
226 * buf contains the data to be converted, len is the length
227 * of the buffer.
228 *
229 * This will convert at most 1024 bytes from buf
230 */
231static char* hexstring(const unsigned char *buf, unsigned int len) {
232 static char hexbuf[4097];
233 unsigned int l;
234 const unsigned char* p;
235 unsigned char* out;
236 unsigned char h[] = {'0', '1', '2', '3', '4', '5', '6', '7',
237 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
238
239 if (len > 1024) {
240 l = 1024;
241 } else {
242 l = len;
243 }
244 p = buf;
245 out = hexbuf;
246 do {
247 *out++ = h[(*p)>>4];
248 *out++ = h[*p & 0x0F];
249 } while(--l && p++);
250
251 *out = 0x00;
252
253 return hexbuf;
254}
255#endif
256
257
258void iap_tx_strlcpy(const unsigned char *str)
259{
260 ptrdiff_t txfree;
261 int r;
262
263 txfree = TX_BUFLEN - (iap_txnext - iap_txstart);
264 r = strlcpy(iap_txnext, str, txfree);
265
266 if (r < txfree)
267 {
268 /* No truncation occured
269 * Account for the terminating \0
270 */
271 iap_txnext += (r+1);
272 } else {
273 /* Truncation occured, the TX buffer is now full. */
274 iap_txnext = iap_txstart + TX_BUFLEN;
275 }
276}
277
278void iap_reset_auth(struct auth_t* auth)
279{
280 auth->state = AUST_NONE;
281 auth->max_section = 0;
282 auth->next_section = 0;
283}
284
285void iap_reset_device(struct device_t* device)
286{
287 iap_reset_auth(&(device->auth));
288 device->lingoes = 0;
289 device->notifications = 0;
290 device->changed_notifications = 0;
291 device->do_notify = false;
292 device->do_power_notify = false;
293 device->accinfo = ACCST_NONE;
294 device->capabilities = 0;
295 device->capabilities_queried = 0;
296}
297
298static int iap_task(struct timeout *tmo)
299{
300 (void) tmo;
301
302 queue_post(&iap_queue, IAP_EV_TICK, 0);
303 return MS_TO_TICKS(100);
304}
305
306/* This thread is waiting for events posted to iap_queue and calls
307 * the appropriate subroutines in response
308 */
309static void iap_thread(void)
310{
311 struct queue_event ev;
312 while(1) {
313 queue_wait(&iap_queue, &ev);
314 switch (ev.id)
315 {
316 /* Handle the regular 100ms tick used for driving the
317 * authentication state machine and notifications
318 */
319 case IAP_EV_TICK:
320 {
321 iap_periodic();
322 break;
323 }
324
325 /* Handle a newly received message from the device */
326 case IAP_EV_MSG_RCVD:
327 {
328 iap_handlepkt();
329 break;
330 }
331
332 /* Handle memory allocation. This is used only once, during
333 * startup
334 */
335 case IAP_EV_MALLOC:
336 {
337 iap_malloc();
338 break;
339 }
340
341 /* Handle poweroff message */
342 case SYS_POWEROFF:
343 {
344 iap_shutdown = true;
345 break;
346 }
347 }
348 }
349}
350
351/* called by playback when the next track starts */
352static void iap_track_changed(void *ignored)
353{
354 (void)ignored;
355 if ((interface_state == IST_EXTENDED) && device.do_notify) {
356 long playlist_pos = playlist_next(0);
357 playlist_pos -= playlist_get_first_index(NULL);
358 if(playlist_pos < 0)
359 playlist_pos += playlist_amount();
360
361 IAP_TX_INIT4(0x04, 0x0027);
362 IAP_TX_PUT(0x01);
363 IAP_TX_PUT_U32(playlist_pos);
364
365 iap_send_tx();
366 return;
367 }
368}
369
370/* Do general setup of the needed infrastructure.
371 *
372 * Please note that a lot of additional work is done by iap_start()
373 */
374void iap_setup(const int ratenum)
375{
376 iap_bitrate_set(ratenum);
377 iap_remotebtn = BUTTON_NONE;
378 iap_setupflag = true;
379 iap_started = false;
380 iap_running = false;
381}
382
383/* Actually bring up the message queue, message handler thread and
384 * notification timer
385 *
386 * NOTE: This is running in interrupt context
387 */
388static void iap_start(void)
389{
390 unsigned int tid;
391
392 if (iap_started)
393 return;
394
395 iap_reset_device(&device);
396 queue_init(&iap_queue, true);
397 tid = create_thread(iap_thread, thread_stack, sizeof(thread_stack),
398 0, "iap"
399 IF_PRIO(, PRIORITY_SYSTEM)
400 IF_COP(, CPU));
401 if (!tid)
402 panicf("Could not create iap thread");
403 timeout_register(&iap_task_tmo, iap_task, MS_TO_TICKS(100), (intptr_t)NULL);
404 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed);
405
406 /* Since we cannot allocate memory while in interrupt context
407 * post a message to our own queue to get that done
408 */
409 queue_post(&iap_queue, IAP_EV_MALLOC, 0);
410 iap_started = true;
411}
412
413static void iap_malloc(void)
414{
415#ifndef IAP_MALLOC_DYNAMIC
416 static unsigned char serbuf[IAP_MALLOC_SIZE];
417#endif
418
419 if (iap_running)
420 return;
421
422#ifdef IAP_MALLOC_DYNAMIC
423 iap_buffer_handle = core_alloc_ex("iap", IAP_MALLOC_SIZE, &iap_buflib_callbacks);
424 if (iap_buffer_handle < 0)
425 panicf("Could not allocate buffer memory");
426 iap_buffers = core_get_data(iap_buffer_handle);
427#else
428 iap_buffers = serbuf;
429#endif
430 iap_txstart = iap_buffers;
431 iap_txpayload = iap_txstart+5;
432 iap_txnext = iap_txpayload;
433 iap_rxstart = iap_buffers+(TX_BUFLEN+6);
434 iap_rxpayload = iap_rxstart;
435 iap_rxnext = iap_rxpayload;
436 iap_rxlen = RX_BUFLEN+2;
437 iap_running = true;
438}
439
440void iap_bitrate_set(const int ratenum)
441{
442 switch(ratenum)
443 {
444 case 0:
445 serial_bitrate(0);
446 break;
447 case 1:
448 serial_bitrate(9600);
449 break;
450 case 2:
451 serial_bitrate(19200);
452 break;
453 case 3:
454 serial_bitrate(38400);
455 break;
456 case 4:
457 serial_bitrate(57600);
458 break;
459 }
460}
461
462/* Message format:
463 0xff
464 0x55
465 length
466 mode
467 command (2 bytes)
468 parameters (0-n bytes)
469 checksum (length+mode+parameters+checksum == 0)
470*/
471
472/* Send the current content of the TX buffer.
473 * This will check for TX buffer overflow and panic, but it might
474 * be too late by then (although one would have to overflow the complete
475 * RX buffer as well)
476 */
477void iap_send_tx(void)
478{
479 int i, chksum;
480 ptrdiff_t txlen;
481 unsigned char* txstart;
482
483 txlen = iap_txnext - iap_txpayload;
484
485 if (txlen <= 0)
486 return;
487
488 if (txlen > TX_BUFLEN)
489 panicf("IAP: TX buffer overflow");
490
491 if (txlen < 256)
492 {
493 /* Short packet */
494 txstart = iap_txstart+2;
495 *(txstart+2) = txlen;
496 chksum = txlen;
497 } else {
498 /* Long packet */
499 txstart = iap_txstart;
500 *(txstart+2) = 0x00;
501 *(txstart+3) = (txlen >> 8) & 0xFF;
502 *(txstart+4) = (txlen) & 0xFF;
503 chksum = *(txstart+3) + *(txstart+4);
504 }
505 *(txstart) = 0xFF;
506 *(txstart+1) = 0x55;
507
508 for (i=0; i<txlen; i++)
509 {
510 chksum += iap_txpayload[i];
511 }
512 *(iap_txnext) = 0x100 - (chksum & 0xFF);
513
514#ifdef LOGF_ENABLE
515 logf("T: %s", hexstring(txstart+3, (iap_txnext - txstart)-3));
516#endif
517 for (i=0; i <= (iap_txnext - txstart); i++)
518 {
519 while(!tx_rdy()) ;
520 tx_writec(txstart[i]);
521 }
522}
523
524/* This is just a compatibility wrapper around the new TX buffer
525 * infrastructure
526 */
527void iap_send_pkt(const unsigned char * data, const int len)
528{
529 if (!iap_running)
530 return;
531
532 iap_txnext = iap_txpayload;
533 IAP_TX_PUT_DATA(data, len);
534 iap_send_tx();
535}
536
537bool iap_getc(const unsigned char x)
538{
539 struct state_t *s = &frame_state;
540 static long pkt_timeout;
541
542 if (!iap_setupflag)
543 return false;
544
545 /* Check the time since the last packet arrived. */
546 if ((s->state != ST_SYNC) && TIME_AFTER(current_tick, pkt_timeout)) {
547 /* Packet timeouts only make sense while not waiting for the
548 * sync byte */
549 s->state = ST_SYNC;
550 return iap_getc(x);
551 }
552
553
554 /* run state machine to detect and extract a valid frame */
555 switch (s->state) {
556 case ST_SYNC:
557 if (x == 0xFF) {
558 /* The IAP infrastructure is started by the first received sync
559 * byte. It takes a while to spin up, so do not advance the state
560 * machine until it has started.
561 */
562 if (!iap_running)
563 {
564 iap_start();
565 break;
566 }
567 iap_rxnext = iap_rxpayload;
568 s->state = ST_SOF;
569 }
570 break;
571 case ST_SOF:
572 if (x == 0x55) {
573 /* received a valid sync/SOF pair */
574 s->state = ST_LEN;
575 } else {
576 s->state = ST_SYNC;
577 return iap_getc(x);
578 }
579 break;
580 case ST_LEN:
581 s->check = x;
582 s->count = 0;
583 if (x == 0) {
584 /* large packet */
585 s->state = ST_LENH;
586 } else {
587 /* small packet */
588 if (x > (iap_rxlen-2))
589 {
590 /* Packet too long for buffer */
591 s->state = ST_SYNC;
592 break;
593 }
594 s->len = x;
595 s->state = ST_DATA;
596 put_u16(iap_rxnext, s->len);
597 iap_rxnext += 2;
598 }
599 break;
600 case ST_LENH:
601 s->check += x;
602 s->len = x << 8;
603 s->state = ST_LENL;
604 break;
605 case ST_LENL:
606 s->check += x;
607 s->len += x;
608 if ((s->len == 0) || (s->len > (iap_rxlen-2))) {
609 /* invalid length */
610 s->state = ST_SYNC;
611 break;
612 } else {
613 s->state = ST_DATA;
614 put_u16(iap_rxnext, s->len);
615 iap_rxnext += 2;
616 }
617 break;
618 case ST_DATA:
619 s->check += x;
620 *(iap_rxnext++) = x;
621 s->count += 1;
622 if (s->count == s->len) {
623 s->state = ST_CHECK;
624 }
625 break;
626 case ST_CHECK:
627 s->check += x;
628 if ((s->check & 0xFF) == 0) {
629 /* done, received a valid frame */
630 iap_rxpayload = iap_rxnext;
631 queue_post(&iap_queue, IAP_EV_MSG_RCVD, 0);
632 } else {
633 /* Invalid frame */
634 }
635 s->state = ST_SYNC;
636 break;
637 default:
638#ifdef LOGF_ENABLE
639 logf("Unhandled iap state %d", (int) s->state);
640#else
641 panicf("Unhandled iap state %d", (int) s->state);
642#endif
643 break;
644 }
645
646 pkt_timeout = current_tick + IAP_PKT_TIMEOUT;
647
648 /* return true while still hunting for the sync and start-of-frame byte */
649 return (s->state == ST_SYNC) || (s->state == ST_SOF);
650}
651
652void iap_get_trackinfo(const unsigned int track, struct mp3entry* id3)
653{
654 int tracknum;
655 int fd;
656 struct playlist_track_info info;
657
658 tracknum = track;
659
660 tracknum += playlist_get_first_index(NULL);
661 if(tracknum >= playlist_amount())
662 tracknum -= playlist_amount();
663
664 /* If the tracknumber is not the current one,
665 read id3 from disk */
666 if(playlist_next(0) != tracknum)
667 {
668 playlist_get_track_info(NULL, tracknum, &info);
669 fd = open(info.filename, O_RDONLY);
670 memset(id3, 0, sizeof(*id3));
671 get_metadata(id3, fd, info.filename);
672 close(fd);
673 } else {
674 memcpy(id3, audio_current_track(), sizeof(*id3));
675 }
676}
677
678uint32_t iap_get_trackpos(void)
679{
680 struct mp3entry *id3 = audio_current_track();
681
682 return id3->elapsed;
683}
684
685uint32_t iap_get_trackindex(void)
686{
687 struct playlist_info* playlist = playlist_get_current();
688
689 return (playlist->index - playlist->first_index);
690}
691
692void iap_periodic(void)
693{
694 static int count;
695
696 if(!iap_setupflag) return;
697
698 /* Handle pending authentication tasks */
699 switch (device.auth.state)
700 {
701 case AUST_INIT:
702 {
703 /* Send out GetDevAuthenticationInfo */
704 IAP_TX_INIT(0x00, 0x14);
705
706 iap_send_tx();
707 device.auth.state = AUST_CERTREQ;
708 break;
709 }
710
711 case AUST_CERTDONE:
712 {
713 /* Send out GetDevAuthenticationSignature, with
714 * 20 bytes of challenge and a retry counter of 1.
715 * Since we do not really care about the content of the
716 * challenge we just use the first 20 bytes of whatever
717 * is in the RX buffer right now.
718 */
719 IAP_TX_INIT(0x00, 0x17);
720 IAP_TX_PUT_DATA(iap_rxstart, 20);
721 IAP_TX_PUT(0x01);
722
723 iap_send_tx();
724 device.auth.state = AUST_CHASENT;
725 break;
726 }
727
728 default:
729 {
730 break;
731 }
732 }
733
734 /* Time out button down events */
735 if (iap_timeoutbtn)
736 iap_timeoutbtn -= 1;
737
738 if (!iap_timeoutbtn)
739 {
740 iap_remotebtn = BUTTON_NONE;
741 iap_repeatbtn = 0;
742 iap_btnshuffle = false;
743 iap_btnrepeat = false;
744 }
745
746 /* Handle power down messages. */
747 if (iap_shutdown && device.do_power_notify)
748 {
749 /* NotifyiPodStateChange */
750 IAP_TX_INIT(0x00, 0x23);
751 IAP_TX_PUT(0x01);
752
753 iap_send_tx();
754
755 /* No further actions, we're going down */
756 iap_reset_device(&device);
757 return;
758 }
759
760 /* Handle GetAccessoryInfo messages */
761 if (device.accinfo == ACCST_INIT)
762 {
763 /* GetAccessoryInfo */
764 IAP_TX_INIT(0x00, 0x27);
765 IAP_TX_PUT(0x00);
766
767 iap_send_tx();
768 device.accinfo = ACCST_SENT;
769 }
770
771 /* Do not send requests for device information while
772 * an authentication is still running, this seems to
773 * confuse some devices
774 */
775 if (!DEVICE_AUTH_RUNNING && (device.accinfo == ACCST_DATA))
776 {
777 int first_set;
778
779 /* Find the first bit set in the capabilities field,
780 * ignoring those we already asked for
781 */
782 first_set = find_first_set_bit(device.capabilities & (~device.capabilities_queried));
783
784 if (first_set != 32)
785 {
786 /* Add bit to queried cababilities */
787 device.capabilities_queried |= BIT_N(first_set);
788
789 switch (first_set)
790 {
791 /* Name */
792 case 0x01:
793 /* Firmware version */
794 case 0x04:
795 /* Hardware version */
796 case 0x05:
797 /* Manufacturer */
798 case 0x06:
799 /* Model number */
800 case 0x07:
801 /* Serial number */
802 case 0x08:
803 /* Maximum payload size */
804 case 0x09:
805 {
806 IAP_TX_INIT(0x00, 0x27);
807 IAP_TX_PUT(first_set);
808
809 iap_send_tx();
810 break;
811 }
812
813 /* Minimum supported iPod firmware version */
814 case 0x02:
815 {
816 IAP_TX_INIT(0x00, 0x27);
817 IAP_TX_PUT(2);
818 IAP_TX_PUT_U32(IAP_IPOD_MODEL);
819 IAP_TX_PUT(IAP_IPOD_FIRMWARE_MAJOR);
820 IAP_TX_PUT(IAP_IPOD_FIRMWARE_MINOR);
821 IAP_TX_PUT(IAP_IPOD_FIRMWARE_REV);
822
823 iap_send_tx();
824 break;
825 }
826
827 /* Minimum supported lingo version. Queries Lingo 0 */
828 case 0x03:
829 {
830 IAP_TX_INIT(0x00, 0x27);
831 IAP_TX_PUT(3);
832 IAP_TX_PUT(0);
833
834 iap_send_tx();
835 break;
836 }
837 }
838
839 device.accinfo = ACCST_SENT;
840 }
841 }
842
843 if (!device.do_notify) return;
844 if (device.notifications == 0) return;
845
846 /* Volume change notifications are sent every 100ms */
847 if (device.notifications & (BIT_N(4) | BIT_N(16))) {
848 /* Currently we do not track volume changes, so this is
849 * never sent.
850 *
851 * TODO: Fix volume tracking
852 */
853 }
854
855 /* All other events are sent every 500ms */
856 count += 1;
857 if (count < 5) return;
858
859 count = 0;
860
861 /* RemoteEventNotification */
862
863 /* Mode 04 PlayStatusChangeNotification */
864 /* Are we in Extended Mode */
865 if (interface_state == IST_EXTENDED) {
866 /* Return Track Position */
867 struct mp3entry *id3 = audio_current_track();
868 unsigned long time_elapsed = id3->elapsed;
869 IAP_TX_INIT4(0x04, 0x0027);
870 IAP_TX_PUT(0x04);
871 IAP_TX_PUT_U32(time_elapsed);
872
873 iap_send_tx();
874 }
875
876 /* Track position (ms) or Track position (s) */
877 if (device.notifications & (BIT_N(0) | BIT_N(15)))
878 {
879 uint32_t t;
880 uint16_t ts;
881 bool changed;
882
883 t = iap_get_trackpos();
884 ts = (t / 1000) & 0xFFFF;
885
886 if ((device.notifications & BIT_N(0)) && (device.trackpos_ms != t))
887 {
888 IAP_TX_INIT(0x03, 0x09);
889 IAP_TX_PUT(0x00);
890 IAP_TX_PUT_U32(t);
891 device.changed_notifications |= BIT_N(0);
892 changed = true;
893
894 iap_send_tx();
895 }
896
897 if ((device.notifications & BIT_N(15)) && (device.trackpos_s != ts)) {
898 IAP_TX_INIT(0x03, 0x09);
899 IAP_TX_PUT(0x0F);
900 IAP_TX_PUT_U16(ts);
901 device.changed_notifications |= BIT_N(15);
902 changed = true;
903
904 iap_send_tx();
905 }
906
907 if (changed)
908 {
909 device.trackpos_ms = t;
910 device.trackpos_s = ts;
911 }
912 }
913
914 /* Track index */
915 if (device.notifications & BIT_N(1))
916 {
917 uint32_t index;
918
919 index = iap_get_trackindex();
920
921 if (device.track_index != index) {
922 IAP_TX_INIT(0x03, 0x09);
923 IAP_TX_PUT(0x01);
924 IAP_TX_PUT_U32(index);
925 device.changed_notifications |= BIT_N(1);
926
927 iap_send_tx();
928
929 device.track_index = index;
930 }
931 }
932
933 /* Chapter index */
934 if (device.notifications & BIT_N(2))
935 {
936 uint32_t index;
937
938 index = iap_get_trackindex();
939
940 if (device.track_index != index)
941 {
942 IAP_TX_INIT(0x03, 0x09);
943 IAP_TX_PUT(0x02);
944 IAP_TX_PUT_U32(index);
945 IAP_TX_PUT_U16(0);
946 IAP_TX_PUT_U16(0xFFFF);
947 device.changed_notifications |= BIT_N(2);
948
949 iap_send_tx();
950
951 device.track_index = index;
952 }
953 }
954
955 /* Play status */
956 if (device.notifications & BIT_N(3))
957 {
958 unsigned char play_status;
959
960 play_status = audio_status();
961
962 if (device.play_status != play_status)
963 {
964 IAP_TX_INIT(0x03, 0x09);
965 IAP_TX_PUT(0x03);
966 if (play_status & AUDIO_STATUS_PLAY) {
967 /* Playing or paused */
968 if (play_status & AUDIO_STATUS_PAUSE) {
969 /* Paused */
970 IAP_TX_PUT(0x02);
971 } else {
972 /* Playing */
973 IAP_TX_PUT(0x01);
974 }
975 } else {
976 IAP_TX_PUT(0x00);
977 }
978 device.changed_notifications |= BIT_N(3);
979
980 iap_send_tx();
981
982 device.play_status = play_status;
983 }
984 }
985
986 /* Power/Battery */
987 if (device.notifications & BIT_N(5))
988 {
989 unsigned char power_state;
990 unsigned char battery_l;
991
992 power_state = charger_input_state;
993 battery_l = battery_level();
994
995 if ((device.power_state != power_state) || (device.battery_level != battery_l))
996 {
997 IAP_TX_INIT(0x03, 0x09);
998 IAP_TX_PUT(0x05);
999
1000 iap_fill_power_state();
1001 device.changed_notifications |= BIT_N(5);
1002
1003 iap_send_tx();
1004
1005 device.power_state = power_state;
1006 device.battery_level = battery_l;
1007 }
1008 }
1009
1010 /* Equalizer state
1011 * This is not handled yet.
1012 *
1013 * TODO: Fix equalizer handling
1014 */
1015
1016 /* Shuffle */
1017 if (device.notifications & BIT_N(7))
1018 {
1019 unsigned char shuffle;
1020
1021 shuffle = global_settings.playlist_shuffle;
1022
1023 if (device.shuffle != shuffle)
1024 {
1025 IAP_TX_INIT(0x03, 0x09);
1026 IAP_TX_PUT(0x07);
1027 IAP_TX_PUT(shuffle?0x01:0x00);
1028 device.changed_notifications |= BIT_N(7);
1029
1030 iap_send_tx();
1031
1032 device.shuffle = shuffle;
1033 }
1034 }
1035
1036 /* Repeat */
1037 if (device.notifications & BIT_N(8))
1038 {
1039 unsigned char repeat;
1040
1041 repeat = global_settings.repeat_mode;
1042
1043 if (device.repeat != repeat)
1044 {
1045 IAP_TX_INIT(0x03, 0x09);
1046 IAP_TX_PUT(0x08);
1047 switch (repeat)
1048 {
1049 case REPEAT_OFF:
1050 {
1051 IAP_TX_PUT(0x00);
1052 break;
1053 }
1054
1055 case REPEAT_ONE:
1056 {
1057 IAP_TX_PUT(0x01);
1058 break;
1059 }
1060
1061 case REPEAT_ALL:
1062 {
1063 IAP_TX_PUT(0x02);
1064 break;
1065 }
1066 }
1067 device.changed_notifications |= BIT_N(8);
1068
1069 iap_send_tx();
1070
1071 device.repeat = repeat;
1072 }
1073 }
1074
1075 /* Date/Time */
1076 if (device.notifications & BIT_N(9))
1077 {
1078 struct tm* tm;
1079
1080 tm = get_time();
1081
1082 if (memcmp(tm, &(device.datetime), sizeof(struct tm)))
1083 {
1084 IAP_TX_INIT(0x03, 0x09);
1085 IAP_TX_PUT(0x09);
1086 IAP_TX_PUT_U16(tm->tm_year);
1087
1088 /* Month */
1089 IAP_TX_PUT(tm->tm_mon+1);
1090
1091 /* Day */
1092 IAP_TX_PUT(tm->tm_mday);
1093
1094 /* Hour */
1095 IAP_TX_PUT(tm->tm_hour);
1096
1097 /* Minute */
1098 IAP_TX_PUT(tm->tm_min);
1099
1100 device.changed_notifications |= BIT_N(9);
1101
1102 iap_send_tx();
1103
1104 memcpy(&(device.datetime), tm, sizeof(struct tm));
1105 }
1106 }
1107
1108 /* Alarm
1109 * This is not supported yet.
1110 *
1111 * TODO: Fix alarm handling
1112 */
1113
1114 /* Backlight
1115 * This is not supported yet.
1116 *
1117 * TODO: Fix backlight handling
1118 */
1119
1120 /* Hold switch */
1121 if (device.notifications & BIT_N(0x0C))
1122 {
1123 unsigned char hold;
1124
1125 hold = button_hold();
1126 if (device.hold != hold) {
1127 IAP_TX_INIT(0x03, 0x09);
1128 IAP_TX_PUT(0x0C);
1129 IAP_TX_PUT(hold?0x01:0x00);
1130
1131 device.changed_notifications |= BIT_N(0x0C);
1132
1133 iap_send_tx();
1134
1135 device.hold = hold;
1136 }
1137 }
1138
1139 /* Sound check
1140 * This is not supported yet.
1141 *
1142 * TODO: Fix sound check handling
1143 */
1144
1145 /* Audiobook check
1146 * This is not supported yet.
1147 *
1148 * TODO: Fix audiobook handling
1149 */
1150}
1151
1152/* Change the current interface state.
1153 * On a change from IST_EXTENDED to IST_STANDARD, or from IST_STANDARD
1154 * to IST_EXTENDED, pause playback, if playing
1155 */
1156void iap_interface_state_change(const enum interface_state new)
1157{
1158 if (((interface_state == IST_EXTENDED) && (new == IST_STANDARD)) ||
1159 ((interface_state == IST_STANDARD) && (new == IST_EXTENDED))) {
1160 if (audio_status() == AUDIO_STATUS_PLAY)
1161 {
1162 REMOTE_BUTTON(BUTTON_RC_PLAY);
1163 }
1164 }
1165
1166 interface_state = new;
1167}
1168
1169static void iap_handlepkt_mode5(const unsigned int len, const unsigned char *buf)
1170{
1171 (void) len;
1172 unsigned int cmd = buf[1];
1173 switch (cmd)
1174 {
1175 /* Sent from iPod Begin Transmission */
1176 case 0x02:
1177 {
1178 /* RF Transmitter: Begin High Power transmission */
1179 unsigned char data0[] = {0x05, 0x02};
1180 iap_send_pkt(data0, sizeof(data0));
1181 break;
1182 }
1183
1184 /* Sent from iPod End High Power Transmission */
1185 case 0x03:
1186 {
1187 /* RF Transmitter: End High Power transmission */
1188 unsigned char data1[] = {0x05, 0x03};
1189 iap_send_pkt(data1, sizeof(data1));
1190 break;
1191 }
1192 /* Return Version Number ?*/
1193 case 0x04:
1194 {
1195 /* do nothing */
1196 break;
1197 }
1198 }
1199}
1200
1201#if 0
1202static void iap_handlepkt_mode7(const unsigned int len, const unsigned char *buf)
1203{
1204 unsigned int cmd = buf[1];
1205 switch (cmd)
1206 {
1207 /* RetTunerCaps */
1208 case 0x02:
1209 {
1210 /* do nothing */
1211
1212 /* GetAccessoryInfo */
1213 unsigned char data[] = {0x00, 0x27, 0x00};
1214 iap_send_pkt(data, sizeof(data));
1215 break;
1216 }
1217
1218 /* RetTunerFreq */
1219 case 0x0A:
1220 /* fall through */
1221 /* TunerSeekDone */
1222 case 0x13:
1223 {
1224 rmt_tuner_freq(len, buf);
1225 break;
1226 }
1227
1228 /* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
1229 case 0x21:
1230 {
1231 rmt_tuner_rds_data(len, buf);
1232 break;
1233 }
1234 }
1235}
1236#endif
1237
1238void iap_handlepkt(void)
1239{
1240 int level;
1241 int length;
1242
1243 if(!iap_setupflag) return;
1244
1245 /* if we are waiting for a remote button to go out,
1246 delay the handling of the new packet */
1247 if(iap_repeatbtn)
1248 {
1249 queue_post(&iap_queue, IAP_EV_MSG_RCVD, 0);
1250 sleep(1);
1251 return;
1252 }
1253
1254 /* handle command by mode */
1255 length = get_u16(iap_rxstart);
1256#ifdef LOGF_ENABLE
1257 logf("R: %s", hexstring(iap_rxstart+2, (length)));
1258#endif
1259
1260 unsigned char mode = *(iap_rxstart+2);
1261 switch (mode) {
1262 case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break;
1263 case 2: iap_handlepkt_mode2(length, iap_rxstart+2); break;
1264 case 3: iap_handlepkt_mode3(length, iap_rxstart+2); break;
1265 case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break;
1266 case 5: iap_handlepkt_mode5(length, iap_rxstart+2); break;
1267 /* case 7: iap_handlepkt_mode7(length, iap_rxstart+2); break; */
1268 }
1269
1270 /* Remove the handled packet from the RX buffer
1271 * This needs to be done with interrupts disabled, to make
1272 * sure the buffer and the pointers into it are handled
1273 * cleanly
1274 */
1275 level = disable_irq_save();
1276 memmove(iap_rxstart, iap_rxstart+(length+2), (RX_BUFLEN+2)-(length+2));
1277 iap_rxnext -= (length+2);
1278 iap_rxpayload -= (length+2);
1279 iap_rxlen += (length+2);
1280 restore_irq(level);
1281
1282 /* poke the poweroff timer */
1283 reset_poweroff_timer();
1284}
1285
1286int remote_control_rx(void)
1287{
1288 int btn = iap_remotebtn;
1289 if(iap_repeatbtn)
1290 iap_repeatbtn--;
1291
1292 return btn;
1293}
1294
1295const unsigned char *iap_get_serbuf(void)
1296{
1297 return iap_rxstart;
1298}
1299
1300#ifdef IAP_MALLOC_DYNAMIC
1301static int iap_move_callback(int handle, void* current, void* new)
1302{
1303 (void) handle;
1304 (void) current;
1305
1306 iap_txstart = new;
1307 iap_txpayload = iap_txstart+5;
1308 iap_txnext = iap_txpayload;
1309 iap_rxstart = iap_buffers+(TX_BUFLEN+6);
1310
1311 return BUFLIB_CB_OK;
1312}
1313#endif
1314
1315/* Change the shuffle state */
1316void iap_shuffle_state(const bool state)
1317{
1318 /* Set shuffle to enabled */
1319 if(state && !global_settings.playlist_shuffle)
1320 {
1321 global_settings.playlist_shuffle = 1;
1322 settings_save();
1323 if (audio_status() & AUDIO_STATUS_PLAY)
1324 playlist_randomise(NULL, current_tick, true);
1325 }
1326 /* Set shuffle to disabled */
1327 else if(!state && global_settings.playlist_shuffle)
1328 {
1329 global_settings.playlist_shuffle = 0;
1330 settings_save();
1331 if (audio_status() & AUDIO_STATUS_PLAY)
1332 playlist_sort(NULL, true);
1333 }
1334}
1335
1336/* Change the repeat state */
1337void iap_repeat_state(const unsigned char state)
1338{
1339 if (state != global_settings.repeat_mode)
1340 {
1341 global_settings.repeat_mode = state;
1342 settings_save();
1343 if (audio_status() & AUDIO_STATUS_PLAY)
1344 audio_flush_and_reload_tracks();
1345 }
1346}
1347
1348void iap_repeat_next(void)
1349{
1350 switch (global_settings.repeat_mode)
1351 {
1352 case REPEAT_OFF:
1353 {
1354 iap_repeat_state(REPEAT_ALL);
1355 break;
1356 }
1357 case REPEAT_ALL:
1358 {
1359 iap_repeat_state(REPEAT_ONE);
1360 break;
1361 }
1362 case REPEAT_ONE:
1363 {
1364 iap_repeat_state(REPEAT_OFF);
1365 break;
1366 }
1367 }
1368}
1369
1370/* This function puts the current power/battery state
1371 * into the TX buffer. The buffer is assumed to be initialized
1372 */
1373void iap_fill_power_state(void)
1374{
1375 unsigned char power_state;
1376 unsigned char battery_l;
1377
1378 power_state = charger_input_state;
1379 battery_l = battery_level();
1380
1381 if (power_state == NO_CHARGER) {
1382 if (battery_l < 30) {
1383 IAP_TX_PUT(0x00);
1384 } else {
1385 IAP_TX_PUT(0x01);
1386 }
1387 IAP_TX_PUT((char)((battery_l * 255)/100));
1388 } else {
1389 IAP_TX_PUT(0x04);
1390 IAP_TX_PUT(0x00);
1391 }
1392}
diff --git a/apps/iap/iap-core.h b/apps/iap/iap-core.h
new file mode 100644
index 0000000000..d06e3c300c
--- /dev/null
+++ b/apps/iap/iap-core.h
@@ -0,0 +1,250 @@
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#ifndef _IAP_CORE_H
20#define _IAP_CORE_H
21
22#include <stdint.h>
23#include <string.h>
24#include "timefuncs.h"
25#include "metadata.h"
26#include "playlist.h"
27#include "iap.h"
28
29#define LOGF_ENABLE
30/* #undef LOGF_ENABLE */
31#ifdef LOGF_ENABLE
32 #include "logf.h"
33#endif
34
35/* The Model ID of the iPod we emulate. Currently a 160GB classic */
36#define IAP_IPOD_MODEL (0x00130200U)
37
38/* The firmware version we emulate. Currently 2.0.3 */
39#define IAP_IPOD_FIRMWARE_MAJOR (2)
40#define IAP_IPOD_FIRMWARE_MINOR (0)
41#define IAP_IPOD_FIRMWARE_REV (3)
42
43/* Status code for IAP ack messages */
44#define IAP_ACK_OK (0x00) /* Success */
45#define IAP_ACK_UNKNOWN_DB (0x01) /* Unknown Database Category */
46#define IAP_ACK_CMD_FAILED (0x02) /* Command failed */
47#define IAP_ACK_NO_RESOURCE (0x03) /* Out of resources */
48#define IAP_ACK_BAD_PARAM (0x04) /* Bad parameter */
49#define IAP_ACK_UNKNOWN_ID (0x05) /* Unknown ID */
50#define IAP_ACK_PENDING (0x06) /* Command pending */
51#define IAP_ACK_NO_AUTHEN (0x07) /* Not authenticated */
52#define IAP_ACK_BAD_AUTHEN (0x08) /* Bad authentication version */
53/* 0x09 reserved */
54#define IAP_ACK_CERT_INVAL (0x0A) /* Certificate invalid */
55#define IAP_ACK_CERT_PERM (0x0B) /* Certificate permissions invalid */
56/* 0x0C-0x10 reserved */
57#define IAP_ACK_RES_INVAL (0x11) /* Invalid accessory resistor value */
58
59/* Add a button to the remote button bitfield. Also set iap_repeatbtn=1
60 * to ensure a button press is at least delivered once.
61 */
62#define REMOTE_BUTTON(x) do { \
63 iap_remotebtn |= (x); \
64 iap_timeoutbtn = 3; \
65 iap_repeatbtn = 2; \
66 } while(0)
67
68/* States of the extended command support */
69enum interface_state {
70 IST_STANDARD, /* General state, support lingo 0x00 commands */
71 IST_EXTENDED, /* Extended Interface lingo (0x04) negotiated */
72};
73
74/* States of the authentication state machine */
75enum authen_state {
76 AUST_NONE, /* Initial state, no message sent */
77 AUST_INIT, /* Remote side has requested authentication */
78 AUST_CERTREQ, /* Remote certificate requested */
79 AUST_CERTBEG, /* Certificate is being received */
80 AUST_CERTDONE, /* Certificate received */
81 AUST_CHASENT, /* Challenge sent */
82 AUST_CHADONE, /* Challenge response received */
83 AUST_AUTH, /* Authentication complete */
84};
85
86/* State of authentication */
87struct auth_t {
88 enum authen_state state; /* Current state of authentication */
89 unsigned char max_section; /* The maximum number of certificate sections */
90 unsigned char next_section; /* The next expected section number */
91};
92
93/* State of GetAccessoryInfo */
94enum accinfo_state {
95 ACCST_NONE, /* Initial state, no message sent */
96 ACCST_INIT, /* Send out initial GetAccessoryInfo */
97 ACCST_SENT, /* Wait for initial RetAccessoryInfo */
98 ACCST_DATA, /* Query device information, according to capabilities */
99};
100
101/* A struct describing an attached device and it's current
102 * state
103 */
104struct device_t {
105 struct auth_t auth; /* Authentication state */
106 enum accinfo_state accinfo; /* Accessory information state */
107 uint32_t lingoes; /* Negotiated lingoes */
108 uint32_t notifications; /* Requested notifications. These are just the
109 * notifications explicitly requested by the
110 * device
111 */
112 uint32_t changed_notifications; /* Tracks notifications that changed since the last
113 * call to SetRemoteEventNotification or GetRemoteEventStatus
114 */
115 bool do_notify; /* Notifications enabled */
116 bool do_power_notify; /* Whether to send power change notifications.
117 * These are sent automatically to all devices
118 * that used IdentifyDeviceLingoes to identify
119 * themselves, independent of other notifications
120 */
121
122 uint32_t trackpos_ms; /* These fields are to save the current state */
123 uint32_t track_index; /* of various fields so we can send a notification */
124 uint32_t chapter_index; /* if they change */
125 unsigned char play_status;
126 bool mute;
127 unsigned char volume;
128 unsigned char power_state;
129 unsigned char battery_level;
130 uint32_t equalizer_index;
131 unsigned char shuffle;
132 unsigned char repeat;
133 struct tm datetime;
134 unsigned char alarm_state;
135 unsigned char alarm_hour;
136 unsigned char alarm_minute;
137 unsigned char backlight;
138 bool hold;
139 unsigned char soundcheck;
140 unsigned char audiobook;
141 uint16_t trackpos_s;
142 uint32_t capabilities; /* Capabilities of the device, as returned by type 0
143 * of GetAccessoryInfo
144 */
145 uint32_t capabilities_queried; /* Capabilities already queried */
146};
147
148extern struct device_t device;
149#define DEVICE_AUTHENTICATED (device.auth.state == AUST_AUTH)
150#define DEVICE_AUTH_RUNNING ((device.auth.state != AUST_NONE) && (device.auth.state != AUST_AUTH))
151#define DEVICE_LINGO_SUPPORTED(x) (device.lingoes & BIT_N((x)&0x1f))
152
153extern unsigned long iap_remotebtn;
154extern unsigned int iap_timeoutbtn;
155extern int iap_repeatbtn;
156
157extern unsigned char* iap_txpayload;
158extern unsigned char* iap_txnext;
159
160/* These are a number of helper macros to manage the dynamic TX buffer content
161 * These macros DO NOT CHECK for buffer overflow. iap_send_tx() will, but
162 * it might be too late at that point. See the current size of TX_BUFLEN
163 */
164
165/* Initialize the TX buffer with a lingo and command ID. This will reset the
166 * data pointer, effectively invalidating unsent information in the TX buffer.
167 * There are two versions of this, one for 1 byte command IDs (all Lingoes except
168 * 0x04) and one for two byte command IDs (Lingo 0x04)
169 */
170#define IAP_TX_INIT(lingo, command) do { \
171 iap_txnext = iap_txpayload; \
172 IAP_TX_PUT((lingo)); \
173 IAP_TX_PUT((command)); \
174 } while (0)
175
176#define IAP_TX_INIT4(lingo, command) do { \
177 iap_txnext = iap_txpayload; \
178 IAP_TX_PUT((lingo)); \
179 IAP_TX_PUT_U16((command)); \
180 } while (0)
181
182/* Put an unsigned char into the TX buffer */
183#define IAP_TX_PUT(data) *(iap_txnext++) = (data)
184
185/* Put a 16bit unsigned quantity into the TX buffer */
186#define IAP_TX_PUT_U16(data) do { \
187 put_u16(iap_txnext, (data)); \
188 iap_txnext += 2; \
189 } while (0)
190
191/* Put a 32bit unsigned quantity into the TX buffer */
192#define IAP_TX_PUT_U32(data) do { \
193 put_u32(iap_txnext, (data)); \
194 iap_txnext += 4; \
195 } while (0)
196
197/* Put an arbitrary amount of data (identified by a char pointer and
198 * a length) into the TX buffer
199 */
200#define IAP_TX_PUT_DATA(data, len) do { \
201 memcpy(iap_txnext, (unsigned char *)(data), (len)); \
202 iap_txnext += (len); \
203 } while(0)
204
205/* Put a NULL terminated string into the TX buffer, including the
206 * NULL byte
207 */
208#define IAP_TX_PUT_STRING(str) IAP_TX_PUT_DATA((str), strlen((str))+1)
209
210/* Put a NULL terminated string into the TX buffer, taking care not to
211 * overflow the buffer. If the string does not fit into the TX buffer
212 * it will be truncated, but always NULL terminated.
213 *
214 * This function is expensive compared to the other IAP_TX_PUT_*
215 * functions
216 */
217#define IAP_TX_PUT_STRLCPY(str) iap_tx_strlcpy(str)
218
219extern unsigned char lingo_versions[32][2];
220#define LINGO_SUPPORTED(x) (LINGO_MAJOR((x)&0x1f) > 0)
221#define LINGO_MAJOR(x) (lingo_versions[(x)&0x1f][0])
222#define LINGO_MINOR(x) (lingo_versions[(x)&0x1f][1])
223
224void put_u16(unsigned char *buf, const uint16_t data);
225void put_u32(unsigned char *buf, const uint32_t data);
226uint32_t get_u32(const unsigned char *buf);
227uint16_t get_u16(const unsigned char *buf);
228void iap_tx_strlcpy(const unsigned char *str);
229
230void iap_reset_auth(struct auth_t* auth);
231void iap_reset_device(struct device_t* device);
232
233void iap_shuffle_state(bool state);
234void iap_repeat_state(unsigned char state);
235void iap_repeat_next(void);
236void iap_fill_power_state(void);
237
238void iap_send_tx(void);
239
240extern enum interface_state interface_state;
241void iap_interface_state_change(const enum interface_state new);
242
243extern bool iap_btnrepeat;
244extern bool iap_btnshuffle;
245
246uint32_t iap_get_trackpos(void);
247uint32_t iap_get_trackindex(void);
248void iap_get_trackinfo(const unsigned int track, struct mp3entry* id3);
249
250#endif
diff --git a/apps/iap/iap-lingo.h b/apps/iap/iap-lingo.h
new file mode 100644
index 0000000000..0c0a9e633d
--- /dev/null
+++ b/apps/iap/iap-lingo.h
@@ -0,0 +1,23 @@
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
20void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf);
21void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf);
22void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf);
23void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf);
diff --git a/apps/iap/iap-lingo0.c b/apps/iap/iap-lingo0.c
new file mode 100644
index 0000000000..9e0355cb3f
--- /dev/null
+++ b/apps/iap/iap-lingo0.c
@@ -0,0 +1,1035 @@
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#include "iap-core.h"
21#include "iap-lingo.h"
22#include "kernel.h"
23#include "system.h"
24
25/*
26 * This macro is meant to be used inside an IAP mode message handler.
27 * It is passed the expected minimum length of the message buffer.
28 * If the buffer does not have the required lenght an ACK
29 * packet with a Bad Parameter error is generated.
30 */
31#define CHECKLEN(x) do { \
32 if (len < (x)) { \
33 cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
34 return; \
35 }} while(0)
36
37/* Check for authenticated state, and return an ACK Not
38 * Authenticated on failure.
39 */
40#define CHECKAUTH do { \
41 if (!DEVICE_AUTHENTICATED) { \
42 cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \
43 return; \
44 }} while(0)
45
46static void cmd_ack(const unsigned char cmd, const unsigned char status)
47{
48 IAP_TX_INIT(0x00, 0x02);
49 IAP_TX_PUT(status);
50 IAP_TX_PUT(cmd);
51 iap_send_tx();
52}
53
54#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
55
56static void cmd_pending(const unsigned char cmd, const uint32_t msdelay)
57{
58 IAP_TX_INIT(0x00, 0x02);
59 IAP_TX_PUT(0x06);
60 IAP_TX_PUT(cmd);
61 IAP_TX_PUT_U32(msdelay);
62 iap_send_tx();
63}
64
65void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
66{
67 unsigned int cmd = buf[1];
68
69 /* We expect at least two bytes in the buffer, one for the
70 * lingo, one for the command
71 */
72 CHECKLEN(2);
73
74 switch (cmd) {
75 /* RequestIdentify (0x00)
76 *
77 * Sent from the iPod to the device
78 */
79
80 /* Identify (0x01)
81 * This command is deprecated.
82 *
83 * It is used by a device to inform the iPod of the devices
84 * presence and of the lingo the device supports.
85 *
86 * Also, it is used to negotiate power for RF transmitters
87 *
88 * Packet format (offset in buf[]: Description)
89 * 0x00: Lingo ID: General Lingo, always 0x00
90 * 0x01: Command, always 0x01
91 * 0x02: Lingo supported by the device
92 *
93 * Some RF transmitters use an extended version of this
94 * command:
95 *
96 * 0x00: Lingo ID: General Lingo, always 0x00
97 * 0x01: Command, always 0x01
98 * 0x02: Lingo supported by the device, always 0x05 (RF Transmitter)
99 * 0x03: Reserved, always 0x00
100 * 0x04: Number of valid bits in the following fields
101 * 0x05-N: Datafields holding the number of bits specified in 0x04
102 *
103 * Returns: (none)
104 *
105 * TODO:
106 * BeginHighPower/EndHighPower should be send in the periodic handler,
107 * depending on the current play status
108 */
109 case 0x01:
110 {
111 unsigned char lingo = buf[2];
112
113 /* This is sufficient even for Lingo 0x05, as we are
114 * not actually reading from the extended bits for now
115 */
116 CHECKLEN(3);
117
118 /* Issuing this command exits any extended interface states
119 * and resets authentication
120 */
121 iap_interface_state_change(IST_STANDARD);
122 iap_reset_device(&device);
123
124 switch (lingo) {
125 case 0x04:
126 {
127 /* A single lingo device negotiating the
128 * extended interface lingo. This causes an interface
129 * state change.
130 */
131 iap_interface_state_change(IST_EXTENDED);
132 break;
133 }
134
135 case 0x05:
136 {
137 /* FM transmitter sends this: */
138 /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
139 sleep(HZ/3);
140 /* RF Transmitter: Begin transmission */
141 IAP_TX_INIT(0x05, 0x02);
142
143 iap_send_tx();
144 break;
145 }
146 }
147
148 if (lingo < 32) {
149 /* All devices that Identify get access to Lingoes 0x00 and 0x02 */
150 device.lingoes = BIT_N(0x00) | BIT_N(0x02);
151
152 device.lingoes |= BIT_N(lingo);
153
154 /* Devices that Identify with Lingo 0x04 also gain access
155 * to Lingo 0x03
156 */
157 if (lingo == 0x04)
158 device.lingoes |= BIT_N(0x03);
159 } else {
160 device.lingoes = 0;
161 }
162 break;
163 }
164
165 /* ACK (0x02)
166 *
167 * Sent from the iPod to the device
168 */
169
170 /* RequestRemoteUIMode (0x03)
171 *
172 * Request the current Extended Interface Mode state
173 * This command may be used only if the accessory requests Lingo 0x04
174 * during its identification process.
175 *
176 * Packet format (offset in buf[]: Description)
177 * 0x00: Lingo ID: General Lingo, always 0x00
178 * 0x01: Command, always 0x03
179 *
180 * Returns on success:
181 * ReturnRemoteUIMode
182 *
183 * Packet format (offset in data[]: Description)
184 * 0x00: Lingo ID: General Lingo, always 0x00
185 * 0x01: Command, always 0x04
186 * 0x02: Current Extended Interface Mode (zero: false, non-zero: true)
187 *
188 * Returns on failure:
189 * IAP_ACK_BAD_PARAM
190 */
191 case 0x03:
192 {
193 if (!DEVICE_LINGO_SUPPORTED(0x04)) {
194 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
195 break;
196 }
197
198 IAP_TX_INIT(0x00, 0x04);
199 if (interface_state == IST_EXTENDED)
200 IAP_TX_PUT(0x01);
201 else
202 IAP_TX_PUT(0x00);
203
204 iap_send_tx();
205 break;
206 }
207
208 /* ReturnRemoteUIMode (0x04)
209 *
210 * Sent from the iPod to the device
211 */
212
213 /* EnterRemoteUIMode (0x05)
214 *
215 * Request Extended Interface Mode
216 * This command may be used only if the accessory requests Lingo 0x04
217 * during its identification process.
218 *
219 * Packet format (offset in buf[]: Description)
220 * 0x00: Lingo ID: General Lingo, always 0x00
221 * 0x01: Command, always 0x05
222 *
223 * Returns on success:
224 * IAP_ACK_PENDING
225 * IAP_ACK_OK
226 *
227 * Returns on failure:
228 * IAP_ACK_BAD_PARAM
229 */
230 case 0x05:
231 {
232 if (!DEVICE_LINGO_SUPPORTED(0x04)) {
233 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
234 break;
235 }
236
237 cmd_pending(cmd, 1000);
238 iap_interface_state_change(IST_EXTENDED);
239 cmd_ok(cmd);
240 break;
241 }
242
243 /* ExitRemoteUIMode (0x06)
244 *
245 * Leave Extended Interface Mode
246 * This command may be used only if the accessory requests Lingo 0x04
247 * during its identification process.
248 *
249 * Packet format (offset in buf[]: Description)
250 * 0x00: Lingo ID: General Lingo, always 0x00
251 * 0x01: Command, always 0x06
252 *
253 * Returns on success:
254 * IAP_ACK_PENDING
255 * IAP_ACK_OK
256 *
257 * Returns on failure:
258 * IAP_ACK_BAD_PARAM
259 */
260 case 0x06:
261 {
262 if (!DEVICE_LINGO_SUPPORTED(0x04)) {
263 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
264 break;
265 }
266
267 cmd_pending(cmd, 1000);
268 iap_interface_state_change(IST_STANDARD);
269 cmd_ok(cmd);
270 break;
271 }
272
273 /* RequestiPodName (0x07)
274 *
275 * Retrieves the name of the iPod
276 *
277 * Packet format (offset in buf[]: Description)
278 * 0x00: Lingo ID: General Lingo, always 0x00
279 * 0x01: Command, always 0x07
280 *
281 * Returns:
282 * ReturniPodName
283 *
284 * Packet format (offset in data[]: Description)
285 * 0x00: Lingo ID: General Lingo, always 0x00
286 * 0x01: Command, always 0x08
287 * 0x02-0xNN: iPod name as NULL-terminated UTF8 string
288 */
289 case 0x07:
290 {
291 IAP_TX_INIT(0x00, 0x08);
292 IAP_TX_PUT_STRING("ROCKBOX");
293
294 iap_send_tx();
295 break;
296 }
297
298 /* ReturniPodName (0x08)
299 *
300 * Sent from the iPod to the device
301 */
302
303 /* RequestiPodSoftwareVersion (0x09)
304 *
305 * Returns the major, minor and revision numbers of the iPod
306 * software version. This not any Lingo protocol version.
307 *
308 * Packet format (offset in buf[]: Description)
309 * 0x00: Lingo ID: General Lingo, always 0x00
310 * 0x01: Command, always 0x09
311 *
312 * Returns:
313 * ReturniPodSoftwareVersion
314 *
315 * Packet format (offset in data[]: Description)
316 * 0x00: Lingo ID: General Lingo, always 0x00
317 * 0x01: Command, always 0x0A
318 * 0x02: iPod major software version
319 * 0x03: iPod minor software version
320 * 0x04: iPod revision software version
321 */
322 case 0x09:
323 {
324 IAP_TX_INIT(0x00, 0x0A);
325 IAP_TX_PUT(IAP_IPOD_FIRMWARE_MAJOR);
326 IAP_TX_PUT(IAP_IPOD_FIRMWARE_MINOR);
327 IAP_TX_PUT(IAP_IPOD_FIRMWARE_REV);
328
329 iap_send_tx();
330 break;
331 }
332
333 /* ReturniPodSoftwareVersion (0x0A)
334 *
335 * Sent from the iPod to the device
336 */
337
338 /* RequestiPodSerialNum (0x0B)
339 *
340 * Returns the iPod serial number
341 *
342 * Packet format (offset in buf[]: Description)
343 * 0x00: Lingo ID: General Lingo, always 0x00
344 * 0x01: Command, always 0x0B
345 *
346 * Returns:
347 * ReturniPodSerialNumber
348 *
349 * Packet format (offset in data[]: Description)
350 * 0x00: Lingo ID: General Lingo, always 0x00
351 * 0x01: Command, always 0x0C
352 * 0x02-0xNN: Serial number as NULL-terminated UTF8 string
353 */
354 case 0x0B:
355 {
356 IAP_TX_INIT(0x00, 0x0C);
357 IAP_TX_PUT_STRING("0123456789");
358
359 iap_send_tx();
360 break;
361 }
362
363 /* ReturniPodSerialNum (0x0C)
364 *
365 * Sent from the iPod to the device
366 */
367
368 /* RequestiPodModelNum (0x0D)
369 *
370 * Returns the model number as a 32bit unsigned integer and
371 * as a string.
372 *
373 * Packet format (offset in buf[]: Description)
374 * 0x00: Lingo ID: General Lingo, always 0x00
375 * 0x01: Command, always 0x0D
376 *
377 * Returns:
378 * ReturniPodModelNum
379 *
380 * Packet format (offset in data[]: Description)
381 * 0x00: Lingo ID: General Lingo, always 0x00
382 * 0x01: Command, always 0x0E
383 * 0x02-0x05: Model number as 32bit integer
384 * 0x06-0xNN: Model number as NULL-terminated UTF8 string
385 */
386 case 0x0D:
387 {
388 IAP_TX_INIT(0x00, 0x0E);
389 IAP_TX_PUT_U32(IAP_IPOD_MODEL);
390 IAP_TX_PUT_STRING("ROCKBOX");
391
392 iap_send_tx();
393 break;
394 }
395
396 /* ReturniPodSerialNum (0x0E)
397 *
398 * Sent from the iPod to the device
399 */
400
401 /* RequestLingoProtocolVersion (0x0F)
402 *
403 * Packet format (offset in buf[]: Description)
404 * 0x00: Lingo ID: General Lingo, always 0x00
405 * 0x01: Command, always 0x0F
406 * 0x02: Lingo for which to request version information
407 *
408 * Returns on success:
409 * ReturnLingoProtocolVersion
410 *
411 * Packet format (offset in data[]: Description)
412 * 0x00: Lingo ID: General Lingo, always 0x00
413 * 0x01: Command, always 0x10
414 * 0x02: Lingo for which version information is returned
415 * 0x03: Major protocol version for the given lingo
416 * 0x04: Minor protocol version for the given lingo
417 *
418 * Returns on failure:
419 * IAP_ACK_BAD_PARAM
420 */
421 case 0x0F:
422 {
423 unsigned char lingo = buf[2];
424
425 CHECKLEN(3);
426
427 /* Supported lingos and versions are read from the lingo_versions
428 * array
429 */
430 if (LINGO_SUPPORTED(lingo)) {
431 IAP_TX_INIT(0x00, 0x10);
432 IAP_TX_PUT(lingo);
433 IAP_TX_PUT(LINGO_MAJOR(lingo));
434 IAP_TX_PUT(LINGO_MINOR(lingo));
435
436 iap_send_tx();
437 } else {
438 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
439 }
440 break;
441 }
442
443 /* ReturnLingoProtocolVersion (0x10)
444 *
445 * Sent from the iPod to the device
446 */
447
448 /* IdentifyDeviceLingoes (0x13);
449 *
450 * Used by a device to inform the iPod of the devices
451 * presence and of the lingoes the device supports.
452 *
453 * Packet format (offset in buf[]: Description)
454 * 0x00: Lingo ID: General Lingo, always 0x00
455 * 0x01: Command, always 0x13
456 * 0x02-0x05: Device lingoes spoken
457 * 0x06-0x09: Device options
458 * 0x0A-0x0D: Device ID. Only important for authentication
459 *
460 * Returns on success:
461 * IAP_ACK_OK
462 *
463 * Returns on failure:
464 * IAP_ACK_CMD_FAILED
465 */
466 case 0x13:
467 {
468 uint32_t lingoes = get_u32(&buf[2]);
469 uint32_t options = get_u32(&buf[6]);
470 uint32_t deviceid = get_u32(&buf[0x0A]);
471 bool seen_unsupported = false;
472 unsigned char i;
473
474 CHECKLEN(14);
475
476 /* Issuing this command exits any extended interface states */
477 iap_interface_state_change(IST_STANDARD);
478
479 /* Loop through the lingoes advertised by the device.
480 * If it tries to use a lingo we do not support, return
481 * a Command Failed ACK.
482 */
483 for(i=0; i<32; i++) {
484 if (lingoes & BIT_N(i)) {
485 /* Bit set by device */
486 if (!LINGO_SUPPORTED(i)) {
487 seen_unsupported = true;
488 }
489 }
490 }
491
492 /* Bit 0 _must_ be set by the device */
493 if (!(lingoes & 1)) {
494 seen_unsupported = true;
495 }
496
497 /* Specifying a deviceid without requesting authentication is
498 * an error
499 */
500 if (deviceid && !(options & 0x03))
501 seen_unsupported = true;
502
503 /* Specifying authentication without a deviceid is an error */
504 if (!deviceid && (options & 0x03))
505 seen_unsupported = true;
506
507 device.lingoes = 0;
508 if (seen_unsupported) {
509 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
510 break;
511 }
512 iap_reset_device(&device);
513 device.lingoes = lingoes;
514
515 /* Devices using IdentifyDeviceLingoes get power off notifications */
516 device.do_power_notify = true;
517
518 /* If a new authentication is requested, start the auth
519 * process.
520 * The periodic handler will take care of sending out the
521 * GetDevAuthenticationInfo packet
522 *
523 * If no authentication is requested, schedule the start of
524 * GetAccessoryInfo
525 */
526 if (deviceid && (options & 0x03) && !DEVICE_AUTH_RUNNING) {
527 device.auth.state = AUST_INIT;
528 } else {
529 device.accinfo = ACCST_INIT;
530 }
531
532 cmd_ok(cmd);
533
534 /* Bit 5: RF Transmitter lingo */
535 if (lingoes & (1 << 5))
536 {
537 /* FM transmitter sends this: */
538 /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
539
540 /* GetAccessoryInfo */
541 unsigned char data2[] = {0x00, 0x27, 0x00};
542 iap_send_pkt(data2, sizeof(data2));
543 /* RF Transmitter: Begin transmission */
544 unsigned char data3[] = {0x05, 0x02};
545 iap_send_pkt(data3, sizeof(data3));
546 }
547
548
549#if 0
550 /* Bit 7: RF Tuner lingo */
551 if (lingoes & (1 << 7))
552 {
553 /* ipod fm remote sends this: */
554 /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
555 radio_present = 1;
556 /* GetDevAuthenticationInfo */
557 unsigned char data4[] = {0x00, 0x14};
558 iap_send_pkt(data4, sizeof(data4));
559 }
560#endif
561 break;
562 }
563
564 /* GetDevAuthenticationInfo (0x14)
565 *
566 * Sent from the iPod to the device
567 */
568
569 /* RetDevAuthenticationInfo (0x15)
570 *
571 * Send certificate information from the device to the iPod.
572 * The certificate may come in multiple parts and has
573 * to be reassembled.
574 *
575 * Packet format (offset in buf[]: Description)
576 * 0x00: Lingo ID: General Lingo, always 0x00
577 * 0x01: Command, always 0x15
578 * 0x02: Authentication major version
579 * 0x03: Authentication minor version
580 * 0x04: Certificate current section index
581 * 0x05: Certificate maximum section index
582 * 0x06-0xNN: Certificate data
583 *
584 * Returns on success:
585 * IAP_ACK_OK for intermediate sections
586 * AckDevAuthenticationInfo for the last section
587 *
588 * Returns on failure:
589 * IAP_ACK_BAD_PARAMETER
590 * AckDevAuthenticationInfo for version mismatches
591 *
592 */
593 case 0x15:
594 {
595 /* There are two formats of this packet. One with only
596 * the version information bytes (for Auth version 1.0)
597 * and the long form shown above
598 */
599 CHECKLEN(4);
600
601 if (!DEVICE_AUTH_RUNNING) {
602 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
603 break;
604 }
605
606 /* We only support version 2.0 */
607 if ((buf[2] != 2) || (buf[3] != 0)) {
608 /* Version mismatches are signalled by AckDevAuthenticationInfo
609 * with the status set to Authentication Information unsupported
610 */
611 iap_reset_auth(&(device.auth));
612
613 IAP_TX_INIT(0x00, 0x16);
614 IAP_TX_PUT(0x08);
615
616 iap_send_tx();
617 break;
618 }
619
620 /* There must be at least one byte of certificate data
621 * in the packet
622 */
623 CHECKLEN(7);
624
625 switch (device.auth.state)
626 {
627 /* This is the first packet. Note the maximum section number
628 * so we can check it later.
629 */
630 case AUST_CERTREQ:
631 {
632 device.auth.max_section = buf[5];
633 device.auth.state = AUST_CERTBEG;
634
635 /* Intentional fall-through */
636 }
637 /* All following packets */
638 case AUST_CERTBEG:
639 {
640 /* Check if this is the expected section */
641 if (buf[4] != device.auth.next_section) {
642 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
643 break;
644 }
645
646 /* Is this the last section? */
647 if (device.auth.next_section == device.auth.max_section) {
648 /* If we could really do authentication we'd have to
649 * check the certificate here. Since we can't, just acknowledge
650 * the packet with an "everything OK" AckDevAuthenticationInfo
651 *
652 * Also, start GetAccessoryInfo process
653 */
654 IAP_TX_INIT(0x00, 0x16);
655 IAP_TX_PUT(0x00);
656
657 iap_send_tx();
658 device.auth.state = AUST_CERTDONE;
659 device.accinfo = ACCST_INIT;
660 } else {
661 device.auth.next_section++;
662 cmd_ok(cmd);
663 }
664 break;
665 }
666
667 default:
668 {
669 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
670 break;
671 }
672 }
673
674 break;
675 }
676
677 /* AckDevAuthenticationInfo (0x16)
678 *
679 * Sent from the iPod to the device
680 */
681
682 /* GetDevAuthenticationSignature (0x17)
683 *
684 * Sent from the iPod to the device
685 */
686
687 /* RetDevAuthenticationSignature (0x18)
688 *
689 * Return a calculated signature based on the device certificate
690 * and the challenge sent with GetDevAuthenticationSignature
691 *
692 * Packet format (offset in buf[]: Description)
693 * 0x00: Lingo ID: General Lingo, always 0x00
694 * 0x01: Command, always 0x17
695 * 0x02-0xNN: Certificate data
696 *
697 * Returns on success:
698 * AckDevAuthenticationStatus
699 *
700 * Packet format (offset in data[]: Description)
701 * 0x00: Lingo ID: General Lingo, always 0x00
702 * 0x01: Command, always 0x19
703 * 0x02: Status (0x00: OK)
704 *
705 * Returns on failure:
706 * IAP_ACK_BAD_PARAM
707 *
708 * TODO:
709 * There is a timeout of 75 seconds between GetDevAuthenticationSignature
710 * and RetDevAuthenticationSignature for Auth 2.0. This is currently not
711 * checked.
712 */
713 case 0x18:
714 {
715 if (device.auth.state != AUST_CHASENT) {
716 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
717 break;
718 }
719
720 /* Here we could check the signature. Since we can't, just
721 * acknowledge and go to authenticated status
722 */
723 IAP_TX_INIT(0x00, 0x19);
724 IAP_TX_PUT(0x00);
725
726 iap_send_tx();
727 device.auth.state = AUST_AUTH;
728 break;
729 }
730
731 /* AckDevAuthenticationStatus (0x19)
732 *
733 * Sent from the iPod to the device
734 */
735
736 /* GetiPodAuthenticationInfo (0x1A)
737 *
738 * Obtain authentication information from the iPod.
739 * This cannot be implemented without posessing an Apple signed
740 * certificate and the corresponding private key.
741 *
742 * Packet format (offset in buf[]: Description)
743 * 0x00: Lingo ID: General Lingo, always 0x00
744 * 0x01: Command, always 0x1A
745 *
746 * This command requires authentication
747 *
748 * Returns:
749 * IAP_ACK_CMD_FAILED
750 */
751 case 0x1A:
752 {
753 CHECKAUTH;
754
755 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
756 break;
757 }
758
759 /* RetiPodAuthenticationInfo (0x1B)
760 *
761 * Sent from the iPod to the device
762 */
763
764 /* AckiPodAuthenticationInfo (0x1C)
765 *
766 * Confirm authentication information from the iPod.
767 * This cannot be implemented without posessing an Apple signed
768 * certificate and the corresponding private key.
769 *
770 * Packet format (offset in buf[]: Description)
771 * 0x00: Lingo ID: General Lingo, always 0x00
772 * 0x01: Command, always 0x1C
773 * 0x02: Authentication state (0x00: OK)
774 *
775 * This command requires authentication
776 *
777 * Returns: (none)
778 */
779 case 0x1C:
780 {
781 CHECKAUTH;
782
783 break;
784 }
785
786 /* GetiPodAuthenticationSignature (0x1D)
787 *
788 * Send challenge information to the iPod.
789 * This cannot be implemented without posessing an Apple signed
790 * certificate and the corresponding private key.
791 *
792 * Packet format (offset in buf[]: Description)
793 * 0x00: Lingo ID: General Lingo, always 0x00
794 * 0x01: Command, always 0x1D
795 * 0x02-0x15: Challenge
796 *
797 * This command requires authentication
798 *
799 * Returns:
800 * IAP_ACK_CMD_FAILED
801 */
802 case 0x1D:
803 {
804 CHECKAUTH;
805
806 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
807 break;
808 }
809
810 /* RetiPodAuthenticationSignature (0x1E)
811 *
812 * Sent from the iPod to the device
813 */
814
815 /* AckiPodAuthenticationStatus (0x1F)
816 *
817 * Confirm chellenge information from the iPod.
818 * This cannot be implemented without posessing an Apple signed
819 * certificate and the corresponding private key.
820 *
821 * Packet format (offset in buf[]: Description)
822 * 0x00: Lingo ID: General Lingo, always 0x00
823 * 0x01: Command, always 0x1C
824 * 0x02: Challenge state (0x00: OK)
825 *
826 * This command requires authentication
827 *
828 * Returns: (none)
829 */
830 case 0x1F:
831 {
832 CHECKAUTH;
833
834 break;
835 }
836
837 /* NotifyiPodStateChange (0x23)
838 *
839 * Sent from the iPod to the device
840 */
841
842 /* GetIpodOptions (0x24)
843 *
844 * Request supported features of the iPod
845 *
846 * Packet format (offset in buf[]: Description)
847 * 0x00: Lingo ID: General Lingo, always 0x00
848 * 0x01: Command, always 0x24
849 *
850 * Retuns:
851 * RetiPodOptions
852 *
853 * Packet format (offset in data[]: Description)
854 * 0x00: Lingo ID: General Lingo, always 0x00
855 * 0x01: Command, always 0x25
856 * 0x02-0x09: Options as a bitfield
857 */
858 case 0x24:
859 {
860 /* There are only two features that can be communicated via this
861 * function, video support and the ability to control line-out usage.
862 * Rockbox supports neither
863 */
864 IAP_TX_INIT(0x00, 0x25);
865 IAP_TX_PUT_U32(0x00);
866 IAP_TX_PUT_U32(0x00);
867
868 iap_send_tx();
869 break;
870 }
871
872 /* RetiPodOptions (0x25)
873 *
874 * Sent from the iPod to the device
875 */
876
877 /* GetAccessoryInfo (0x27)
878 *
879 * Sent from the iPod to the device
880 */
881
882 /* RetAccessoryInfo (0x28)
883 *
884 * Send information about the device
885 *
886 * Packet format (offset in buf[]: Description)
887 * 0x00: Lingo ID: General Lingo, always 0x00
888 * 0x01: Command, always 0x28
889 * 0x02: Accessory info type
890 * 0x03-0xNN: Accessory information (depends on 0x02)
891 *
892 * Returns: (none)
893 *
894 * TODO: Actually do something with the information received here.
895 * Some devices actually expect us to request the data they
896 * offer, so completely ignoring this does not work, either.
897 */
898 case 0x28:
899 {
900 CHECKLEN(3);
901
902 switch (buf[0x02])
903 {
904 /* Info capabilities */
905 case 0x00:
906 {
907 CHECKLEN(7);
908
909 device.capabilities = get_u32(&buf[0x03]);
910 /* Type 0x00 was already queried, that's where this information comes from */
911 device.capabilities_queried = 0x01;
912 device.capabilities &= ~0x01;
913 break;
914 }
915
916 /* For now, ignore all other information */
917 default:
918 {
919 break;
920 }
921 }
922
923 /* If there are any unqueried capabilities left, do so */
924 if (device.capabilities)
925 device.accinfo = ACCST_DATA;
926
927 break;
928 }
929
930 /* GetiPodPreferences (0x29)
931 *
932 * Retrieve information about the current state of the
933 * iPod.
934 *
935 * Packet format (offset in buf[]: Description)
936 * 0x00: Lingo ID: General Lingo, always 0x00
937 * 0x01: Command, always 0x29
938 * 0x02: Information class requested
939 *
940 * This command requires authentication
941 *
942 * Returns on success:
943 * RetiPodPreferences
944 *
945 * Packet format (offset in data[]: Description)
946 * 0x00: Lingo ID: General Lingo, always 0x00
947 * 0x01: Command, always 0x2A
948 * 0x02: Information class provided
949 * 0x03: Information
950 *
951 * Returns on failure:
952 * IAP_ACK_BAD_PARAM
953 */
954 case 0x29:
955 {
956 CHECKLEN(3);
957 CHECKAUTH;
958
959 IAP_TX_INIT(0x00, 0x2A);
960 /* The only information really supported is 0x03, Line-out usage.
961 * All others are video related
962 */
963 if (buf[2] == 0x03) {
964 IAP_TX_PUT(0x03);
965 IAP_TX_PUT(0x01); /* Line-out enabled */
966
967 iap_send_tx();
968 } else {
969 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
970 }
971
972 break;
973 }
974
975 /* RetiPodPreference (0x2A)
976 *
977 * Sent from the iPod to the device
978 */
979
980 /* SetiPodPreferences (0x2B)
981 *
982 * Set preferences on the iPod
983 *
984 * Packet format (offset in buf[]: Description)
985 * 0x00: Lingo ID: General Lingo, always 0x00
986 * 0x01: Command, always 0x29
987 * 0x02: Prefecence class requested
988 * 0x03: Preference setting
989 * 0x04: Restore on exit
990 *
991 * This command requires authentication
992 *
993 * Returns on success:
994 * IAP_ACK_OK
995 *
996 * Returns on failure:
997 * IAP_ACK_BAD_PARAM
998 * IAP_ACK_CMD_FAILED
999 */
1000 case 0x2B:
1001 {
1002 CHECKLEN(5);
1003 CHECKAUTH;
1004
1005 /* The only information really supported is 0x03, Line-out usage.
1006 * All others are video related
1007 */
1008 if (buf[2] == 0x03) {
1009 /* If line-out disabled is requested, reply with IAP_ACK_CMD_FAILED,
1010 * otherwise with IAP_ACK_CMD_OK
1011 */
1012 if (buf[3] == 0x00) {
1013 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
1014 } else {
1015 cmd_ok(cmd);
1016 }
1017 } else {
1018 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1019 }
1020
1021 break;
1022 }
1023
1024 /* The default response is IAP_ACK_BAD_PARAM */
1025 default:
1026 {
1027#ifdef LOGF_ENABLE
1028 logf("iap: Unsupported Mode00 Command");
1029#else
1030 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1031#endif
1032 break;
1033 }
1034 }
1035}
diff --git a/apps/iap/iap-lingo2.c b/apps/iap/iap-lingo2.c
new file mode 100644
index 0000000000..4fbf730192
--- /dev/null
+++ b/apps/iap/iap-lingo2.c
@@ -0,0 +1,278 @@
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 0x02, Simple Remote Lingo
21 *
22 * TODO:
23 * - Fix cmd 0x00 handling, there has to be a more elegant way of doing
24 * this
25 */
26
27#include "iap-core.h"
28#include "iap-lingo.h"
29#include "system.h"
30#include "button.h"
31#include "audio.h"
32#include "settings.h"
33
34/*
35 * This macro is meant to be used inside an IAP mode message handler.
36 * It is passed the expected minimum length of the message buffer.
37 * If the buffer does not have the required lenght an ACK
38 * packet with a Bad Parameter error is generated.
39 */
40#define CHECKLEN(x) do { \
41 if (len < (x)) { \
42 cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
43 return; \
44 }} while(0)
45
46static void cmd_ack(const unsigned char cmd, const unsigned char status)
47{
48 IAP_TX_INIT(0x02, 0x01);
49 IAP_TX_PUT(status);
50 IAP_TX_PUT(cmd);
51
52 iap_send_tx();
53}
54
55#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
56
57void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
58{
59 unsigned int cmd = buf[1];
60
61 /* We expect at least three bytes in the buffer, one for the
62 * lingo, one for the command, and one for the first button
63 * state bits.
64 */
65 CHECKLEN(3);
66
67 /* Lingo 0x02 must have been negotiated */
68 if (!DEVICE_LINGO_SUPPORTED(0x02)) {
69 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
70 return;
71 }
72
73 switch (cmd)
74 {
75 /* ContextButtonStatus (0x00)
76 *
77 * Transmit button events from the device to the iPod
78 *
79 * Packet format (offset in buf[]: Description)
80 * 0x00: Lingo ID: Simple Remote Lingo, always 0x02
81 * 0x01: Command, always 0x00
82 * 0x02: Button states 0:7
83 * 0x03: Button states 8:15 (optional)
84 * 0x04: Button states 16:23 (optional)
85 * 0x05: Button states 24:31 (optional)
86 *
87 * Returns: (none)
88 */
89 case 0x00:
90 {
91 iap_remotebtn = BUTTON_NONE;
92 iap_timeoutbtn = 0;
93
94 if(buf[2] != 0)
95 {
96 if(buf[2] & 1)
97 REMOTE_BUTTON(BUTTON_RC_PLAY);
98 if(buf[2] & 2)
99 REMOTE_BUTTON(BUTTON_RC_VOL_UP);
100 if(buf[2] & 4)
101 REMOTE_BUTTON(BUTTON_RC_VOL_DOWN);
102 if(buf[2] & 8)
103 REMOTE_BUTTON(BUTTON_RC_RIGHT);
104 if(buf[2] & 16)
105 REMOTE_BUTTON(BUTTON_RC_LEFT);
106 }
107 else if(len >= 4 && buf[3] != 0)
108 {
109 if(buf[3] & 1) /* play */
110 {
111 if (audio_status() != AUDIO_STATUS_PLAY)
112 REMOTE_BUTTON(BUTTON_RC_PLAY);
113 }
114 if(buf[3] & 2) /* pause */
115 {
116 if (audio_status() == AUDIO_STATUS_PLAY)
117 REMOTE_BUTTON(BUTTON_RC_PLAY);
118 }
119 if(buf[3] & 128) /* Shuffle */
120 {
121 if (!iap_btnshuffle)
122 {
123 iap_shuffle_state(!global_settings.playlist_shuffle);
124 iap_btnshuffle = true;
125 }
126 }
127 }
128 else if(len >= 5 && buf[4] != 0)
129 {
130 if(buf[4] & 1) /* repeat */
131 {
132 if (!iap_btnrepeat)
133 {
134 iap_repeat_next();
135 iap_btnrepeat = true;
136 }
137 }
138
139 /* Power off
140 * Not quite sure how to react to this, but stopping playback
141 * is a good start.
142 */
143 if (buf[4] & 0x04)
144 {
145 if (audio_status() == AUDIO_STATUS_PLAY)
146 REMOTE_BUTTON(BUTTON_RC_PLAY);
147 }
148
149 if(buf[4] & 16) /* ffwd */
150 REMOTE_BUTTON(BUTTON_RC_RIGHT);
151 if(buf[4] & 32) /* frwd */
152 REMOTE_BUTTON(BUTTON_RC_LEFT);
153 }
154
155 break;
156 }
157 /* ACK (0x01)
158 *
159 * Sent from the iPod to the device
160 */
161
162 /* ImageButtonStatus (0x02)
163 *
164 * Transmit image button events from the device to the iPod
165 *
166 * Packet format (offset in buf[]: Description)
167 * 0x00: Lingo ID: Simple Remote Lingo, always 0x02
168 * 0x01: Command, always 0x02
169 * 0x02: Button states 0:7
170 * 0x03: Button states 8:15 (optional)
171 * 0x04: Button states 16:23 (optional)
172 * 0x05: Button states 24:31 (optional)
173 *
174 * This command requires authentication
175 *
176 * Returns on success:
177 * IAP_ACK_OK
178 *
179 * Returns on failure:
180 * IAP_ACK_*
181 */
182 case 0x02:
183 {
184 if (!DEVICE_AUTHENTICATED) {
185 cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
186 break;
187 }
188
189 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
190 break;
191 }
192
193 /* VideoButtonStatus (0x03)
194 *
195 * Transmit video button events from the device to the iPod
196 *
197 * Packet format (offset in buf[]: Description)
198 * 0x00: Lingo ID: Simple Remote Lingo, always 0x02
199 * 0x01: Command, always 0x03
200 * 0x02: Button states 0:7
201 * 0x03: Button states 8:15 (optional)
202 * 0x04: Button states 16:23 (optional)
203 * 0x05: Button states 24:31 (optional)
204 *
205 * This command requires authentication
206 *
207 * Returns on success:
208 * IAP_ACK_OK
209 *
210 * Returns on failure:
211 * IAP_ACK_*
212 */
213 case 0x03:
214 {
215 if (!DEVICE_AUTHENTICATED) {
216 cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
217 break;
218 }
219
220 cmd_ack(cmd, IAP_ACK_CMD_FAILED);
221 break;
222 }
223
224 /* AudioButtonStatus (0x04)
225 *
226 * Transmit audio button events from the device to the iPod
227 *
228 * Packet format (offset in buf[]: Description)
229 * 0x00: Lingo ID: Simple Remote Lingo, always 0x02
230 * 0x01: Command, always 0x04
231 * 0x02: Button states 0:7
232 * 0x03: Button states 8:15 (optional)
233 * 0x04: Button states 16:23 (optional)
234 * 0x05: Button states 24:31 (optional)
235 *
236 * This command requires authentication
237 *
238 * Returns on success:
239 * IAP_ACK_OK
240 *
241 * Returns on failure:
242 * IAP_ACK_*
243 */
244 case 0x04:
245 {
246 unsigned char repeatbuf[6];
247
248 if (!DEVICE_AUTHENTICATED) {
249 cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
250 break;
251 }
252
253 /* This is basically the same command as ContextButtonStatus (0x00),
254 * with the difference that it requires authentication and that
255 * it returns an ACK packet to the device.
256 * So just route it through the handler again, with 0x00 as the
257 * command
258 */
259 memcpy(repeatbuf, buf, 6);
260 repeatbuf[1] = 0x00;
261 iap_handlepkt_mode2((len<6)?len:6, repeatbuf);
262
263 cmd_ok(cmd);
264 break;
265 }
266
267 /* The default response is IAP_ACK_BAD_PARAM */
268 default:
269 {
270#ifdef LOGF_ENABLE
271 logf("iap: Unsupported Mode02 Command");
272#else
273 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
274#endif
275 break;
276 }
277 }
278}
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}
diff --git a/apps/iap/iap-lingo4.c b/apps/iap/iap-lingo4.c
new file mode 100644
index 0000000000..fa0196645b
--- /dev/null
+++ b/apps/iap/iap-lingo4.c
@@ -0,0 +1,3153 @@
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#include "iap-core.h"
21#include "iap-lingo.h"
22#include "dir.h"
23#include "settings.h"
24#include "filetree.h"
25#include "wps.h"
26#include "playback.h"
27
28/*
29 * This macro is meant to be used inside an IAP mode message handler.
30 * It is passed the expected minimum length of the message buffer.
31 * If the buffer does not have the required lenght an ACK
32 * packet with a Bad Parameter error is generated.
33 */
34#define CHECKLEN(x) do { \
35 if (len < (x)) { \
36 cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
37 return; \
38 }} while(0)
39
40/* Check for authenticated state, and return an ACK Not
41 * Authenticated on failure.
42 */
43#define CHECKAUTH do { \
44 if (!DEVICE_AUTHENTICATED) { \
45 cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \
46 return; \
47 }} while(0)
48
49/* Used to remember the last Type and Record requested */
50static char cur_dbrecord[5] = {0};
51
52/* Used to remember the total number of filtered database records */
53static unsigned long dbrecordcount = 0;
54
55/* Used to remember the LAST playlist selected */
56static unsigned long last_selected_playlist = 0;
57
58static void cmd_ack(const unsigned int cmd, const unsigned char status)
59{
60 IAP_TX_INIT4(0x04, 0x0001);
61 IAP_TX_PUT(status);
62 IAP_TX_PUT_U16(cmd);
63 iap_send_tx();
64}
65
66#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
67
68static void get_playlist_name(unsigned char *dest,
69 unsigned long item_offset,
70 size_t max_length)
71{
72 if (item_offset == 0) return;
73 DIR* dp;
74 struct dirent* playlist_file = NULL;
75
76 dp = opendir(global_settings.playlist_catalog_dir);
77
78 char *extension;
79 unsigned long nbr = 0;
80 while ((nbr < item_offset) && ((playlist_file = readdir(dp)) != NULL))
81 {
82 /*Increment only if there is a playlist extension*/
83 if ((extension=strrchr(playlist_file->d_name, '.')) != NULL){
84 if ((strcmp(extension, ".m3u") == 0 ||
85 strcmp(extension, ".m3u8") == 0))
86 nbr++;
87 }
88 }
89 if (playlist_file != NULL) {
90 strlcpy(dest, playlist_file->d_name, max_length);
91 }
92 closedir(dp);
93}
94
95static void seek_to_playlist(unsigned long index)
96{
97 unsigned char selected_playlist
98 [sizeof(global_settings.playlist_catalog_dir)
99 + 1
100 + MAX_PATH] = {0};
101
102 strcpy(selected_playlist,
103 global_settings.playlist_catalog_dir);
104 int len = strlen(selected_playlist);
105 selected_playlist[len] = '/';
106 get_playlist_name (selected_playlist + len + 1,
107 index,
108 MAX_PATH);
109 ft_play_playlist(selected_playlist,
110 global_settings.playlist_catalog_dir,
111 strrchr(selected_playlist, '/') + 1);
112
113}
114
115static unsigned long nbr_total_playlists(void)
116{
117 DIR* dp;
118 unsigned long nbr_total_playlists = 0;
119 struct dirent* playlist_file = NULL;
120 char *extension;
121 dp = opendir(global_settings.playlist_catalog_dir);
122 while ((playlist_file = readdir(dp)) != NULL)
123 {
124 /*Increment only if there is a playlist extension*/
125 if ((extension=strrchr(playlist_file->d_name, '.')) != NULL)
126 {
127 if ((strcmp(extension, ".m3u") == 0 ||
128 strcmp(extension, ".m3u8") == 0))
129 {
130 nbr_total_playlists++;
131 }
132 }
133 }
134 closedir(dp);
135 return nbr_total_playlists;
136}
137
138void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
139{
140 unsigned int cmd = (buf[1] << 8) | buf[2];
141 /* Lingo 0x04 commands are at least 3 bytes in length */
142 CHECKLEN(3);
143
144 /* Lingo 0x04 must have been negotiated */
145 if (!DEVICE_LINGO_SUPPORTED(0x04)) {
146#ifdef LOGF_ENABLE
147 logf("iap: Mode04 Not Negotiated");
148#endif
149 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
150 return;
151 }
152
153 /* All these commands require extended interface mode */
154 if (interface_state != IST_EXTENDED) {
155#ifdef LOGF_ENABLE
156 logf("iap: Not in Mode04 Extended Mode");
157#endif
158 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
159 return;
160 }
161 switch (cmd)
162 {
163 case 0x0001: /* CmdAck. See above cmd_ack() */
164 /*
165 * The following is the description for the Apple Firmware
166 * The iPod sends this telegram to acknowledge the receipt of a
167 * command and return the command status. The command ID field
168 * indicates the device command for which the response is being
169 * sent. The command status indicates the results of the command
170 * (success or failure).
171 *
172 * Byte Value Meaning
173 * 0 0xFF Sync byte (required only for UART serial)
174 * 1 0x55 Start of telegram
175 * 2 0x06 Telegram payload length
176 * 3 0x04 Lingo ID: Extended Interface lingo
177 * 4 0x00 Command ID (bits 15:8)
178 * 5 0x01 Command ID (bits 7:0)
179 * 6 0xNN Command result status. Possible values are:
180 *  0x00 = Success (OK)
181 * 0x01 = ERROR: Unknown database category
182 *  0x02 = ERROR: Command failed
183 * 0x03 = ERROR: Out of resources
184 * 0x04 = ERROR: Bad parameter
185 * 0x05 = ERROR: Unknown ID
186 * 0x06 = Reserved
187 * 0x07 = Accessory not authenticated
188 *  0x08 - 0xFF = Reserved
189 * 7 0xNN The ID of the command being acknowledged (bits 15:8).
190 * 8 0xNN The ID of the command being acknowledged (bits 7:0).
191 * 9 0xNN Telegram payload checksum byte
192 */
193 {
194 /* We should NEVER receive this command so ERROR if we do */
195 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
196 break;
197 }
198 case 0x0002: /* GetCurrentPlayingTrackChapterInfo */
199 /* The following is the description for the Apple Firmware
200 * Requests the chapter information of the currently playing track.
201 * In response, the iPod sends a
202 * Command 0x0003: ReturnCurrentPlayingTrackChapterInfo
203 * telegram to the device.
204 * Note: The returned track index is valid only when there is a
205 * currently playing or paused track.
206 *
207 * Byte Value Meaning
208 * 0 0xFF Sync byte (required only for UART serial)
209 * 1 0x55 Start of telegram
210 * 2 0x03 Telegram payload length
211 * 3 0x04 Lingo ID: Extended Interface lingo
212 * 4 0x00 Command ID (bits 15:8)
213 * 5 0x02 Command ID (bits 7:0)
214 * 6 0xF7 Telegram payload checksum byte
215 *
216 * We Return that the track does not have chapter information by
217 * returning chapter index -1 (0xFFFFFFFF) and chapter count 0
218 * (0x00000000)
219 */
220 {
221 unsigned char data[] = {0x04, 0x00, 0x03,
222 0xFF, 0xFF, 0xFF, 0xFF,
223 0x00, 0x00, 0x00, 0x00};
224 iap_send_pkt(data, sizeof(data));
225 break;
226 }
227 case 0x0003: /* ReturnCurrentPlayingTrackChapterInfo. See Above */
228 /* The following is the description for the Apple Firmware
229 *
230 * Returns the chapter information of the currently playing track.
231 * The iPod sends this telegramin response to the
232 * Command 0x0002: GetCurrentPlayingTrackChapterInfo
233 * telegram from the device. The track chapter information includes
234 * the currently playingtrack's chapter index,as well as the
235 * total number of chapters in the track. The track chapter and the
236 * total number of chapters are 32-bit signed integers. The chapter
237 * index of the firstchapter is always 0x00000000. If the track does
238 * not have chapter information, a chapter index of -1(0xFFFFFFFF)
239 * and a chapter count of 0 (0x00000000) are returned.
240 *
241 * Byte Value Meaning
242 * 0 0xFF Sync byte (required only for UART serial)
243 * 1 0x55 Start of telegram
244 * 2 0x0B Telegram payload length
245 * 3 0x04 Lingo ID: Extended Interface lingo
246 * 4 0x00 Command ID (bits 15:8)
247 * 5 0x03 Command ID (bits 7:0)
248 * 6 0xNN Current chapter index (bits 31:24)
249 * 7 0xNN Current chapter index (bits 23:16)
250 * 8 0xNN Current chapter index (bits 15:8)
251 * 9 0xNN Current chapter index (bits 7:0)
252 * 10 0xNN Chapter count (bits 31:24)
253 * 11 0xNN Chapter count (bits 23:16)
254 * 12 0xNN Chapter count (bits 15:8)
255 * 13 0xNN Chapter count (bits 7:0)
256 * 14 0xNN Telegram payload checksum byte
257 */
258 {
259 /* We should NEVER receive this command so ERROR if we do */
260 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
261 break;
262 }
263 case 0x0004: /* SetCurrentPlayingTrackChapter */
264 /* The following is the description for the Apple Firmware
265 *
266 * Sets the currently playing track chapter.You can send the Command
267 * 0x0002: GetCurrentPlayingTrackChapterInfo telegram to get the
268 * chapter count and the index of the currently playing chapter in
269 * the current track. In response to the command
270 * SetCurrentPlayingTrackChapter, the iPod sends an ACK telegram
271 * with the command status.
272 *
273 * Note: This command should be used only when the iPod is in a
274 * playing or paused state. The command fails if the iPod is stopped
275 * or if the track does not contain chapter information.
276 *
277 * Byte Value Meaning
278 * 0 0xFF Sync byte (required only for UART serial)
279 * 1 0x55 Start of telegram
280 * 2 0x07 Telegram payload length
281 * 3 0x04 Lingo ID: Extended Interface lingo
282 * 4 0x00 Command ID (bits 15:8)
283 * 5 0x04 Command ID (bits 7:0)
284 * 6 0xNN Chapter index (bits 31:24)
285 * 7 0xNN Chapter index (bits 23:16)
286 * 8 0xNN Chapter index (bits 15:8)
287 * 9 0xNN Chapter index (bits 7:0)
288 * 10 0xNN Telegram payload checksum byte
289 *
290 * We don't do anything with this as we don't support chapters,
291 * so just return BAD_PARAM
292 */
293 {
294 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
295 break;
296 }
297 case 0x0005: /* GetCurrentPlayingTrackChapterPlayStatus */
298 /* The following is the description for the Apple Firmware
299 *
300 * Requests the chapter playtime status of the currently playing
301 * track. The status includes the chapter length and the time
302 * elapsed within that chapter. In response to a valid telegram, the
303 * iPod sends a Command 0x0006:
304 * ReturnCurrentPlayingTrackChapterPlayStatus telegram to the
305 * device.
306 *
307 * Note: If the telegram length or chapter index is invalid for
308 * instance, if the track does not contain chapter information the
309 * iPod responds with an ACK telegram including the specific error
310 * status.
311 *
312 * Byte Value Meaning
313 * 0 0xFF Sync byte (required only for UART serial)
314 * 1 0x55 Start of telegram
315 * 2 0x07 Telegram payload length
316 * 3 0x04 Lingo ID: Extended Interface lingo
317 * 4 0x00 Command ID (bits 15:8)
318 * 5 0x05 Command ID (bits 7:0)
319 * 6 0xNN Currently playingchapter index (bits31:24)
320 * 7 0xNN Currently playingchapter index (bits23:16)
321 * 8 0xNN Currently playing chapter index (bits 15:8)
322 * 9 0xNN Currently playing chapter index (bits 7:0)
323 * 10 0xNN Telegram payload checksum byte
324 *
325 * The returned data includes 4 bytes for Chapter Length followed
326 * by 4 bytes of elapsed time If there is no currently playing
327 * chapter, length and elapsed are 0
328 * We don't do anything with this as we don't support chapters,
329 * so just return BAD_PARAM
330 */
331 {
332 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
333 break;
334 }
335 case 0x0006: /* ReturnCurrentPlayingTrackChapterPlayStatus. See Above */
336 /* The following is the description for the Apple Firmware
337 *
338 * Returns the play status of the currently playing track chapter.
339 * The iPod sends this telegram in response to the Command 0x0005:
340 * GetCurrentPlayingTrackChapterPlayStatus telegram from the device.
341 * The returned information includes the chapter length and elapsed
342 * time, in milliseconds. If there is no currently playing chapter,
343 * the chapter length and elapsed time are zero.
344 *
345 * Byte Value Meaning
346 * 0 0xFF Sync byte (required only for UART serial)
347 * 1 0x55 Start of telegram
348 * 2 0x0B Telegram payload length
349 * 3 0x04 Lingo ID: Extended Interface lingo
350 * 4 0x00 Command ID (bits 15:8)
351 * 5 0x06 Command ID (bits 7:0)
352 * 6 0xNN Chapter length in milliseconds (bits 31:24)
353 * 7 0xNN Chapter length in milliseconds (bits 23:16)
354 * 8 0xNN Chapter length in milliseconds (bits 15:8)
355 * 9 0xNN Chapter length in milliseconds (bits 7:0)
356 * 10 0xNN Elapsed time in chapter, in milliseconds (bits 31:24)
357 * 11 0xNN Elapsed time in chapter, in milliseconds (bits 23:16)
358 * 12 0xNN Elapsed time in chapter, in milliseconds (bits 15:8)
359 * 13 0xNN Elapsed time in chapter, in milliseconds (bits 7:0)
360 * 14 0xNN Telegram payload checksum byte
361 */
362 {
363 /* We should NEVER receive this command so ERROR if we do */
364 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
365 break;
366 }
367 case 0x0007: /* GetCurrentPlayingTrackChapterName */
368 /* The following is the description for the Apple Firmware
369 *
370 * Requests a chapter name in the currently playing track. In
371 * response to a valid telegram, the iPod sends a Command 0x0008:
372 * ReturnCurrentPlayingTrackChapterName telegram to the device.
373 *
374 * Note: If the received telegram length or track index is invalid
375 * for instance, if the track does not have chapter information or
376 * is not a part of the Audiobook category, the iPod responds with
377 * an ACK telegram including the specific error status.
378 *
379 * Byte Value Meaning
380 * 0 0xFF Sync byte (required only for UART serial)
381 * 1 0x55 Start of telegram
382 * 2 0x07 Telegram payload length
383 * 3 0x04 Lingo ID: Extended Interface lingo
384 * 4 0x00 Command ID (bits 15:8)
385 * 5 0x07 Command ID (bits 7:0)
386 * 6 0xNN Chapter index (bits 31:24)
387 * 7 0xNN Chapter index (bits 23:16)
388 * 8 0xNN Chapter index (bits 15:8)
389 * 9 0xNN Chapter index (bits 7:0)
390 * 10 0xNN Telegram payload checksum byte
391 *
392 * We don't do anything with this as we don't support chapters,
393 * so just return BAD_PARAM
394 */
395 {
396 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
397 break;
398 }
399 case 0x0008: /* ReturnCurrentPlayingTrackChapterName. See Above */
400 /* The following is the description for the Apple Firmware
401 *
402 * Returns a chapter name in the currently playing track. The iPod
403 * sends this telegram in response to a valid Command 0x0007:
404 * GetCurrentPlayingTrackChapterName telegram from the
405 * device. The chapter name is encoded as a null-terminated UTF-8
406 * character array.
407 *
408 * Note: The chapter name string is not limited to 252 characters;
409 * it may be sent in small or large telegram format depending on
410 * the string length. The small telegram format is shown.
411 *
412 * Byte Value Meaning
413 * 0 0xFF Sync byte (required only for UART serial)
414 * 1 0x55 Start of telegram
415 * 2 0xNN Telegram payload length
416 * 3 0x04 Lingo ID: Extended Interface lingo
417 * 4 0x00 Command ID (bits 15:8)
418 * 5 0x08 Command ID (bits 7:0)
419 * 6-N 0xNN Chapter name as UTF-8 character array
420 *(last byte) 0xNN Telegram payload checksum byte
421 *
422 */
423 {
424 /* We should NEVER receive this command so ERROR if we do */
425 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
426 break;
427 }
428 case 0x0009: /* GetAudioBookSpeed */
429 /* The following is the description for the Apple Firmware
430 *
431 * Requests the current iPod audiobook speed state. The iPod
432 * responds with the “Command 0x000A: ReturnAudiobookSpeed”
433 * telegram indicating the current audiobook speed.
434 *
435 * Byte Value Meaning
436 * 0 0xFF Sync byte (required only for UART serial)
437 * 1 0x55 Start of telegram
438 * 2 0x03 Telegram payload length
439 * 3 0x04 Lingo ID: Extended Interface lingo
440 * 4 0x00 Command ID (bits 15:8)
441 * 5 0x09 Command ID (bits 7:0)
442 * 6 0xF0 Telegram payload checksum byte
443 *
444 * ReturnAudioBookSpeed
445 * 0x00 = Normal, 0xFF = Slow, 0x01 = Fast
446 * We always respond with Normal speed
447 */
448 {
449 unsigned char data[] = {0x04, 0x00, 0x0A, 0x00};
450 iap_send_pkt(data, sizeof(data));
451 break;
452 }
453 case 0x000A: /* ReturnAudioBookSpeed. See Above */
454 /* The following is the description for the Apple Firmware
455 *
456 * Returns the current audiobook speed setting. The iPod sends
457 * this telegram in response to the Command 0x0009:
458 * GetAudiobookSpeed command from the device.
459 *
460 * Byte Value Meaning
461 * 0 0xFF Sync byte (required only for UART serial)
462 * 1 0x55 Start of telegram
463 * 2 0x04 Telegram payload length
464 * 3 0x04 Lingo ID: Extended Interface lingo
465 * 4 0x00 Command ID (bits 15:8)
466 * 5 0x0A Command ID (bits 7:0)
467 * 6 0xNN Audiobook speed status code.
468 * 0xFF Slow (-1)
469 * 0x00 Normal
470 * 0x01 Fast (+1)
471 * 7 0xNN Telegram payload checksum byte
472 *
473 */
474 {
475 /* We should NEVER receive this command so ERROR if we do */
476 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
477 break;
478 }
479 case 0x000B: /* SetAudioBookSpeed */
480 /* The following is the description for the Apple Firmware
481 * Sets the speed of audiobook playback. The iPod audiobook speed
482 * states are listed above. This telegram has two modes: one to
483 * set the speed of the currently playing audiobook and a second
484 * to set the audiobook speed for all audiobooks. Byte number 7
485 * is an optional byte; devices should not send this byte if they
486 * want to set the speed only of the currently playing audiobook.
487 * If devices want to set the global audiobook speed setting then
488 * they must use byte number 7. This is the Restore on Exit byte;
489 * a nonzero value restores the original audiobook speed setting
490 * when the accessory is detached. If this byte is zero, the
491 * audiobook speed setting set by the accessory is saved and
492 * persists after the accessory is detached from the iPod. See below
493 * for the telegram format used when including the Restore on Exit
494 * byte.
495 * In response to this command, the iPod sends an ACK telegram with
496 * the command status.
497 *
498 * Note: Accessory developers are encouraged to always use the
499 * Restore on Exit byte with a nonzero value, to restore any of the
500 * user's iPod settings that were modified by the accessory.
501 *
502 * Byte Value Meaning (Cuurent audiobook only
503 * 0 0xFF Sync byte (required only for UART serial)
504 * 1 0x55 Start of telegram
505 * 2 0x04 Telegram payload length
506 * 3 0x04 Lingo ID: Extended Interface lingo
507 * 4 0x00 Command ID (bits 15:8)
508 * 5 0x0B Command ID (bits 7:0)
509 * 6 0xNN New audiobook speed code.
510 * 7 0xNN Telegram payload checksum byte
511 *
512 *
513 * SetAudiobookSpeed telegram to set the global audiobook speed
514 * Byte Value Meaning
515 * 0 0xFF Sync byte (required only for UART serial)
516 * 1 0x55 Start of telegram
517 * 2 0x05 Telegram payload length
518 * 3 0x04 Lingo ID: Extended Interface lingo
519 * 4 0x00 Command ID (bits 15:8)
520 * 5 0x0B Command ID (bits 7:0)
521 * 6 0xNN Global audiobook speed code.
522 * 7 0xNN Restore on Exit byte.
523 * If 1, the original setting is restored on detach;
524 * if 0, the newsetting persists after accessory detach.
525 * 8 0xNN Telegram payload checksum byte
526 *
527 *
528 * We don't do anything with this as we don't support chapters,
529 * so just return BAD_PARAM
530 */
531 {
532 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
533 break;
534 }
535 case 0x000C: /* GetIndexedPlayingTrackInfo */
536 /* The following is the description for the Apple Firmware
537 *
538 * Gets track information for the track at the specified index.
539 * The track info type field specifies the type of information to be
540 * returned, such as song lyrics, podcast name, episode date, and
541 * episode description. In response, the iPod sends the Command
542 * 0x000D: ReturnIndexedPlayingTrackInfo command with the requested
543 * track information. If the information type is invalid or does not
544 * apply to the selected track, the iPod returns an ACK with an
545 * error status.
546 *
547 * Byte Value Meaning
548 * 0 0xFF Sync byte (required only for UART serial)
549 * 1 0x55 Start of telegram
550 * 2 0x0A Telegram payload length
551 * 3 0x04 Lingo ID: Extended Interface lingo
552 * 4 0x00 Command ID (bits 15:8)
553 * 5 0x0C Command ID (bits 7:0)
554 * 6 0xNN Track info type. See Below
555 * 7 0xNN Track index (bits 31:24)
556 * 8 0xNN Track index (bits 23:16)
557 * 9 0xNN Track index (bits 15:8)
558 * 10 0xNN Track index (bits 7:0)
559 * 11 0xNN Chapter index (bits 15:8)
560 * 12 0xNN Chapter index (bits 7:0)
561 * (last byte)0xNN Telegram payload checksum byte
562 *
563 * Track Info Types: Return Data
564 * 0x00 Track Capabilities. 10byte data
565 * 0x01 Podcast Name UTF-8 String
566 * 0x02 Track Release Date 7Byte Data All 0 if invalid
567 * 0x03 Track Description UTF-8 String
568 * 0x04 Track Song Lyrics UTF-8 String
569 * 0x05 Track Genre UTF-8 String
570 * 0x06 Track Composer UTF-8 String
571 * 0x07 Track Artwork Count 2byte formatID and 2byte count of images
572 * 0x08-0xff Reserved
573 *
574 * Track capabilities
575 * 0x00-0x03
576 * Bit0 is Audiobook
577 * Bit1 has chapters
578 * Bit2 has album art
579 * Bit3 has lyrics
580 * Bit4 is podcast episode
581 * Bit5 has Release Date
582 * Bit6 has Decription
583 * Bit7 Contains Video
584 * Bit8 Play as Video
585 * Bit9+ Reserved
586 * 0x04-0x07 Total Track Length in ms
587 * 0x08-0x09 Chapter Count
588 *
589 * Track Release Date Encoding
590 * 0x00 Seconds 0-59
591 * 0x01 Minutes 0-59
592 * 0x02 Hours 0-23
593 * 0x03 Day Of Month 1-31
594 * 0x04 Month 1-12
595 * 0x05 Year Bits 15:8 2006 = 2006AD
596 * 0x06 Year Bits 7:0
597 * 0x07 Weekday 0-6 where 0=Sunday 6=Saturday
598 *
599 *
600 */
601 {
602 if (len < (10))
603 {
604 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
605 break;
606 }
607 struct mp3entry *id3 = audio_current_track();
608
609 switch(buf[3])
610 {
611 case 0x01: /* Podcast Not Supported */
612 case 0x04: /* Lyrics Not Supported */
613 case 0x03: /* Description */
614 case 0x05: /* Genre */
615 case 0x06: /* Composer */
616 {
617 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
618 break;
619 }
620 default:
621 {
622 IAP_TX_INIT4(0x04, 0x000D);
623 IAP_TX_PUT(buf[3]);
624 switch(buf[3])
625 {
626 case 0x00:
627 {
628 /* Track Capabilities 10Bytes Data */
629 IAP_TX_PUT_U32(0); /* Track Capabilities. */
630 /* Currently None Supported*/
631 IAP_TX_PUT_U32(id3->length); /* Track Length */
632 IAP_TX_PUT_U16(0x00); /* Chapter Count */
633 break;
634 }
635 case 0x02:
636 {
637 /* Track Release Date 7 Bytes Data
638 * Currently only returns a fixed value,
639 * Sunday 1st Feb 2011 3Hr 4Min 5Secs
640 */
641 IAP_TX_PUT(5); /* Seconds 0-59 */
642 IAP_TX_PUT(4); /* Minutes 0-59 */
643 IAP_TX_PUT(3); /* Hours 0-23 */
644 IAP_TX_PUT(1); /* Day Of Month 1-31 */
645 IAP_TX_PUT(2); /* Month 1-12 */
646 IAP_TX_PUT_U16(2011); /* Year */
647 IAP_TX_PUT(0); /* Day 0=Sunday */
648 break;
649 }
650 case 0x07:
651 {
652 /* Track Artwork Count */
653 /* Currently not supported */
654 IAP_TX_PUT_U16(0x00); /* Format ID */
655 IAP_TX_PUT_U16(0x00); /* Image Count */
656 break;
657 }
658 }
659 iap_send_tx();
660 break;
661 }
662 }
663 break;
664 }
665 case 0x000D: /* ReturnIndexedPlayingTrackInfo. See Above */
666 /* The following is the description for the Apple Firmware
667 *
668 * Returns the requested track information type and data. The iPod
669 * sends this command in response to the Command 0x000C:
670 * GetIndexedPlayingTrackInfo command.
671 * Data returned as strings are encoded as null-terminated UTF-8
672 * character arrays.
673 * If the track information string does not exist, a null UTF-8
674 * string is returned. If the track has no release date, then the
675 * returned release date has all bytes zeros. Track song lyrics and
676 * the track description are sent in a large or small telegram
677 * format with an incrementing packet index field, spanning
678 * multiple packets if needed.
679 *
680 * Below is the packet format for the
681 * ReturnIndexedPlayingTrackInfo telegram sent in response to a
682 * request for information types 0x00 to 0x02.
683 *
684 * Byte Value Meaning
685 * 0 0xFF Sync byte
686 * 1 0x55 Start of telegram
687 * 2 0xNN Telegram payload length
688 * 3 0x04 Lingo ID: Extended Interface lingo
689 * 4 0x00 Command ID (bits 15:8)
690 * 5 0x0D Command ID (bits 7:0)
691 * 6 0xNN Track info type. See
692 * 7-N 0xNN Track information. The data format is specific
693 * to the track info type.
694 * NN 0xNN Telegram payload checksum byte
695 *
696 * Below is the large packet format for the
697 * ReturnIndexedPlayingTrackInfo telegram sent in response to a
698 * request for information types 0x03 to 0x04. The small telegram
699 * format is not shown.
700 *
701 * Byte Value Meaning
702 * 0 0xFF Sync byte
703 * 1 0x55 Start of telegram
704 * 2 0x00 Telegram payload marker (large format)
705 * 3 0xNN Large telegram payload length (bits 15:8)
706 * 4 0xNN Large telegram payload length (bits 7:0)
707 * 5 0x04 Lingo ID (Extended Interface lingo)
708 * 6 0x00 Command ID (bits 15:8)
709 * 7 0x0D Command ID (bits 7:0)
710 * 8 0xNN Track info type.
711 * 9 0xNN Packet information bits. If set,
712 * these bits have the following meanings:
713 * Bit 0: Indicates that there are multiple packets.
714 * Bit 1: This is the last packet. Applicable only if
715 * bit 0 is set.
716 * Bit 31:2 Reserved
717 * 10 0xNN Packet Index (bits 15:8)
718 * 11 0xNN Packet Index (bits 7:0)
719 * 12-N 0xNN Track information as a UTF-8 string.
720 * NN 0xNN Telegram payload checksum byte
721 *
722 * Track info types and return data
723 * Info Type Code Data Format
724 * 0x00 Track Capabilities and Information 10-byte data.
725 * 0x01 PodcastName UTF-8 string
726 * 0x02 Track Release Date 7-byte data.
727 * 0x03 Track Description UTF-8 string
728 * 0x04 Track Song Lyrics UTF-8 string
729 * 0x05 TrackGenre UTF-8 string
730 * 0x06 Track Composer UTF-8 string
731 * 0x07 Track Artwork Count Artwork count data. The
732 * artwork count is a sequence of 4-byte records; each record
733 * consists of a 2-byte format ID value followed by a 2-byte
734 * count of images in that format for this track. For more
735 * information about formatID and chapter index values, see
736 * commands 0x000E-0x0011 and 0x002A-0x002B.
737 * 0x08-0xFF Reserved
738 *
739 * Track Capabilities and Information encoding
740 * Byte Code
741 * 0x00-0x03 Track Capability bits. If set, these bits have the
742 * following meanings:
743 * Bit 0: Track is audiobook
744 * Bit 1: Track has chapters
745 * Bit 2: Track has album artwork
746 * Bit 3: Track has song lyrics
747 * Bit 4: Track is a podcast episode
748 * Bit 5: Track has release date
749 * Bit 6: Track has description
750 * Bit 7: Track contains video (a video podcast, music
751 * video, movie, or TV show)
752 * Bit 8: Track is currently queued to play as a video
753 * Bit 31:9: Reserved
754 * 0x04 Total track length, in milliseconds (bits 31:24)
755 * 0x05 Total track length, in milliseconds (bits 23:16)
756 * 0x06 Total track length, in milliseconds (bits 15:8)
757 * 0x07 Total track length, in milliseconds (bits 7:0)
758 * 0x08 Chapter count (bits 15:8)
759 * 0x09 Chapter count (bits 7:0)
760 *
761 * Track Release Date encoding
762 * Byte Code
763 * 0x00 Seconds (0-59)
764 * 0x01 Minutes (0-59)
765 * 0x02 Hours (0-23)
766 * 0x03 Day of themonth(1-31)
767 * 0x04 Month (1-12)
768 * 0x05 Year (bits 15:8). For example, 2006 signifies the year 2006
769 * 0x06 Year (bits 7:0)
770 * 0x07 Weekday (0-6, where 0 = Sunday and 6 = Saturday)
771 *
772 */
773 {
774 /* We should NEVER receive this command so ERROR if we do */
775 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
776 break;
777 }
778 case 0x000E: /* GetArtworkFormats */
779 /* The following is the description for the Apple Firmware
780 *
781 * The device sends this command to obtain the list of supported
782 * artwork formats on the iPod. No parameters are sent.
783 *
784 * Byte Value Comment
785 * 0 0xFF Sync byte (required only for UART serial)
786 * 1 0x55 Start of telegram
787 * 2 0x03 Length of packet
788 * 3 0x04 Lingo ID: Extended Interface lingo
789 * 4 0x00 Command ID (bits 15:8)
790 * 5 0x0E Command ID (bits 7:0)
791 * 6 0xEB Checksum/
792 *
793 * Returned Artwork Formats are a 7byte record.
794 * formatID:2
795 * pixelFormat:1
796 * width:2
797 * height:2
798 */
799 {
800 unsigned char data[] = {0x04, 0x00, 0x0F,
801 0x04, 0x04, /* FormatID */
802 0x02, /* PixelFormat*/
803 0x00, 0x64, /* 100 pixels */
804 0x00, 0x64, /* 100 pixels */
805 0x04, 0x05, /* FormatID */
806 0x02, /* PixelFormat*/
807 0x00, 0xC8, /* 200 pixels */
808 0x00, 0xC8 /* 200 pixels */
809 };
810 iap_send_pkt(data, sizeof(data));
811 break;
812 }
813 case 0x000F: /* RetArtworkFormats. See Above */
814 /* The following is the description for the Apple Firmware
815 *
816 * The iPod sends this command to the device, giving it the list
817 * of supported artwork formats. Each format is described in a
818 * 7-byte record (formatID:2, pixelFormat:1, width:2, height:2).
819 * The formatID is used when sending GetTrackArtworkTimes.
820 * The device may return zero records.
821 *
822 * Byte Value Comment
823 * 0 0xFF Sync byte (required only for UART serial)
824 * 1 0x55 Start of telegram
825 * 2 0xNN Length of packet
826 * 3 0x04 Lingo ID: Extended Interface lingo
827 * 4 0x00 Command ID (bits 15:8)
828 * 5 0x0F Command ID (bits 7:0)
829 * NN 0xNN formatID (15:8) iPod-assigned value for this format
830 * NN 0xNN formatID (7:0)
831 * NN 0xNN pixelFormat. Same as from SetDisplayImage
832 * NN 0xNN imageWidth(15:8).Number of pixels widefor eachimage.
833 * NN 0xNN imageWidth (7:0)
834 * NN 0xNN imageHeight (15:8). Number of pixels high for each
835 * NN 0xNN imageHeight (7:0). image
836 * Previous 7 bytes may be repeated NN times
837 * NN 0xNN Checksum
838 *
839 */
840 {
841 /* We should NEVER receive this command so ERROR if we do */
842 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
843 break;
844 }
845 case 0x0010: /* GetTrackArtworkData */
846 /* The following is the description for the Apple Firmware
847 * The device sends this command to the iPod to request data for a
848 * given trackIndex, formatID, and artworkIndex. The time offset
849 * from track start is the value returned by GetTrackArtworkTimes
850 *
851 * Byte Value Comment
852 * 0 0xFF Sync byte (required only for UART serial)
853 * 1 0x55 Start of telegram
854 * 2 0x0D Length of packet
855 * 3 0x04 Lingo ID: Extended Interface lingo
856 * 4 0x00 Command ID (bits 15:8)
857 * 5 0x10 Command ID (bits 7:0)
858 * 6 0xNN trackIndex (31:24).
859 * 7 0xNN trackIndex(23:16)
860 * 8 0xNN trackIndex (15:8)
861 * 9 0xNN trackIndex (7:0)
862 * 10 0xNN formatID (15:8)
863 * 11 0xNN formatID (7:0)
864 * 12 0xNN time offset from track start, in ms (31:24)
865 * 13 0xNN time offset from track start, in ms (23:16)
866 * 14 0xNN time offset from track start, in ms (15:8)
867 * 15 0xNN time offset from track start, in ms (7:0)
868 * 16 0xNN Checksum
869 *
870 * Returned data is
871 * DescriptorTelegramIndex: 2
872 * pixelformatcode: 1
873 * ImageWidthPixels: 2
874 * ImageHeightPixels: 2
875 * InsetRectangleTopLeftX: 2
876 * InsetRectangleTopLeftY: 2
877 * InsetRectangleBotRightX: 2
878 * InsetRectangleBotRightY: 2
879 * RowSizeInBytes: 4
880 * ImagePixelData:VariableLength
881 * Subsequent packets omit bytes 8 - 24
882 */
883 {
884 unsigned char data[] = {0x04, 0x00, 0x11,
885 0x00, 0x00, /* DescriptorIndex */
886 0x00, /* PixelFormat */
887 0x00, 0x00, /* ImageWidthPixels*/
888 0x00, 0x00, /* ImageHeightPixels*/
889 0x00, 0x00, /* InsetRectangleTopLeftX*/
890 0x00, 0x00, /* InsetRectangleTopLeftY*/
891 0x00, 0x00, /* InsetRectangleBotRightX*/
892 0x00, 0x00, /* InsetRectangleBotRoghtY*/
893 0x00, 0x00, 0x00, 0x00,/* RowSize*/
894 0x00 /* ImagePixelData Var Length*/
895 };
896 iap_send_pkt(data, sizeof(data));
897 break;
898 }
899 case 0x0011: /* RetTrackArtworkData. See Abobe */
900 /* The following is the description for the Apple Firmware
901 *
902 * The iPod sends the requested artwork to the accessory. Multiple
903 * RetTrackArtworkData commands may be necessary to transfer all
904 * the data because it will be too much to fit into a single packet.
905 * This command uses nearly the same format as the SetDisplayImage
906 * command (command 0x0032). The only difference is the addition
907 * of 2 coordinates; they define an inset rectangle that describes
908 * any padding that may have been added to the image. The
909 * coordinates consist of two x,y pairs. Each x or y value is 2
910 * bytes, so the total size of the coordinate set is 8 bytes.
911 *
912 * Byte Value Comment
913 * 0 0xFF Sync byte (required only for UART serial)
914 * 1 0x55 Start of telegram
915 * 2 0xNN Length of packet
916 * 3 0x04 Lingo ID: Extended Interface lingo
917 * 4 0x00 Command ID (bits 15:8)
918 * 5 0x11 Command ID (bits 7:0)
919 * 6 0x00 Descriptor telegram index (15:8).
920 * These fields uniquely identify each packet in the
921 * RetTrackArtworkData transaction. The first telegram
922 * is the descriptor telegram, which always starts with
923 * an index of 0x0000.
924 * 7 0x00 Descriptor telegram index (7:0)
925 * 8 0xNN Display pixel format code.
926 * 9 0xNN Imagewidth in pixels (15:8)
927 * 10 0xNN Image width in pixels (7:0)
928 * 11 0xNN Image height in pixels (15:8)
929 * 12 0xNN Image height in pixels (7:0)
930 * 13 0xNN Inset rectangle, top-left point, x value (15:8)
931 * 14 0xNN Inset rectangle, top-left point, x value (7:0)
932 * 15 0xNN Inset rectangle, top-left point, y value (15:8)
933 * 16 0xNN Inset rectangle, top-left point, y value (7:0)
934 * 17 0xNN Inset rectangle,bottom-rightpoint,xvalue(15:8)
935 * 18 0xNN Inset rectangle,bottom-rightpoint, x value(7:0)
936 * 19 0xNN Inset rectangle,bottom-rightpoint,y value(15:8)
937 * 20 0xNN Inset rectangle, bottom-right point, y value(7:0)
938 * 21 0xNN Rowsize in bytes (31:24)
939 * 22 0xNN Rowsize in bytes (23:16)
940 * 23 0xNN Row size in bytes (15:8)
941 * 24 0xNN Row size in bytes (7:0)
942 * 25-NN 0xNN Image pixel data (variable length)
943 * NN 0xNN Checksum
944 *
945 * In subsequent packets in the sequence, bytes 8 through 24 are
946 * omitted.
947 */
948 {
949 /* We should NEVER receive this command so ERROR if we do */
950 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
951 break;
952 }
953 case 0x0012: /* RequestProtocolVersion */
954 /* The following is the description for the Apple Firmware
955 *
956 * This command is deprecated.
957 *
958 * Requests the version of the running protocol from the iPod.
959 * The iPod responds with a Command 0x0013:ReturnProtocolVersion
960 * command.
961 *
962 * Note: This command requests the Extended Interface protocol
963 * version, not the iPod software version.
964 *
965 * Byte Value Meaning
966 * 0 0xFF Sync byte (required only for UART serial)
967 * 1 0x55 Start of telegram
968 * 2 0x03 Telegram payload length
969 * 3 0x04 Lingo ID: Extended Interface lingo
970 * 4 0x00 Command ID (bits 15:8)
971 * 5 0x12 Command ID (bits 7:0)
972 * 6 0xE7 Telegram payload checksum byte
973 *
974 */
975 {
976 IAP_TX_INIT4(0x04, 0x0013);
977 IAP_TX_PUT(LINGO_MAJOR(0x04));
978 IAP_TX_PUT(LINGO_MINOR(0x04));
979 iap_send_tx();
980 break;
981 }
982 case 0x0013: /* ReturnProtocolVersion. See Above */
983 /* The following is the description for the Apple Firmware
984 * This command is deprecated.
985 * Sent from the iPod to the device
986 * Returns the iPod Extended Interface protocol version number.
987 * The iPod sends this command in response to the Command 0x0012:
988 * RequestProtocolVersion command from the device. The major
989 * version number specifies the protocol version digits to the left
990 * of the decimal point; the minor version number specifies the
991 * digits to the right of the decimal point. For example, a major
992 * version number of 0x01 and a minor version number of 0x08
993 * represents an Extended Interface protocol version of 1.08. This
994 * protocol information is also available through the General lingo
995 * (lingo 0x00) command RequestLingoProtocolVersion when passing
996 * lingo 0x04 as the lingo parameter.
997 *
998 * Note: This command returns the Extended Interface protocol
999 * version, not the iPod software version.
1000 *
1001 * Byte Value Meaning
1002 * 0 0xFF Sync byte (required only for UART serial)
1003 * 1 0x55 Start of telegram
1004 * 2 0x05 Telegram payload length
1005 * 3 0x04 Lingo ID: Extended Interface lingo
1006 * 4 0x00 Command ID (bits 15:8)
1007 * 5 0x13 Command ID (bits 7:0)
1008 * 6 0xNN Protocol major version number
1009 * 7 0xNN Protocol minor version number
1010 * 8 0xNN Telegram payload checksum byte
1011 *
1012 */
1013 {
1014 /* We should NEVER receive this command so ERROR if we do */
1015 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1016 break;
1017 }
1018 case 0x0014: /* RequestiPodName */
1019 /* The following is the description for the Apple Firmware
1020 * This command is deprecated.
1021 * Retrieves the name of the iPod
1022 *
1023 * Returns the name of the user's iPod or 'iPod' if the iPod name
1024 * is undefined. This allows the iPod name to be shown in the
1025 * human-machineinterface(HMI) of the interfacingbody. The iPod
1026 * responds with the Command 0x0015: ReturniPodName command
1027 * containing the iPod name text string.
1028 *
1029 * Byte Value Meaning
1030 * 0 0xFF Sync byte (required only for UART serial)
1031 * 1 0x55 Start of telegram
1032 * 2 0x03 Telegram payload length
1033 * 3 0x04 Lingo ID: Extended Interface lingo
1034 * 4 0x00 Command ID (bits 15:8)
1035 * 5 0x14 Command ID (bits 7:0)
1036 * 6 0xE5 Telegram payload checksum byte
1037 *
1038 * We return ROCKBOX, should this be definable?
1039 * Should it be Volume Name?
1040 */
1041 {
1042 IAP_TX_INIT4(0x04, 0x0015);
1043 IAP_TX_PUT_STRING("ROCKBOX");
1044 iap_send_tx();
1045 break;
1046 }
1047 case 0x0015: /* ReturniPodName. See Above */
1048 /* The following is the description for the Apple Firmware
1049 * This command is deprecated.
1050 * Sent from the iPod to the device
1051 *
1052 * The iPod sends this command in response to the Command 0x0014:
1053 * RequestiPodName telegram from the device. The iPod name is
1054 * encoded as a null-terminated UTF-8 character array. The iPod
1055 * name string is not limited to 252 characters; it may be sent
1056 * in small or large telegram format. The small telegram format
1057 * is shown.
1058 *
1059 * Note: Starting with version 1.07 of the Extended Interface lingo,
1060 * the ReturniPodName command on Windows-formatted iPods returns the
1061 * iTunes name of the iPod instead of the Windows volume name.
1062 *
1063 * Byte Value Meaning
1064 * 0 0xFF Sync byte (required only for UART serial)
1065 * 1 0x55 Start of telegram
1066 * 2 0xNN Telegram payload length
1067 * 3 0x04 Lingo ID: Extended Interface lingo
1068 * 4 0x00 Command ID (bits 15:8)
1069 * 5 0x15 Command ID (bits 7:0)
1070 * 6-N 0xNN iPod name as UTF-8 character array
1071 * NN 0xNN Telegram payload checksum byte
1072 *
1073 */
1074 {
1075 /* We should NEVER receive this command so ERROR if we do */
1076 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1077 break;
1078 }
1079 case 0x0016: /* ResetDBSelection */
1080 /* The following is the description for the Apple Firmware
1081 *
1082 * Resets the current database selection to an empty state and
1083 * invalidates the category entry count that is, sets the count
1084 * to 0, for all categories except the playlist category. This is
1085 * analogous to pressing the Menu button repeatedly to get to the
1086 * topmost iPod HMI menu. Any previously selected database items
1087 * are deselected. The command has no effect on the playback engine
1088 * In response, the iPod sends an ACK telegram with the command
1089 * status. Once the accessory has reset the database selection,
1090 * it must initialize the category count before it can select
1091 * database records. Please refer to Command 0x0018:
1092 * GetNumberCategorizedDBRecords and Command 0x0017:
1093 * SelectDBRecord for details.
1094 *
1095 * Note: Starting with protocol version 1.07, the ResetDBSelection
1096 * command clears the sort order.
1097 *
1098 * Byte Value Meaning
1099 * 0 0xFF Sync byte (required only for UART serial)
1100 * 1 0x55 Start of telegram
1101 * 2 0x03 Telegram payload length
1102 * 3 0x04 Lingo ID: Extended Interface lingo
1103 * 4 0x00 Command ID (bits 15:8)
1104 * 5 0x16 Command ID (bits 7:0)
1105 * 6 0xE3 Telegram payload checksum byte
1106 *
1107 *
1108 * Reset the DB Record Type
1109 * Hierarchy is as follows
1110 * All = 0
1111 * Playlist = 1
1112 * Artist = 2
1113 * Album = 3
1114 * Genre = 4
1115 * Tracks = 5
1116 * Composers = 6
1117 * Audiobooks = 7
1118 * Podcasts = 8
1119 *
1120 */
1121 {
1122 cur_dbrecord[0] = 0;
1123 put_u32(&cur_dbrecord[1],0);
1124 /* respond with cmd ok packet */
1125 cmd_ok(cmd);
1126 break;
1127 }
1128 case 0x0017: /* SelectDBRecord */
1129 /* The following is the description for the Apple Firmware
1130 * Selects one or more records in the Database Engine, based on a
1131 * category relative index. For example, selecting category two
1132 * (artist) and record index one results in a list of selected
1133 * tracks (or database records) from the second artist in the
1134 * artist list.
1135 * Selections are additive and limited by the category hierarchy;
1136 * Subsequent selections are made based on the subset of records
1137 * resulting from the previous selections and not from the entire
1138 * database.
1139 * Note that the selection of a single record automatically passes
1140 * it to the Playback Engine and starts its playback. Record indices
1141 * consist of a 32-bit signed integer. To select database records
1142 * with a specific sort order, use
1143 * Command 0x0038: SelectSortDBRecord
1144 * SelectDBRecord should be called only once a category count has
1145 * been initialized through a call to Command 0x0018:
1146 * GetNumberCategorizedDBRecords. Without a valid category count,
1147 * the SelectDBRecord call cannot select a database record and will
1148 * fail with a command failed ACK. Accessories that make use of
1149 * Command 0x0016: ResetDBSelection must always initialize the
1150 * category count before selecting a new database record using
1151 * SelectDBRecord.
1152 * Accessories should pay close attention to the ACK returned by the
1153 * SelectDBRecord command. Ignoring errors may cause unexpected
1154 * behavior.
1155 * To undo a database selection, send the SelectDBRecord telegram
1156 * with the current category selected in theDatabase Engine and a
1157 * record index of -1 (0xFFFFFFFF). This has the same effect as
1158 * pressing the iPod Menu button once and moves the database
1159 * selection up to the next highest menu level. For example, if a
1160 * device selected artist number three and then album number one,
1161 * it could use the SelectDBRecord(Album, -1) telegram to return
1162 * to the database selection of artist number three. If multiple
1163 * database selections have been made, devices can use any of the
1164 * previously used categories to return to the next highest database
1165 * selection. If the category used in one of these SelectDBRecord
1166 * telegrams has not been used in a previous database selection
1167 * then the command is treated as a no-op.
1168 * Sending a SelectDBRecord telegram with the Track category and a
1169 * record index of -1 is invalid, because the previous database
1170 * selection made with the Track category and a valid index passes
1171 * the database selection to the Playback Engine.
1172 * Sending a SelectDBRecord(Track, -1) telegram returns a parameter
1173 * error. The iPod also returns a bad parameter error ACK when
1174 * devices send the SelectDBRecord telegram with an invalid category
1175 * type, or with the Track category and an index greater than the
1176 * total number of tracks available on the iPod.
1177 *
1178 * Note: Selecting a podcast always selects from the main podcast
1179 * library regardless of the current category context of the iPod.
1180 *
1181 * To immediately go to the topmost iPod menu level and reset all
1182 * database selections, send the ResetDBSelection telegram to the
1183 * iPod.
1184 *
1185 * Byte Value Meaning
1186 * 0 0xFF Sync byte (required only for UART serial)
1187 * 1 0x55 Start of telegram
1188 * 2 0x08 Telegram payload length
1189 * 3 0x04 Lingo ID: Extended Interface lingo
1190 * 4 0x00 Command ID (bits 15:8)
1191 * 5 0x17 Command ID (bits 7:0)
1192 * 6 0xNN Database category type. See
1193 * 7 0xNN Database record index (bits 31:24)
1194 * 8 0xNN Database record index (bits 23:16)
1195 * 9 0xNN Database record index (bits 15:8)
1196 * 10 0xNN Database record index (bits 7:0)
1197 * 11 0xNN Telegram payload checksum byte
1198 *
1199 * The valid database categories are listed below
1200 *
1201 * Category Code Protocol version
1202 * Reserved 0x00 N/A
1203 * Playlist 0x01 1.00
1204 * Artist 0x02 1.00
1205 * Album 0x03 1.00
1206 * Genre 0x04 1.00
1207 * Track 0x05 1.00
1208 * Composer 0x06 1.00
1209 * Audiobook0x07 1.06
1210 * Podcast 0x08 1.08
1211 * Reserved 0x09+ N/A
1212 *
1213 * cur_dbrecord[0] is the record type
1214 * cur_dbrecord[1-4] is the u32 of the record number requested
1215 * which might be a playlist or a track number depending on
1216 * the value of cur_dbrecord[0]
1217 */
1218 {
1219 memcpy(cur_dbrecord, buf + 3, 5);
1220
1221 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
1222 uint32_t index;
1223 uint32_t trackcount;
1224 index = get_u32(&cur_dbrecord[1]);
1225 trackcount = playlist_amount();
1226 if ((cur_dbrecord[0] == 5) && (index > trackcount))
1227 {
1228 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1229 break;
1230 }
1231 if ((cur_dbrecord[0] == 1) && (index > (nbr_total_playlists() + 1)))
1232 {
1233 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1234 break;
1235 }
1236 audio_pause();
1237 switch (cur_dbrecord[0])
1238 {
1239 case 0x01: /* Playlist*/
1240 {
1241 if (index != 0x00) /* 0x00 is the On-The-Go Playlist and
1242 we do nothing with it */
1243 {
1244 last_selected_playlist = index;
1245 audio_skip(-iap_get_trackindex());
1246 seek_to_playlist(last_selected_playlist);
1247 }
1248 break;
1249 }
1250 case 0x02: /* Artist */
1251 case 0x03: /* Album */
1252 case 0x04: /* Genre */
1253 case 0x05: /* Track */
1254 case 0x06: /* Composer */
1255 {
1256 audio_skip(index - playlist_next(0));
1257 break;
1258 }
1259 default:
1260 {
1261 /* We don't do anything with the other selections.
1262 * YET.
1263 */
1264 break;
1265 }
1266 }
1267 if (!paused)
1268 audio_resume();
1269 /* respond with cmd ok packet */
1270 cmd_ok(cmd);
1271 break;
1272 }
1273 case 0x0018: /* GetNumberCategorizedDBRecords */
1274 /* The following is the description for the Apple Firmware
1275 *
1276 * Retrieves the number of records in a particular database
1277 * category.
1278 * For example, a device can get the number of artists or albums
1279 * present in the database. The category types are described above.
1280 * The iPod responds with a Command 0x0019:
1281 * ReturnNumberCategorizedDBRecords telegram indicating the number
1282 * of records present for this category.
1283 * GetNumberCategorizedDBRecords must be called to initialize the
1284 * category count before selecting a database record using Command
1285 * 0x0017: SelectDBRecord or Command 0x0038: SelectSortDBRecord
1286 * commands. A category’s record count can change based on the prior
1287 * categories selected and the database hierarchy. The accessory
1288 * is expected to call GetNumberCategorizedDBRecords in order to
1289 * get the valid range of category entries before selecting a
1290 * record in that category.
1291 *
1292 * Note: The record count returned by this command depends on the
1293 * database state before this command is sent. If the database has
1294 * been reset using Command 0x0016: ResetDBSelection this command
1295 * returns the total number of records for a given category.
1296 * However, if this command is sent after one or more categories
1297 * are selected, the record count is the subset of records that are
1298 * members of all the categories selected prior to this command.
1299 *
1300 * Byte Value Meaning
1301 * 0 0xFF Sync byte (required only for UART serial)
1302 * 1 0x55 Start of telegram
1303 * 2 0x04 Telegram payload length
1304 * 3 0x04 Lingo ID: Extended Interface lingo
1305 * 4 0x00 Command ID (bits 15:8)
1306 * 5 0x18 Command ID (bits 7:0)
1307 * 6 0xNN Database category type. See above
1308 * 7 0xNN Telegram payload checksum byte
1309 *
1310 * This is the actual number of available records for that category
1311 * some head units (Alpine CDE-103BT) use this command before
1312 * requesting records and then hang if not enough records are
1313 * returned.
1314 */
1315 {
1316 unsigned char data[] = {0x04, 0x00, 0x19,
1317 0x00, 0x00, 0x00, 0x00};
1318 switch(buf[3]) /* type number */
1319 {
1320 case 0x01: /* total number of playlists */
1321 dbrecordcount = nbr_total_playlists() + 1;
1322 break;
1323 case 0x05: /* total number of Tracks */
1324 case 0x02: /* total number of Artists */
1325 case 0x03: /* total number of Albums */
1326 /* We don't sort on the above but some Head Units
1327 * require at least one to exist so we just return
1328 * the number of tracks in the playlist. */
1329 dbrecordcount = playlist_amount();
1330 break;
1331 case 0x04: /* total number of Genres */
1332 case 0x06: /* total number of Composers */
1333 case 0x07: /* total number of AudioBooks */
1334 case 0x08: /* total number of Podcasts */
1335 /* We don't support the above so just return that
1336 there are none available. */
1337 dbrecordcount = 0;
1338 break;
1339 }
1340 put_u32(&data[3], dbrecordcount);
1341 iap_send_pkt(data, sizeof(data));
1342 break;
1343 }
1344 case 0x0019: /* ReturnNumberCategorizedDBRecords. See Above */
1345 /* The following is the description for the Apple Firmware
1346 *
1347 * Returns the number of database records matching the specified
1348 * database category. The iPod sends this telegram in response to
1349 * the Command 0x0018: GetNumberCategorizedDBRecords telegram from
1350 * the device. Individual records can then be extracted by sending
1351 * Command 0x001A: RetrieveCategorizedDatabaseRecords to the iPod.
1352 * If no matching database records are found, a record count of
1353 * zero is returned. Category types are described above.
1354 * After selecting the podcast category, the number of artist,
1355 * album, composer, genre, and audiobook records is always zero.
1356 *
1357 * Byte Value Meaning
1358 * 0 0xFF Sync byte (required only for UART serial)
1359 * 1 0x55 Start of telegram
1360 * 2 0x07 Telegram payload length
1361 * 3 0x04 Lingo ID: Extended Interface lingo
1362 * 4 0x00 Command ID (bits 15:8)
1363 * 5 0x19 Command ID (bits 7:0)
1364 * 6 0xNN Database record count (bits 31:24)
1365 * 7 0xNN Database record count (bits 23:16)
1366 * 8 0xNN Database record count (bits 15:8)
1367 * 9 0xNN Database record count (bits 7:0)
1368 * 10 0xNN Telegram payload checksum byte
1369 *
1370 */
1371 {
1372 /* We should NEVER receive this command so ERROR if we do */
1373 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1374 break;
1375 }
1376 case 0x001A: /* RetrieveCategorizedDatabaseRecords */
1377 /* The following is the description for the Apple Firmware
1378 *
1379 * Retrieves one or more database records from the iPod,
1380 * typically based on the results from the Command 0x0018:
1381 * GetNumberCategorizedDBRecords query. The database
1382 * category types are described above. This telegram
1383 * specifies the starting record index and the number of
1384 * records to retrieve (the record count). This allows a device
1385 * to retrieve an individual record or the entire set of records
1386 * for a category. The record start index and record count consist
1387 * of 32-bit signed integers. To retrieve all records from a given
1388 * starting record index, set the record count to -1 (0xFFFFFFFF).
1389 * The iPod responds to this telegram with a separate Command
1390 * 0x001B: ReturnCategorizedDatabaseRecord telegram FOR EACH record
1391 * matching the specified criteria (category and record index
1392 * range).
1393 *
1394 * Byte Value Meaning
1395 * 0 0xFF Sync byte (required only for UART serial)
1396 * 1 0x55 Start of telegram
1397 * 2 0x0C Telegram payload length
1398 * 3 0x04 Lingo ID: Extended Interface lingo
1399 * 4 0x00 Command ID (bits 15:8)
1400 * 5 0x1A Command ID (bits 7:0)
1401 * 6 0xNN Database category type. See above
1402 * 7 0xNN Database record start index (bits 31:24)
1403 * 8 0xNN Database record start index (bits 23:16)
1404 * 9 0xNN Database record start index (bits 15:8)
1405 * 10 0xNN Database record start index (bits 7:0)
1406 * 11 0xNN Database record read count (bits 31:24)
1407 * 12 0xNN Database record read count (bits 23:16)
1408 * 13 0xNN Database record read count (bits 15:8)
1409 * 14 0xNN Database record read count (bits 7:0)
1410 * 15 0xNN Telegram payload checksum byte
1411
1412 * The returned data
1413 * contains information for a single database record. The iPod sends
1414 * one or more of these telegrams in response to the Command 0x001A:
1415 * RetrieveCategorizedDatabaseRecords telegram from the device. The
1416 * category record index is included to allow the device to
1417 * determine which record has been sent. The record data is sent as
1418 * a null-terminated UTF-8 encoded data array.
1419 */
1420 {
1421 unsigned char data[7 + MAX_PATH] =
1422 {0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00,
1423 'O','n','-','T','h','e','-','G','o','\0'};
1424 struct playlist_track_info track;
1425 struct mp3entry id3;
1426
1427 unsigned long start_index = get_u32(&buf[4]);
1428 unsigned long read_count = get_u32(&buf[8]);
1429 unsigned long counter = 0;
1430 unsigned int number_of_playlists = nbr_total_playlists();
1431 uint32_t trackcount;
1432 trackcount = playlist_amount();
1433 size_t len;
1434
1435 if ((buf[3] == 0x05) && ((start_index + read_count ) > trackcount))
1436 {
1437 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1438 break;
1439 }
1440 if ((buf[3] == 0x01) && ((start_index + read_count) > (number_of_playlists + 1)))
1441 {
1442 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1443 break;
1444 }
1445 for (counter=0;counter<read_count;counter++)
1446 {
1447 switch(buf[3]) /* type number */
1448 {
1449 case 0x01: /* Playlists */
1450 get_playlist_name(data +7,start_index+counter, MAX_PATH);
1451 /*Remove file extension*/
1452 char *dot=NULL;
1453 dot = (strrchr(data+7, '.'));
1454 if (dot != NULL)
1455 *dot = '\0';
1456 break;
1457 case 0x05: /* Tracks */
1458 case 0x02: /* Artists */
1459 case 0x03: /* Albums */
1460 case 0x04: /* Genre */
1461 case 0x06: /* Composer */
1462 playlist_get_track_info(NULL, start_index + counter,
1463 &track);
1464 iap_get_trackinfo(start_index + counter, &id3);
1465 switch(buf[3])
1466 {
1467 case 0x05:
1468 len = strlcpy((char *)&data[7], id3.title,64);
1469 break;
1470 case 0x02:
1471 len = strlcpy((char *)&data[7], id3.artist,64);
1472 break;
1473 case 0x03:
1474 len = strlcpy((char *)&data[7], id3.album,64);
1475 break;
1476 case 0x04:
1477 case 0x06:
1478 len = strlcpy((char *)&data[7], "Not Supported",14);
1479 break;
1480 }
1481 break;
1482 }
1483 put_u32(&data[3], start_index+counter);
1484 iap_send_pkt(data, 7 + strlen(data+7) + 1);
1485 yield();
1486 }
1487 break;
1488 }
1489 case 0x001B: /* ReturnCategorizedDatabaseRecord. See Above */
1490 /* The following is the description for the Apple Firmware
1491 *
1492 * Contains information for a single database record. The iPod sends
1493 * ONE OR MORE of these telegrams in response to the Command 0x001A:
1494 * RetrieveCategorizedDatabaseRecords telegram from the device. The
1495 * category record index is included to allow the device to
1496 * determine which record has been sent. The record data is sent
1497 * as a null-terminated UTF-8 encoded data array.
1498 *
1499 * Note: The database record string is not limited to 252 characters
1500 * it may be sent in small or large telegram format, depending on
1501 * the record size. The small telegram format is shown.
1502 *
1503 * Byte Value Meaning
1504 * 0 0xFF Sync byte (required only for UART serial)
1505 * 1 0x55 Start of telegram
1506 * 2 0xNN Telegram payload length
1507 * 3 0x04 Lingo ID: Extended Interface lingo
1508 * 4 0x00 Command ID (bits 15:8)
1509 * 5 0x1B Command ID (bits 7:0)
1510 * 6 0xNN Database record category index (bits 31:24)
1511 * 7 0xNN Database record category index (bits 23:16)
1512 * 8 0xNN Database record category index (bits 15:8)
1513 * 9 0xNN Database record category index (bits 7:0)
1514 * 10-N 0xNN Database record as a UTF-8 character array.
1515 * NN 0xNN Telegram payload checksum byte
1516 *
1517 */
1518 {
1519 /* We should NEVER receive this command so ERROR if we do */
1520 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1521 break;
1522 }
1523 case 0x001C: /* GetPlayStatus */
1524 /* The following is the description for the Apple Firmware
1525 *
1526 * Requests the current iPod playback status, allowing the
1527 * device to display feedback to the user. In response, the
1528 * iPod sends a Command 0x001D: ReturnPlayStatus telegram
1529 * with the current playback status.
1530 *
1531 * Byte Value Meaning
1532 * 0 0xFF Sync byte (required only for UART serial)
1533 * 1 0x55 Start of telegram
1534 * 2 0x03 Telegram payload length
1535 * 3 0x04 Lingo ID: Extended Interface lingo
1536 * 4 0x00 Command ID (bits 15:8)
1537 * 5 0x1C Command ID (bits 7:0)
1538 * 6 0xDD Telegram payload checksum byte
1539 *
1540 */
1541 {
1542 unsigned char data[] = {0x04, 0x00, 0x1D,
1543 0x00, 0x00, 0x00, 0x00,
1544 0x00, 0x00, 0x00, 0x00,
1545 0x00};
1546 struct mp3entry *id3 = audio_current_track();
1547 unsigned long time_total = id3->length;
1548 unsigned long time_elapsed = id3->elapsed;
1549 int status = audio_status();
1550 put_u32(&data[3], time_total);
1551 put_u32(&data[7], time_elapsed);
1552 if (status == AUDIO_STATUS_PLAY)
1553 data[11] = 0x01; /* play */
1554 else if (status & AUDIO_STATUS_PAUSE)
1555 data[11] = 0x02; /* pause */
1556 iap_send_pkt(data, sizeof(data));
1557 break;
1558 }
1559 case 0x001D: /* ReturnPlayStatus. See Above */
1560 /* The following is the description for the Apple Firmware
1561 *
1562 * Returns the current iPod playback status. The iPod sends this
1563 * telegram in response to the Command 0x001C: GetPlayStatus
1564 * telegram from the device. The information returned includes the
1565 * current track length, track position, and player state.
1566 *
1567 * Note: The track length and track position fields are valid only
1568 * if the player state is Playing or Paused. For other player
1569 * states, these fields should be ignored.
1570 *
1571 * Byte Value Meaning
1572 * 0 0xFF Sync byte (required only for UART serial)
1573 * 1 0x55 Start of telegram
1574 * 2 0x0C Telegram payload length
1575 * 3 0x04 Lingo ID: Extended Interface lingo
1576 * 4 0x00 Command ID (bits 15:8)
1577 * 5 0x1D Command ID (bits 7:0)
1578 * 6 0xNN Track length in milliseconds (bits 31:24)
1579 * 7 0xNN Track length in milliseconds (bits 23:16)
1580 * 8 0xNN Track length in milliseconds (bits 15:8)
1581 * 9 0xNN Track length in milliseconds (bits 7:0)
1582 * 10 0xNN Track position in milliseconds (bits 31:24)
1583 * 11 0xNN Track position in milliseconds (bits 23:16)
1584 * 12 0xNN Track position in milliseconds (bits 15:8)
1585 * 13 0xNN Track position in milliseconds (bits 7:0)
1586 * 14 0xNN Player state. Possible values are:
1587 * 0x00 = Stopped
1588 * 0x01 = Playing
1589 * 0x02 = Paused
1590 * 0x03 - 0xFE = Reserved
1591 * 0xFF = Error
1592 * 15 0xNN Telegram payload checksum byte
1593 *
1594 */
1595 {
1596 /* We should NEVER receive this command so ERROR if we do */
1597 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1598 break;
1599 }
1600 case 0x001E: /* GetCurrentPlayingTrackIndex */
1601 /* The following is the description for the Apple Firmware
1602 *
1603 * Requests the playback engine index of the currently playing
1604 * track. In response, the iPod sends a Command 0x001F:
1605 * ReturnCurrentPlayingTrackIndex telegram to the device.
1606 *
1607 * Note: The track index returned is valid only if there is
1608 * currently a track playing or paused.
1609 *
1610 * Byte Value Meaning
1611 * 0 0xFF Sync byte (required only for UART serial)
1612 * 1 0x55 Start of telegram
1613 * 2 0x03 Telegram payload length
1614 * 3 0x04 Lingo ID: Extended Interface lingo
1615 * 4 0x00 Command ID (bits 15:8)
1616 * 5 0x1E Command ID (bits 7:0)
1617 * 6 0xDB Telegram payload checksum byte
1618 *
1619 */
1620 {
1621 unsigned char data[] = {0x04, 0x00, 0x1F,
1622 0xFF, 0xFF, 0xFF, 0xFF};
1623 long playlist_pos = playlist_next(0);
1624 int status = audio_status();
1625 playlist_pos -= playlist_get_first_index(NULL);
1626 if(playlist_pos < 0)
1627 playlist_pos += playlist_amount();
1628 if ((status == AUDIO_STATUS_PLAY) || (status & AUDIO_STATUS_PAUSE))
1629 put_u32(&data[3], playlist_pos);
1630 iap_send_pkt(data, sizeof(data));
1631 break;
1632 }
1633 case 0x001F: /* ReturnCurrentPlayingTrackIndex. See Above */
1634 /* The following is the description for the Apple Firmware
1635 *
1636 * Returns the playback engine index of the current playing track in
1637 * response to the Command 0x001E: GetCurrentPlayingTrackIndex
1638 * telegram from the device. The track index is a 32-bit signed
1639 * integer.
1640 * If there is no track currently playing or paused, an index of -1
1641 * (0xFFFFFFFF) is returned.
1642 *
1643 * Byte Value Meaning
1644 * 0 0xFF Sync byte (required only for UART serial)
1645 * 1 0x55 Start of telegram
1646 * 2 0x07 Telegram payload length
1647 * 3 0x04 Lingo ID: Extended Interface lingo
1648 * 4 0x00 Command ID (bits 15:8)
1649 * 5 0x1F Command ID (bits 7:0)
1650 * 6 0xNN Playback track index (bits 31:24)
1651 * 7 0xNN Playback track index (bits 23:16)
1652 * 8 0xNN Playback track index (bits 15:8)
1653 * 9 0xNN Playback track index (bits 7:0)
1654 * 10 0xNN Telegram payload checksum byte
1655 *
1656 */
1657 {
1658 /* We should NEVER receive this command so ERROR if we do */
1659 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1660 break;
1661 }
1662 case 0x0020: /* GetIndexedPlayingTrackTitle. See 0x0024 below */
1663 /* The following is the description for the Apple Firmware
1664 *
1665 * Requests the title name of the indexed playing track from the
1666 * iPod. In response to a valid telegram, the iPod sends a
1667 * Command 0x0021: ReturnIndexedPlayingTrackTitle telegram to the
1668 * device.
1669 *
1670 * Note: If the telegram length or playing track index is invalid,
1671 * the iPod responds with an ACK telegram including the specific
1672 * error status.
1673 *
1674 * Byte Value Meaning
1675 * 0 0xFF Sync byte (required only for UART serial)
1676 * 1 0x55 Start of telegram
1677 * 2 0x07 Telegram payload length
1678 * 3 0x04 Lingo ID: Extended Interface lingo
1679 * 4 0x00 Command ID (bits 15:8)
1680 * 5 0x20 Command ID (bits 7:0)
1681 * 6 0xNN Playback track index (bits 31:24)
1682 * 7 0xNN Playback track index (bits 23:16)
1683 * 8 0xNN Playback track index (bits 15:8)
1684 * 9 0xNN Playback track index (bits 7:0)
1685 * 10 0xNN Telegram payload checksum byte
1686 *
1687 */
1688 case 0x0021: /* ReturnIndexedPlayingTrackTitle. See 0x0024 Below */
1689 /* The following is the description for the Apple Firmware
1690 *
1691 * Returns the title of the indexed playing track in response to
1692 * a valid Command 0x0020 GetIndexedPlayingTrackTitle telegram from
1693 * the device. The track title is encoded as a null-terminated UTF-8
1694 * character array.
1695 *
1696 * Note: The track title string is not limited to 252 characters;
1697 * it may be sent in small or large telegram format, depending on
1698 * the string length. The small telegram format is shown.
1699 *
1700 * Byte Value Meaning
1701 * 0 0xFF Sync byte (required only for UART serial)
1702 * 1 0x55 Start of telegram
1703 * 2 0xNN Telegram payload length
1704 * 3 0x04 Lingo ID: Extended Interface lingo
1705 * 4 0x00 Command ID (bits 15:8)
1706 * 5 0x21 Command ID (bits 7:0)
1707 * 6-N 0xNN Track title as a UTF-8 character array
1708 * NN 0xNN Telegram payload checksum byte
1709 *
1710 */
1711 case 0x0022: /* GetIndexedPlayingTrackArtistName. See 0x0024 Below */
1712 /* The following is the description for the Apple Firmware
1713 *
1714 * Requests the name of the artist of the indexed playing track
1715 * In response to a valid telegram, the iPod sends a
1716 * Command 0x0023: ReturnIndexedPlayingTrackArtistName telegram to
1717 * the device.
1718 *
1719 * Note: If the telegram length or playing track index is invalid,
1720 * the iPod responds with an ACK telegram including the specific
1721 * error status.
1722 *
1723 * Byte Value Meaning
1724 * 0 0xFF Sync byte (required only for UART serial)
1725 * 1 0x55 Start of telegram
1726 * 2 0x07 Telegram payload length
1727 * 3 0x04 Lingo ID: Extended Interface lingo
1728 * 4 0x00 Command ID (bits 15:8)
1729 * 5 0x22 Command ID (bits 7:0)
1730 * 6 0xNN Playback track index (bits 31:24)
1731 * 7 0xNN Playback track index (bits 23:16)
1732 * 8 0xNN Playback track index (bits 15:8)
1733 * 9 0xNN Playback track index (bits 7:0)
1734 * 10 0xNN Telegram payload checksum byte
1735 *
1736 */
1737 case 0x0023: /* ReturnIndexedPlayingTrackArtistName. See 0x0024 Below */
1738 /* The following is the description for the Apple Firmware
1739 *
1740 * Returns the artist name of the indexed playing track in response
1741 * to a valid Command 0x0022 GetIndexedPlayingTrackArtistName
1742 * telegram from the device. The track artist name is encoded as a
1743 * null-terminated UTF-8 character array.
1744 *
1745 * Note: The artist name string is not limited to 252 characters;
1746 * it may be sent in small or large telegram format, depending on
1747 * the string length. The small telegram format is shown.
1748 *
1749 * Byte Value Meaning
1750 * 0 0xFF Sync byte (required only for UART serial)
1751 * 1 0x55 Start of telegram
1752 * 2 0xNN Telegram payload length
1753 * 3 0x04 Lingo ID: Extended Interface lingo
1754 * 4 0x00 Command ID (bits 15:8)
1755 * 5 0x23 Command ID (bits 7:0)
1756 * 6-N 0xNN Track Artist as a UTF-8 character array
1757 * NN 0xNN Telegram payload checksum byte
1758 *
1759 */
1760 case 0x0024: /* GetIndexedPlayingTrackAlbumName AND
1761 * GetIndexedPlayingTrackTitle AND
1762 * AND GetIndexedPlayingTrackArtistName. */
1763 /* The following is the description for the Apple Firmware
1764 *
1765 * Requests the album name of the indexed playing track
1766 * In response to a valid telegram, the iPod sends a
1767 * Command 0x0025: ReturnIndexedPlayingTrackAlbumName telegram to
1768 * the device.
1769 *
1770 * Note: If the telegram length or playing track index is invalid,
1771 * the iPod responds with an ACK telegram including the specific
1772 * error status.
1773 *
1774 * Byte Value Meaning
1775 * 0 0xFF Sync byte (required only for UART serial)
1776 * 1 0x55 Start of telegram
1777 * 2 0x07 Telegram payload length
1778 * 3 0x04 Lingo ID: Extended Interface lingo
1779 * 4 0x00 Command ID (bits 15:8)
1780 * 5 0x24 Command ID (bits 7:0)
1781 * 6 0xNN Playback track index (bits 31:24)
1782 * 7 0xNN Playback track index (bits 23:16)
1783 * 8 0xNN Playback track index (bits 15:8)
1784 * 9 0xNN Playback track index (bits 7:0)
1785 * 10 0xNN Telegram payload checksum byte
1786 *
1787 */
1788 {
1789 unsigned char data[70] = {0x04, 0x00, 0xFF};
1790 struct mp3entry id3;
1791 int fd;
1792 size_t len;
1793 long tracknum = get_u32(&buf[3]);
1794
1795 data[2] = cmd + 1;
1796 memcpy(&id3, audio_current_track(), sizeof(id3));
1797 tracknum += playlist_get_first_index(NULL);
1798 if(tracknum >= playlist_amount())
1799 tracknum -= playlist_amount();
1800 /* If the tracknumber is not the current one,
1801 read id3 from disk */
1802 if(playlist_next(0) != tracknum)
1803 {
1804 struct playlist_track_info info;
1805 playlist_get_track_info(NULL, tracknum, &info);
1806 fd = open(info.filename, O_RDONLY);
1807 memset(&id3, 0, sizeof(struct mp3entry));
1808 get_metadata(&id3, fd, info.filename);
1809 close(fd);
1810 }
1811 /* Return the requested track data */
1812 switch(cmd)
1813 {
1814 case 0x20:
1815 len = strlcpy((char *)&data[3], id3.title, 64);
1816 iap_send_pkt(data, 4+len);
1817 break;
1818 case 0x22:
1819 len = strlcpy((char *)&data[3], id3.artist, 64);
1820 iap_send_pkt(data, 4+len);
1821 break;
1822 case 0x24:
1823 len = strlcpy((char *)&data[3], id3.album, 64);
1824 iap_send_pkt(data, 4+len);
1825 break;
1826 }
1827 break;
1828 }
1829 case 0x0025: /* ReturnIndexedPlayingTrackAlbumName. See 0x0024 Above */
1830 /* The following is the description for the Apple Firmware
1831 *
1832 * Returns the album name of the indexed playing track in response
1833 * to a valid Command 0x0024 GetIndexedPlayingTrackAlbumName
1834 * telegram from the device. The track artist name is encoded as a
1835 * null-terminated UTF-8 character array.
1836 *
1837 * Note: The album name string is not limited to 252 characters;
1838 * it may be sent in small or large telegram format, depending on
1839 * the string length. The small telegram format is shown.
1840 *
1841 * Byte Value Meaning
1842 * 0 0xFF Sync byte (required only for UART serial)
1843 * 1 0x55 Start of telegram
1844 * 2 0xNN Telegram payload length
1845 * 3 0x04 Lingo ID: Extended Interface lingo
1846 * 4 0x00 Command ID (bits 15:8)
1847 * 5 0x25 Command ID (bits 7:0)
1848 * 6-N 0xNN Track Album as a UTF-8 character array
1849 * NN 0xNN Telegram payload checksum byte
1850 *
1851 */
1852 {
1853 /* We should NEVER receive this command so ERROR if we do */
1854 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
1855 break;
1856 }
1857 case 0x0026: /* SetPlayStatusChangeNotification */
1858 /* The following is the description for the Apple Firmware
1859 * Sets the state of play status change notifications from the iPod
1860 * to the device. Notification of play status changes can be
1861 * globally enabled or disabled. If notifications are enabled, the
1862 * iPod sends a Command 0x0027: PlayStatusChangeNotification
1863 * telegram to the device each time the play status changes, until
1864 * the device sends this telegram again with the disable
1865 * notification option. In response, the iPod sends an ACK telegram
1866 * indicating the status of the SetPlayStatusChangeNotification
1867 * command.
1868 *
1869 * Byte Value Meaning
1870 * 0 0xFF Sync byte (required only for UART serial)
1871 * 1 0x55 Start of telegram
1872 * 2 0x04 Telegram payload length
1873 * 3 0x04 Lingo ID: Extended Interface lingo
1874 * 4 0x00 Command ID (bits 15:8)
1875 * 5 0x26 Command ID (bits 7:0)
1876 * 6 0xNN The state of play status change notifications.
1877 * Possible values are:
1878 * 0x00 = Disable all change notification
1879 * 0x01 = Enable all change notification
1880 * 0x02 - 0xFF = Reserved
1881 * 7 0xNN Telegram payload checksum byte
1882 */
1883 {
1884 device.do_notify = buf[3] ? true : false;
1885 /* respond with cmd ok packet */
1886 cmd_ok(cmd);
1887 break;
1888 }
1889 case 0x0027: /* PlayStatusChangeNotification */
1890 /* This response is handled by iap_track_changed() and iap_periodic()
1891 * within iap-core.c
1892 * The following is the description for the Apple Firmware
1893 *
1894 * The iPod sends this telegram to the device when the iPod play status
1895 * changes, if the device has previously enabled notifications using
1896 * Command 0x0026: SetPlayStatusChangeNotification . This telegram
1897 * contains details about the new play status. Notification telegrams
1898 * for changes in track position occur approximately every 500
1899 * milliseconds while the iPod is playing. Notification telegrams are
1900 * sent from the iPod until the device sends the
1901 * SetPlayStatusChangeNotification telegram with the option to disable
1902 * all notifications.
1903 * Some notifications include additional data about the new play status.
1904 *
1905 * PlayStatusChangeNotification telegram: notifications 0x00, 0x02, 0x03
1906 * Byte Value Meaning
1907 * 0 0xFF Sync byte (required only for UART serial)
1908 * 1 0x55 Start of telegram
1909 * 2 0x04 Telegram payload length
1910 * 3 0x04 Lingo ID: Extended Interface lingo
1911 * 4 0x00 Command ID (bits 15:8)
1912 * 5 0x27 Command ID (bits 7:0)
1913 * 6 0xNN New play status. See Below.
1914 * 7 0xNN Telegram payload checksum byte
1915 *
1916 * Play Status Change Code Extended Status Data (if
1917 * any)
1918 * Playback stopped 0x00 None
1919 * Playback track changed 0x01 New track record index
1920 * (32 bits)
1921 * Playback forward seek stop 0x02 None
1922 * Playback backward seek stop 0x03 None
1923 * Playback track position 0x04 New track position in
1924 * milliseconds (32 bits)
1925 * Playback chapter changed 0x05 New chapter index (32
1926 * bits)
1927 * Reserved 0x06 - 0xFF N/A
1928 *
1929 * Playback track changed (code 0x01)
1930 * Byte Value Meaning
1931 * 0 0xFF Sync byte (required only for UART serial)
1932 * 1 0x55 Start of telegram
1933 * 2 0x08 Telegram payload length
1934 * 3 0x04 Lingo ID: Extended Interface lingo
1935 * 4 0x00 Command ID (bits 15:8)
1936 * 5 0x27 Command ID (bits 7:0)
1937 * 6 0x01 New status: Playback track changed
1938 * 7 0xNN New playback track index (bits 31:24)
1939 * 8 0xNN New playback track index (bits 23:16)
1940 * 9 0xNN New playback track index (bits 15:8)
1941 * 10 0xNN New playback track index (bits 7:0)
1942 * 11 0xNN Telegram payload checksum byte
1943 *
1944 * Playback track position changed (code 0x04)
1945 * Byte Value Meaning
1946 * 0 0xFF Sync byte (required only for UART serial)
1947 * 1 0x55 Start of telegram
1948 * 2 0x08 Telegram payload length
1949 * 3 0x04 Lingo ID: Extended Interface lingo
1950 * 4 0x00 Command ID (bits 15:8)
1951 * 5 0x27 Command ID (bits 7:0)
1952 * 6 0x04 New status: Playback track position changed
1953 * 7 0xNN New track position in milliseconds (bits 31:24)
1954 * 8 0xNN New track position in milliseconds (bits 23:16)
1955 * 9 0xNN New track position in milliseconds (bits 15:8)
1956 * 10 0xNN New track position in milliseconds (bits 7:0)
1957 * 11 0xNN Telegram payload checksum byte
1958 *
1959 * Playback chapter changed (code 0x05)
1960 * Byte Value Meaning
1961 * 0 0xFF Sync byte (required only for UART serial)
1962 * 1 0x55 Start of telegram
1963 * 2 0x08 Telegram payload length
1964 * 3 0x04 Lingo ID: Extended Interface lingo
1965 * 4 0x00 Command ID (bits 15:8)
1966 * 5 0x27 Command ID (bits 7:0)
1967 * 6 0x05 New status: Playback chapter changed
1968 * 7 0xNN New track chapter index (bits 31:24)
1969 * 8 0xNN New track chapter index (bits 23:16)
1970 * 9 0xNN New track chapter index (bits 15:8)
1971 * 10 0xNN New track chapter index (bits 7:0)
1972 * 11 0xNN Telegram payload checksum byte
1973 *
1974 */
1975 case 0x0028: /* PlayCurrentSelection */
1976 /* The following is the description for the Apple Firmware
1977 *
1978 * Requests playback of the currently selected track or list of
1979 * tracks. The currently selected tracks are placed in the Now
1980 * Playing playlist, where they are optionally shuffled (if the
1981 * shuffle feature is enabled). Finally, the specified track record
1982 * index is passed to the player to play. Note that if the track
1983 * index is -1(0xFFFFFFFF), the first track after the shuffle is
1984 * complete is played first. If a track index of n is sent, the nth
1985 * track of the selected tracks is played first, regardless of
1986 * where it is located in the Now Playing playlist after the shuffle
1987 * is performed (assuming that shuffle is on). In response, the iPod
1988 * sends an ACK telegram indicating the status of the command.
1989 *
1990 * Byte Value Meaning
1991 * 0 0xFF Sync byte (required only for UART serial)
1992 * 1 0x55 Start of telegram
1993 * 2 0x07 Telegram payload length
1994 * 3 0x04 Lingo ID: Extended Interface lingo
1995 * 4 0x00 Command ID (bits 15:8)
1996 * 5 0x28 Command ID (bits 7:0)
1997 * 6 0xNN Selection track record index (bits 31:24)
1998 * 7 0xNN Selection track record index (bits 23:16)
1999 * 8 0xNN Selection track record index (bits 15:8)
2000 * 9 0xNN Selection track record index (bits 7:0)
2001 * 10 0xNN Telegram payload checksum byte
2002 *
2003 */
2004 {
2005 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
2006 uint32_t index;
2007 uint32_t trackcount;
2008 index = get_u32(&buf[3]);
2009 trackcount = playlist_amount();
2010 if (index >= trackcount)
2011 {
2012 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2013 break;
2014 }
2015 audio_pause();
2016 if(global_settings.playlist_shuffle)
2017 {
2018 playlist_randomise(NULL, current_tick, true);
2019 }
2020 else
2021 {
2022 playlist_sort(NULL, true);
2023 }
2024 audio_skip(index - playlist_next(0));
2025 if (!paused)
2026 audio_resume();
2027 /* respond with cmd ok packet */
2028 cmd_ok(cmd);
2029 break;
2030 }
2031 case 0x0029: /* PlayControl */
2032 {
2033 /* The following is the description for the Apple Firmware
2034 *
2035 * Sets the new play state of the iPod. The play control command
2036 * codes are shown below. In response, the iPod sends an ACK
2037 * telegram indicating the status of the command.
2038 *
2039 * Note: If a remote device requests the Next or Previous Chapter
2040 * for a track that does not contain chapters, the iPod returns a
2041 * command failed ACK.
2042 *
2043 * Byte Value Meaning
2044 * 0 0xFF Sync byte (required only for UART serial)
2045 * 1 0x55 Start of telegram
2046 * 2 0x04 Telegram payload length
2047 * 3 0x04 Lingo ID: Extended Interface lingo
2048 * 4 0x00 Command ID (bits 15:8)
2049 * 5 0x29 Command ID (bits 7:0)
2050 * 6 0xNN Play control command code.
2051 * 7 0xNN Telegram payload checksum byte
2052 *
2053 * Play Control Command Code Protocol version
2054 * Reserved 0x00 N/A
2055 * Toggle Play/Pause 0x01 1.00
2056 * Stop 0x02 1.00
2057 * Next Track 0x03 1.00
2058 * Previous Track 0x04 1.00
2059 * StartFF 0x05 1.00
2060 * StartRew 0x06 1.00
2061 * EndFFRew 0x07 1.00
2062 * NextChapter 0x08 1.06
2063 * Previous Chapter 0x09 1.06
2064 * Reserved 0x0A - 0xFF
2065 *
2066 */
2067 switch(buf[3])
2068 {
2069 case 0x01: /* play/pause */
2070 iap_remotebtn = BUTTON_RC_PLAY;
2071 iap_repeatbtn = 2;
2072 break;
2073 case 0x02: /* stop */
2074 iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT;
2075 iap_repeatbtn = 2;
2076 break;
2077 case 0x03: /* skip++ */
2078 iap_remotebtn = BUTTON_RC_RIGHT;
2079 iap_repeatbtn = 2;
2080 break;
2081 case 0x04: /* skip-- */
2082 iap_remotebtn = BUTTON_RC_LEFT;
2083 iap_repeatbtn = 2;
2084 break;
2085 case 0x05: /* ffwd */
2086 iap_remotebtn = BUTTON_RC_RIGHT;
2087 break;
2088 case 0x06: /* frwd */
2089 iap_remotebtn = BUTTON_RC_LEFT;
2090 break;
2091 case 0x07: /* end ffwd/frwd */
2092 iap_remotebtn = BUTTON_NONE;
2093 break;
2094 }
2095 /* respond with cmd ok packet */
2096 cmd_ok(cmd);
2097 break;
2098 }
2099 case 0x002A: /* GetTrackArtworkTimes */
2100 /* The following is the description for the Apple Firmware
2101 *
2102 * The device sends this command to the iPod to request the list of
2103 * artwork time locations for a track. A 4-byte track Index
2104 * specifies which track from the Playback Engine is to be selected.
2105 * A 2-byte formatID indicates which type of artwork is desired.
2106 * The 2-byte artworkIndex specifies at which index to begin
2107 * searching for artwork. A value of 0 indicates that the iPod
2108 * should start with the first available artwork.
2109 * The 2-byte artworkCount specifies the maximum number of times
2110 * (artwork locations) to be returned. A value of -1 (0xFFFF)
2111 * indicates that there is no preferred limit. Note that podcasts
2112 * may have a large number of associated images.
2113 *
2114 * Byte Value Comment
2115 * 0 0xFF Sync byte (required only for UART serial)
2116 * 1 0x55 Start of telegram
2117 * 2 0x0D Length of packet
2118 * 3 0x04 Lingo ID: Extended Interface lingo
2119 * 4 0x00 Command ID (bits 15:8)
2120 * 5 0x2A Command ID (bits 7:0)
2121 * 6 0xNN trackIndex(31:24)
2122 * 7 0xNN trackIndex(23:16)
2123 * 8 0xNN trackIndex (15:8)
2124 * 9 0xNN trackIndex (7:0)
2125 * 10 0xNN formatID (15:8)
2126 * 11 0xNN formatID (7:0)
2127 * 12 0xNN artworkIndex (15:8)
2128 * 13 0xNN artworkIndex (7:0)
2129 * 14 0xNN artworkCount (15:8)
2130 * 15 0xNN artworkCount (7:0)
2131 * 16 0xNN Checksum
2132 *
2133 */
2134 {
2135 unsigned char data[] = {0x04, 0x00, 0x2B,
2136 0x00, 0x00, 0x00, 0x00};
2137 iap_send_pkt(data, sizeof(data));
2138 break;
2139 }
2140 case 0x002B: /* ReturnTrackArtworkTimes. See Above */
2141 /* The following is the description for the Apple Firmware
2142 *
2143 * The iPod sends this command to the device to return the list of
2144 * artwork times for a given track. The iPod returns zero or more
2145 * 4-byte times, one for each piece of artwork associated with the
2146 * track and format specified by GetTrackArtworkTimes.
2147 * The number of records returned will be no greater than the number
2148 * specified in the GetTrackArtworkTimes command. It may however, be
2149 * less than requested. This can happen if there are fewer pieces of
2150 * artwork available than were requested, or if the iPod is unable
2151 * to place the full number in a single packet. Check the number of
2152 * records returned against the results of
2153 * RetIndexedPlayingTrackInfo with infoType 7 to ensure that all
2154 * artwork has been received.
2155 *
2156 * Byte Value Comment
2157 * 0 0xFF Sync byte (required only for UART serial)
2158 * 1 0x55 Start of telegram
2159 * 2 0xNN Length of packet
2160 * 3 0x04 Lingo ID: Extended Interface lingo
2161 * 4 0x00 Command ID (bits 15:8)
2162 * 5 0x2B Command ID (bits 7:0)
2163 * 6 0xNN time offset from track start in ms (31:24)
2164 * 7 0xNN time offset from track start in ms (23:16)
2165 * 8 0xNN time offset from track start in ms (15:8)
2166 * 9 0xNN time offset from track start in ms (7:0)
2167 * Preceding 4 bytes may be repeated NN times
2168 * NN 0xNN Checksum
2169 *
2170 */
2171 {
2172 /* We should NEVER receive this command so ERROR if we do */
2173 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2174 break;
2175 }
2176 case 0x002C: /* GetShuffle */
2177 {
2178 /* The following is the description for the Apple Firmware
2179 *
2180 * Requests the current state of the iPod shuffle setting. The iPod
2181 * responds with the Command0x002D: ReturnShuffle telegram.
2182 *
2183 * Byte Value Meaning
2184 * 0 0xFF Sync byte (required only for UART serial)
2185 * 1 0x55 Start of telegram
2186 * 2 0x03 Telegram payload length
2187 * 3 0x04 Lingo ID: Extended Interface lingo
2188 * 4 0x00 Command ID (bits 15:8)
2189 * 5 0x2C Command ID (bits 7:0)
2190 * 6 0xCD Telegram payload checksum byte
2191 *
2192 */
2193 unsigned char data[] = {0x04, 0x00, 0x2D,
2194 0x00};
2195 data[3] = global_settings.playlist_shuffle ? 1 : 0;
2196 iap_send_pkt(data, sizeof(data));
2197 break;
2198 }
2199 case 0x002D: /* ReturnShuffle. See Above */
2200 /* The following is the description for the Apple Firmware
2201 *
2202 * Returns the current state of the shuffle setting. The iPod sends
2203 * this telegram in response to the Command 0x002C: GetShuffle
2204 * telegram from the device.
2205 *
2206 * Byte Value Meaning
2207 * 0 0xFF Sync byte (required only for UART serial)
2208 * 1 0x55 Start of telegram
2209 * 2 0x04 Telegram payload length
2210 * 3 0x04 Lingo ID: Extended Interface lingo
2211 * 4 0x00 Command ID (bits 15:8)
2212 * 5 0x2D Command ID (bits 7:0)
2213 * 6 0xNN Shuffle mode. See Below.
2214 * 7 0xNN Telegram payload checksum byte
2215 *
2216 * Possible values of the shufflemode.
2217 * Value Meaning
2218 * 0x00 Shuffle off
2219 * 0x01 Shuffle tracks
2220 * 0x02 Shuffle albums
2221 * 0x03 – 0xFF Reserved
2222 *
2223 */
2224 {
2225 /* We should NEVER receive this command so ERROR if we do */
2226 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2227 break;
2228 }
2229 case 0x002E: /* SetShuffle */
2230 {
2231 /* The following is the description for the Apple Firmware
2232 *
2233 * Sets the iPod shuffle mode. The iPod shuffle modes are listed
2234 * below. In response, the iPod sends an ACK telegram with the
2235 * command status.
2236 * This telegram has an optional byte, byte 0x07, called the
2237 * RestoreonExit byte. This byte can be used to restore the
2238 * original shuffle setting in use when the accessory was attached
2239 * to the iPod. A non zero value restores the original shuffle
2240 * setting of the iPod when the accessory is detached. If this byte
2241 * is zero, the shuffle setting set by the accessory overwrites the
2242 * original setting and persists after the accessory is detached
2243 * from the iPod.
2244 * Accessory engineers should note that the shuffle mode affects
2245 * items only in the playback engine. The shuffle setting does not
2246 * affect the order of tracks in the database engine, so calling
2247 * Command 0x001A: RetrieveCategorizedDatabaseRecords on a database
2248 * selection with the shuffle mode set returns a list of unshuffled
2249 * tracks. To get the shuffled playlist, an accessory must query the
2250 * playback engine by calling Command 0x0020
2251 * GetIndexedPlayingTrackTitle.
2252 * Shuffling tracks does not affect the track index, just the track
2253 * at that index. If an unshuffled track at playback index 1 is
2254 * shuffled, a new track is placed into index 1. The playback
2255 * indexes themselves are not shuffled.
2256 * When shuffle mode is enabled, tracks that are marked 'skip when
2257 * shuffling' are filtered from the database selection. This affects
2258 * all audiobooks and all tracks that the user has marked in iTunes.
2259 * It also affects all podcasts unless their default 'skip when
2260 * shuffling' markings have been deliberately removed. To apply the
2261 * filter to the playback engine, the accessory should send
2262 * Command 0x0017: SelectDBRecord or
2263 * Command 0x0028: PlayCurrentSelection after enabling shuffle mode.
2264 *
2265 * Note: Accessory developers are encouraged to always use the
2266 * Restore on Exit byte with a nonzero value to restore any settings
2267 * modified by the accessory upon detach.
2268 *
2269 * SetShuffle telegram with Restore on Exit byte
2270 * Byte Value Meaning
2271 * 0 0xFF Sync byte (required only for UART serial)
2272 * 1 0x55 Start of telegram
2273 * 2 0x05 Telegram payload length
2274 * 3 0x04 Lingo ID: Extended Interface lingo
2275 * 4 0x00 Command ID (bits 15:8)
2276 * 5 0x2E Command ID (bits 7:0)
2277 * 6 0xNN New shuffle mode. See above .
2278 * 7 0xNN Restore on Exit byte. If 1, the orig setting is
2279 * restored on detach; if 0, the newsetting persists
2280 * after accessory detach.
2281 * 8 0xNN Telegram payload checksum byte
2282 *
2283 * SetShuffle setting persistent after the accessory detach.
2284 * Byte Value Meaning
2285 * 0 0xFF Sync byte (required only for UART serial)
2286 * 1 0x55 Start of telegram
2287 * 2 0x04 Telegram payload length
2288 * 3 0x04 Lingo ID: Extended Interface lingo
2289 * 4 0x00 Command ID (bits 15:8)
2290 * 5 0x2E Command ID (bits 7:0)
2291 * 6 0xNN New shuffle mode. See above.
2292 * 7 0xNN Telegram payload checksum byte
2293 *
2294 */
2295 if(buf[3] && !global_settings.playlist_shuffle)
2296 {
2297 global_settings.playlist_shuffle = 1;
2298 settings_save();
2299 if (audio_status() & AUDIO_STATUS_PLAY)
2300 playlist_randomise(NULL, current_tick, true);
2301 }
2302 else if(!buf[3] && global_settings.playlist_shuffle)
2303 {
2304 global_settings.playlist_shuffle = 0;
2305 settings_save();
2306 if (audio_status() & AUDIO_STATUS_PLAY)
2307 playlist_sort(NULL, true);
2308 }
2309
2310 /* respond with cmd ok packet */
2311 cmd_ok(cmd);
2312 break;
2313 }
2314 case 0x002F: /* GetRepeat */
2315 {
2316 /* The following is the description for the Apple Firmware
2317 *
2318 * Requests the track repeat state of the iPod. In response, the
2319 * iPod sends a Command 0x0030: ReturnRepeat telegram
2320 * to the device.
2321 *
2322 * Byte Value Meaning
2323 * 0 0xFF Sync byte (required only for UART serial)
2324 * 1 0x55 Start of telegram
2325 * 2 0x03 Telegram payload length
2326 * 3 0x04 Lingo ID: Extended Interface lingo
2327 * 4 0x00 Command ID (bits 15:8)
2328 * 5 0x2F Command ID (bits 7:0)
2329 * 6 0xCA Telegram payload checksum byte
2330 *
2331 */
2332 unsigned char data[] = {0x04, 0x00, 0x30, 0x00};
2333 if(global_settings.repeat_mode == REPEAT_OFF)
2334 data[3] = 0;
2335 else if(global_settings.repeat_mode == REPEAT_ONE)
2336 data[3] = 1;
2337 else
2338 data[3] = 2;
2339 iap_send_pkt(data, sizeof(data));
2340 break;
2341 }
2342 case 0x0030: /* ReturnRepeat. See Above */
2343 /* The following is the description for the Apple Firmware
2344 *
2345 * Returns the current iPod track repeat state to the device.
2346 * The iPod sends this telegram in response to the Command
2347 * 0x002F:GetRepeat command.
2348 *
2349 * Byte Value Meaning
2350 * 0 0xFF Sync byte (required only for UART serial)
2351 * 1 0x55 Start of telegram
2352 * 2 0x04 Telegram payload length
2353 * 3 0x04 Lingo ID: Extended Interface lingo
2354 * 4 0x00 Command ID (bits 15:8)
2355 * 5 0x30 Command ID (bits 7:0)
2356 * 6 0xNN Repeat state. See Below.
2357 * 7 0xNN Telegram payload checksum byte
2358 *
2359 * Repeat state values
2360 * Value Meaning
2361 * 0x00 Repeat off
2362 * 0x01 Repeat one track
2363 * 0x02 Repeat all tracks
2364 * 0x03 - 0xFF Reserved
2365 *
2366 */
2367 {
2368 /* We should NEVER receive this command so ERROR if we do */
2369 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2370 break;
2371 }
2372 case 0x0031: /* SetRepeat */
2373 {
2374 /* The following is the description for the Apple Firmware
2375 *
2376 * Sets the repeat state of the iPod. The iPod track repeat modes
2377 * are listed above. In response, the iPod sends an ACK telegram
2378 * with the command status.
2379 *
2380 * This telegram has an optional byte, byte 0x07, called the
2381 * RestoreonExitbyte. This byte can be used to restore the original
2382 * repeat setting in use when the accessory was attached to the
2383 * iPod. A nonzero value restores the original repeat setting of
2384 * the iPod when the accessory is detached. If this byte is zero,
2385 * the repeat setting set by the accessory overwrites the original
2386 * setting and persists after the accessory is detached from the
2387 * iPod.
2388 *
2389 * Note: Accessory developers are encouraged to always use the
2390 * Restore on Exit byte with a nonzero value to restore any
2391 * settings modified by the accessory upon detach.
2392 *
2393 * SetRepeat telegram with Restore on Exit byte
2394 * Byte Value Meaning
2395 * 0 0xFF Sync byte (required only for UART serial)
2396 * 1 0x55 Start of telegram
2397 * 2 0x05 Telegram payload length
2398 * 3 0x04 Lingo ID: Extended Interface lingo
2399 * 4 0x00 Command ID (bits 15:8)
2400 * 5 0x31 Command ID (bits 7:0)
2401 * 6 0xNN New repeat state. See above.
2402 * 7 0xNN Restore on Exit byte. If 1, the original setting is
2403 * restored on detach; if 0, the newsetting persists
2404 * after accessory detach.
2405 * 8 0xNN Telegram payload checksum byte
2406 *
2407 * SetRepeat setting persistent after the accessory detach.
2408 * Byte Value Meaning
2409 * 0 0xFF Sync byte (required only for UART serial)
2410 * 1 0x55 Start of telegram
2411 * 2 0x04 Telegram payload length
2412 * 3 0x04 Lingo ID: Extended Interface lingo
2413 * 4 0x00 Command ID (bits 15:8)
2414 * 5 0x31 Command ID (bits 7:0)
2415 * 6 0xNN New repeat state. See above.
2416 * 7 0xNN Telegram payload checksum byte
2417 *
2418 */
2419 int oldmode = global_settings.repeat_mode;
2420 if (buf[3] == 0)
2421 global_settings.repeat_mode = REPEAT_OFF;
2422 else if (buf[3] == 1)
2423 global_settings.repeat_mode = REPEAT_ONE;
2424 else if (buf[3] == 2)
2425 global_settings.repeat_mode = REPEAT_ALL;
2426
2427 if (oldmode != global_settings.repeat_mode)
2428 {
2429 settings_save();
2430 if (audio_status() & AUDIO_STATUS_PLAY)
2431 audio_flush_and_reload_tracks();
2432 }
2433 /* respond with cmd ok packet */
2434 cmd_ok(cmd);
2435 break;
2436 }
2437 case 0x0032: /* SetDisplayImage */
2438 {
2439 /* The following is the description for the Apple Firmware
2440 * This sets a bitmap image
2441 * that is displayed on the iPod display when connected to the
2442 * device. It replaces the default checkmark bitmap image that is
2443 * displayed when iPod is connected to an external device
2444 *
2445 * Sets a bitmap image that is shown on the iPod display when it is
2446 * connected to the device. The intent is to allow third party
2447 * branding when the iPod is communicating with an external device.
2448 * An image downloaded using this mechanism replaces the default
2449 * checkmark bitmap image that is displayed when iPod is connected
2450 * to an external device. The new bitmap is retained in RAM for as
2451 * long as the iPod remains powered. After a system reset or deep
2452 * sleep state, the new bitmap is lost and the checkmark image
2453 * restored as the default when the iPod next enters Extended
2454 * Interface mode after power-up.
2455 * Before setting a monochrome display image, the device can send
2456 * the Command 0x0033: GetMonoDisplayImageLimits telegram to obtain
2457 * the current iPod display width, height and pixel format.The
2458 * monochrome display information returned in the Command 0x0034:
2459 * ReturnMonoDisplayImageLimits telegram can be useful to the
2460 * device in deciding which type of display image format is suitable
2461 * for downloading to the iPod.
2462 * On iPods withcolor displays, devices can send the Command 0x0039:
2463 * GetColorDisplayImageLimits telegram to obtain the iPod color
2464 * display width,height,and pixel formats. The color display
2465 * information is returned in the Command 0x003A:
2466 * ReturnColorDisplayImageLimits” telegram.
2467 * To set a display image, the device must successfully send
2468 * SetDisplayImage descriptor and data telegrams to the iPod. The
2469 * SetDisplayImage descriptor telegram (telegram index 0x0000) must
2470 * be sent first, as it gives the iPod a description of the image
2471 * to be downloaded. This telegram is shown in below. The image
2472 * descriptor telegram includes image pixel format, image width and
2473 * height, and display row size (stride) in bytes. Optionally, the
2474 * descriptor telegram may also contain the beginning data of the
2475 * display image, as long as it remains within the maximum length
2476 * limits of the telegram.
2477 * Following the descriptor telegram, the SetDisplayImage data
2478 * telegrams (telegram index 0x0001 - 0xNNNN) should be sent using
2479 * sequential telegram indices until the entire image has been sent
2480 * to the iPod.
2481 *
2482 * Note: The SetDisplayImage telegram payload length is limited to
2483 * 500 bytes. This telegram length limit and the size and format of
2484 * the display image generally determine the minimum number of
2485 * telegrams that are required to set a display image.
2486 *
2487 * Note: Starting with the second generation iPod nano with version
2488 * 1.1.2 firmware, the use of the SetDisplayImage command is
2489 * limited to once every 15 seconds over USB transport. The iPod
2490 * classic and iPod 3G nano apply this restriction to both USB and
2491 * UART transports. Calls made to SetDisplayImage more frequently
2492 * than every 15 seconds will return a successful ACK command, but
2493 * the bitmap will not be displayed on the iPod’s screen. Hence use
2494 * of the SetDisplayImage command should be limited to drawing one
2495 * bitmap image per accessory connect. The iPod touch will accept
2496 * the SetDisplayImage command but will not draw it on the iPod’s
2497 * screen.
2498 *
2499 * Below shows the format of a descriptor telegram. This example
2500 * assumes the display image descriptor data exceeds the small
2501 * telegram payload capacity; a large telegram format is shown.
2502 *
2503 * SetDisplayImage descriptor telegram (telegram index = 0x0000)
2504 * Byte Value Meaning
2505 * 0 0xFF Sync byte (required only for UART serial)
2506 * 1 0x55 Start of telegram
2507 * 2 0x00 Telegram payload marker (large format)
2508 * 3 0xNN Large telegram payload length (bits 15:8)
2509 * 4 0xNN Large telegram payload length (bits 7:0)
2510 * 5 0x04 Lingo ID: Extended Interface lingo
2511 * 6 0x00 Command ID (bits 15:8)
2512 * 7 0x32 Command ID (bits 7:0)
2513 * 8 0x00 Descriptor telegram index (bits 15:8). These fields
2514 * uniquely identify each packet in the SetDisplayImage
2515 * transaction. The first telegram is the Descriptor
2516 * telegram and always starts with an index of 0x0000.
2517 * 9 0x00 Descriptor telegram index (bits 7:0)
2518 * 10 0xNN Display pixel format code. See Below.
2519 * 11 0xNN Imagewidth in pixels (bits 15:8). The number of
2520 * pixels, from left to right, per row.
2521 * 12 0xNN Image width in pixels (bits 7:0)
2522 * 13 0xNN Image height in pixels (bits 15:8). The number of
2523 * rows, from top to bottom, in the image.
2524 * 14 0xNN Image height in pixels (bits 7:0)
2525 * 15 0xNN Row size (stride) in bytes (bits 31:24). The number of
2526 * bytes representing one row of pixels. Each row is
2527 * zero-padded to end on a 32-bit boundary. The
2528 * cumulative size, in bytes, of the image data,
2529 * transferred across all telegrams in this transaction
2530 * is effectively (Row Size * Image Height).
2531 * 16 0xNN Row size (stride) in bytes (bits 23:16)
2532 * 17 0xNN Row size (stride) in bytes (bits 15:8)
2533 * 18 0xNN Row size (stride) in bytes (bits 7:0)
2534 * 19–N 0xNN Display image pixel data
2535 * NN 0xNN Telegram payload checksum byte
2536 *
2537 * SetDisplayImage data telegram (telegram index = 0x0001 - 0xNNNN)
2538 * Byte Value Meaning
2539 * 0 0xFF Sync byte (required only for UART serial)
2540 * 1 0x55 Start of telegram
2541 * 2 0x00 Telegram payload marker (large format)
2542 * 3 0xNN Large telegram payload length (bits 15:8)
2543 * 4 0xNN Large telegram payload length (bits 7:0)
2544 * 5 0x04 Lingo ID: Extended Interface lingo
2545 * 6 0x00 Command ID (bits 15:8)
2546 * 7 0x32 Command ID (bits 7:0)
2547 * 8 0xNN Descriptor telegram index (bits 15:8). These fields
2548 * uniquely identify each packet in the SetDisplayImage
2549 * transaction. The first telegram is the descriptor
2550 * telegram, shown in Table 6-68 (page 97). The
2551 * remaining n-1 telegrams are simply data telegrams,
2552 * where n is determined by the size of the image.
2553 * 9 0xNN Descriptor telegram index (bits 7:0)
2554 * 10–N 0xNN Display image pixel data
2555 * NN 0xNN Telegram payload checksum byte
2556 *
2557 * Note: A known issue causes SetDisplayImage data telegram
2558 * lengths less than 11 bytes to return a bad parameter error (0x04)
2559 * ACK on 3G iPods.
2560 *
2561 * The iPod display is oriented as a rectangular grid of pixels. In
2562 * the horizontal direction (x-coordinate), the pixel columns are
2563 * numbered, left to right, from 0 to Cmax. In the vertical
2564 * direction (y-coordinate), the pixel rows are numbered, top to
2565 * bottom, from 0 to Rmax. Therefore, an (x,y) coordinate of (0,0)
2566 * represents the upper-leftmost pixel on the display and
2567 * (Cmax,Rmax) represents the lower-rightmost pixel on the display.
2568 * A portion of the iPod display pixel layout is shown below, where
2569 * x is the column number, y is the row number, and (Cmax,Rmax)
2570 * represents the maximum row and column numbers.
2571 *
2572 * Pixel layout
2573 * x
2574 * y 0,0 1,0 2,0 3,0 4,0 5,0 6,0 7,0 - Cmax,0
2575 * 0,1 1,1 2,1 3,1 4,1 5,1 6,1 7,1 - Cmax,1
2576 * 0,2 1,2 2,2 3,2 4,2 5,2 6,2 7,2 - Cmax,2
2577 * 0,3 1,3 2,3 3,3 4,3 5,3 6,3 7,3 - Cmax,3
2578 * 0,4 1,4 2,4 3,4 4,4 5,4 6,4 7,4 - Cmax,4
2579 * 0,5 1,5 2,5 3,5 4,5 5,5 6,5 7,5 - Cmax,5
2580 * 0,6 1,6 2,6 3,6 4,6 5,6 6,6 7,6 - Cmax,6
2581 * 0,7 1,7 2,7 3,7 4,7 5,7 6,7 7,7 - Cmax,7
2582 * " " " " " " " " " "
2583 * 0, 1, 2, 3, 4, 5, 6, 7, - Cmax,
2584 * RmaxRmaxRmaxRmaxRmaxRmaxRmaxRmax Rmax
2585 *
2586 * Display pixel format codes
2587 * Display pixel format Code Protocol version
2588 * Reserved 0x00 N/A
2589 * Monochrome, 2 bits per pixel 0x01 1.01
2590 * RGB 565 color, little-endian, 16 bpp 0x02 1.09
2591 * RGB 565 color, big-endian, 16 bpp 0x03 1.09
2592 * Reserved 0x04-0xFF N/A
2593 *
2594 * iPods with color screens support all three image formats. All
2595 * other iPods support only display pixel format 0x01 (monochrome,
2596 * 2 bpp).
2597 *
2598 * Display Pixel Format 0x01
2599 * Display pixel format 0x01 (monochrome, 2 bits per pixel) is the
2600 * pixel format supported by all iPods. Each pixel consists of 2
2601 * bits that control the pixel intensity. The pixel intensities and
2602 * associated binary codes are listed below.
2603 *
2604 * 2 bpp monochrome pixel intensities
2605 * Pixel Intensity Binary Code
2606 * Pixel off (not visible) 00b
2607 * Pixel on 25% (light grey) 01b
2608 * Pixel on 50% (dark grey) 10b
2609 * Pixel on 100% (black) 11b
2610 *
2611 * Each byte of image data contains four packed pixels. The pixel
2612 * ordering within bytes and the byte ordering within 32 bits is
2613 * shown below.
2614 *
2615 * Image Data Byte 0x0000
2616 * Pixel0 Pixel1 Pixel2 Pixel3
2617 * 7 6 5 4 3 2 1 0
2618 *
2619 * Image Data Byte 0x0001
2620 * Pixel4 Pixel5 Pixel6 Pixel7
2621 * 7 6 5 4 3 2 1 0
2622 *
2623 * Image Data Byte 0x0002
2624 * Pixel8 Pixel9 Pixel10 Pixel11
2625 * 7 6 5 4 3 2 1 0
2626 *
2627 * Image Data Byte 0x0003
2628 * Pixel12 Pixel13 Pixel14 Pixel15
2629 * 7 6 5 4 3 2 1 0
2630 *
2631 * Image Data Byte 0xNNNN
2632 * PixelN PixelN+1 PixelN+2 PixelN+3
2633 * 7 6 5 4 3 2 1 0
2634 *
2635 * Display Pixel Formats 0x02 and 0x03
2636 * Display pixel format 0x02 (RGB 565, little-endian) and display
2637 * pixel format 0x03 (RGB 565, big-endian) are available for use
2638 * in all iPods with color screens. Each pixel consists of 16 bits
2639 * that control the pixel intensity for the colors red, green, and
2640 * blue.
2641 * It takes two bytes to represent a single pixel. Red is
2642 * represented by 5 bits, green is represented by 6 bits, and blue
2643 * by the final 5 bits. A 32-bit sequence represents 2 pixels. The
2644 * pixel ordering within bytes and the byte ordering within 32 bits
2645 * for display format 0x02 (RGB 565, little-endian) is shown below.
2646 *
2647 * Image Data Byte 0x0000
2648 * Pixel 0, lower 3 bits of green Pixel 0, all 5 bits of blue
2649 * 7 6 5 4 3 2 1 0
2650 * Image Data Byte 0x0001
2651 * Pixel 0, all 5 bits of red Pixel 0,upper 3 bits of green
2652 * 7 6 5 4 3 2 1 0
2653 *
2654 * Image Data Byte 0x0002
2655 * Pixel 1, lower 3 bits of green Pixel 1, all 5 bits of blue
2656 * 7 6 5 4 3 2 1 0
2657 *
2658 * Image Data Byte 0x0003
2659 * Pixel 1, all 5 bits of red Pixel 1, upper 3 bits of green
2660 * 7 6 5 4 3 2 1 0
2661 *
2662 * The format for display pixel format 0x03 (RGB 565, big-endian, 16
2663 * bpp) is almost identical, with the exception that bytes 0 and 1
2664 * are swapped and bytes 2 and 3 are swapped.
2665 *
2666 */
2667 cmd_ok(cmd);
2668 break;
2669 }
2670 case 0x0033: /* GetMonoDisplayImageLimits */
2671
2672 {
2673 /* The following is the description for the Apple Firmware
2674 *
2675 * Requests the limiting characteristics of the monochrome image
2676 * that can be sent to the iPod for display while it is connected
2677 * to the device. It can be used to determine the display pixel
2678 * format and maximum width and height of a monochrome image to be
2679 * set using the Command 0x0032: SetDisplayImage telegram. In
2680 * response, the iPod sends a Command 0x0034:
2681 * ReturnMonoDisplayImageLimits telegram to the device with the
2682 * requested display information. The GetMonoDisplayImageLimits
2683 * command is supported by iPods with either monochrome or color
2684 * displays. To obtain color display image limits, use Command
2685 * 0x0039: GetColorDisplayImageLimits.
2686 *
2687 * Byte Value Meaning
2688 * 0 0xFF Sync byte (required only for UART serial)
2689 * 1 0x55 Start of telegram
2690 * 2 0x03 Telegram payload length
2691 * 3 0x04 Lingo ID: Extended Interface lingo
2692 * 4 0x00 Command ID (bits 15:8)
2693 * 5 0x33 Command ID (bits 7:0)
2694 * 6 0xC6 Telegram payload checksum byte
2695 *
2696 */
2697 unsigned char data[] = {0x04, 0x00, 0x34,
2698 LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
2699 LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
2700 0x01};
2701 iap_send_pkt(data, sizeof(data));
2702 break;
2703 }
2704 case 0x0034: /* ReturnMonoDisplayImageLimits. See Above*/
2705 /* The following is the description for the Apple Firmware
2706 *
2707 * Returns the limiting characteristics of the monochrome image that
2708 * can be sent to the iPod for display while it is connected to the
2709 * device. The iPod sends this telegram in response to the Command
2710 * 0x0033: GetMonoDisplayImageLimits telegram. Monochrome display
2711 * characteristics include maximum image width and height and the
2712 * display pixel format.
2713 *
2714 * Byte Value Meaning
2715 * 0 0xFF Sync byte (required only for UART serial)
2716 * 1 0x55 Start of telegram
2717 * 2 0xNN Telegram payload length
2718 * 3 0x04 Lingo ID: Extended Interface lingo
2719 * 4 0x00 Command ID (bits 15:8)
2720 * 5 0x34 Command ID (bits 7:0)
2721 * 6 0xNN Maximum image width in pixels (bits 15:8)
2722 * 7 0xNN Maximum image width in pixels (bits 7:0)
2723 * 8 0xNN Maximum image height in pixels (bits 15:8)
2724 * 9 0xNN Maximumimage height in pixels (bits 7:0)
2725 * 10 0xNN Display pixel format (see Table 6-70 (page 99)).
2726 * 11 0xNN Telegram payload checksum byte
2727 *
2728 */
2729 {
2730 /* We should NEVER receive this command so ERROR if we do */
2731 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2732 break;
2733 }
2734 case 0x0035: /* GetNumPlayingTracks */
2735 {
2736 /* The following is the description for the Apple Firmware
2737 *
2738 * Requests the number of tracks in the list of tracks queued to
2739 * play on the iPod. In response, the iPod sends a Command 0x0036:
2740 * ReturnNumPlayingTracks telegram with the count of tracks queued
2741 * to play.
2742 *
2743 * Byte Value Meaning
2744 * 0 0xFF Sync byte (required only for UART serial)
2745 * 1 0x55 Start of telegram
2746 * 2 0x03 Telegram payload length
2747 * 3 0x04 Lingo ID: Extended Interface lingo
2748 * 4 0x00 Command ID (bits 15:8)
2749 * 5 0x35 Command ID (bits 7:0)
2750 * 6 0xC4 Telegram payload checksum byte
2751 *
2752 */
2753 unsigned char data[] = {0x04, 0x00, 0x36,
2754 0x00, 0x00, 0x00, 0x00};
2755 unsigned long playlist_amt = playlist_amount();
2756 put_u32(&data[3], playlist_amt);
2757 iap_send_pkt(data, sizeof(data));
2758 break;
2759 }
2760 case 0x0036: /* ReturnNumPlayingTracks. See Above */
2761 /* The following is the description for the Apple Firmware
2762 *
2763 * Returns the number of tracks in the actual list of tracks queued
2764 * to play, including the currently playing track (if any). The
2765 * iPod sends this telegram in response to the Command 0x0035:
2766 * GetNumPlayingTracks telegram.
2767 *
2768 * Byte Value Meaning
2769 * 0 0xFF Sync byte (required only for UART serial)
2770 * 1 0x55 Start of telegram
2771 * 2 0x07 Telegram payload length
2772 * 3 0x04 Lingo ID: Extended Interface lingo
2773 * 4 0x00 Command ID (bits 15:8)
2774 * 5 0x36 Command ID (bits 7:0)
2775 * 6 0xNN Number of tracks playing(bits 31:24)
2776 * 7 0xNN Number of tracks playing(bits 23:16)
2777 * 8 0xNN Number of tracks playing (bits 15:8)
2778 * 9 0xNN Number of tracks playing (bits 7:0)
2779 * 10 0xNN Telegram payload checksum byte
2780 *
2781 */
2782 {
2783 /* We should NEVER receive this command so ERROR if we do */
2784 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2785 break;
2786 }
2787 case 0x0037: /* SetCurrentPlayingTrack */
2788 /* The following is the description for the Apple Firmware
2789 *
2790 * Sets the index of the track to play in the Now Playing playlist
2791 * on the iPod. The index that is specified here is obtained by
2792 * sending the Command 0x0035: GetNumPlayingTracks and Command
2793 * 0x001E: GetCurrentPlayingTrackIndex telegrams to obtain the
2794 * number of playing tracks and the current playing track index,
2795 * respectively. In response, the iPod sends an ACK telegram
2796 * indicating the status of the command.
2797 *
2798 * Note: The behavior of this command has changed. Before the
2799 * 2G nano, if this command was sent with the current playing track
2800 * index the iPod would pause playback momentarily and then resume.
2801 * Starting with the 2G nano, the iPod restarts playback of the
2802 * current track from the beginning. Older iPods will not be
2803 * updated to the new behavior.
2804 *
2805 * Note: This command is usable only when the iPod is in a playing
2806 * or paused state. If the iPod is stopped, this command fails.
2807 *
2808 * Byte Value Meaning
2809 * 0 0xFF Sync byte (required only for UART serial)
2810 * 1 0x55 Start of telegram
2811 * 2 0x07 Telegram payload length
2812 * 3 0x04 Lingo ID: Extended Interface lingo
2813 * 4 0x00 Command ID (bits 15:8)
2814 * 5 0x37 Command ID (bits 7:0)
2815 * 6 0xNN New current playing track index (bits 31:24)
2816 * 7 0xNN New current playing track index (bits 23:16)
2817 * 8 0xNN New current playing track index (bits 15:8)
2818 * 9 0xNN New current playing track index (bits 7:0)
2819 * 10 0xNN Telegram payload checksum byte
2820 *
2821 */
2822 {
2823 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
2824 long tracknum = get_u32(&buf[3]);
2825
2826 audio_pause();
2827 audio_skip(tracknum - playlist_next(0));
2828 if (!paused)
2829 audio_resume();
2830
2831 /* respond with cmd ok packet */
2832 cmd_ok(cmd);
2833 break;
2834 }
2835 case 0x0038: /* SelectSortDBRecord */
2836 /* The following is the description for the Apple Firmware
2837 *
2838 * Selects one or more records in the iPod database, based on a
2839 * category-relative index. For example, selecting category 2
2840 * (Artist), record index 1, and sort order 3 (Album) results in a
2841 * list of selected tracks (records) from the second artist in the
2842 * artist list, sorted by album name. Selections are additive and
2843 * limited by the category hierarchy. Subsequent selections are
2844 * made based on the subset of records resulting from previous
2845 * selections and not from the entire database. The database
2846 * category types are shown above. The sort order options and codes
2847 * are shown below.
2848 *
2849 * SelectSortDBRecord telegram
2850 * Byte Value Meaning
2851 * 0 0xFF Sync byte (required only for UART serial)
2852 * 1 0x55 Start of telegram
2853 * 2 0x09 Telegram payload length
2854 * 3 0x04 Lingo ID: Extended Interface lingo
2855 * 4 0x00 Command ID (bits 15:8)
2856 * 5 0x38 Command ID (bits 7:0)
2857 * 6 0xNN Database category type.
2858 * 7 0xNN Category record index (bits 31:24)
2859 * 8 0xNN Category record index (bits 23:16)
2860 * 9 0xNN Category record index (bits 15:8)
2861 * 10 0xNN Category record index (bits 7:0)
2862 * 11 0xNN Database sort type.
2863 * 12 0xNN Telegram payload checksum byte
2864 *
2865 * Database sort order options
2866 * Sort Order Code Protocol version
2867 * Sort by genre 0x00 1.00
2868 * Sort by artist 0x01 1.00
2869 * Sort by composer 0x02 1.00
2870 * Sort by album 0x03 1.00
2871 * Sort by name 0x04 1.00
2872 * Sort by playlist 0x05 1.00
2873 * Sort by release date 0x06 1.08
2874 * Reserved 0x07 - 0xFE N/A
2875 * Use default sort type 0xFF 1.00
2876 *
2877 * The default order of song and audiobook tracks on the iPod is
2878 * alphabetical by artist, then alphabetical by that artist's
2879 * albums, then ordered according to the order of the tracks on the
2880 * album.
2881 * For podcasts, the default order of episode tracks is reverse
2882 * chronological. That is, the newest ones are first,then
2883 * alphabetical by podcast name.
2884 * The SelectSortDBRecord command can be used to sort all the song
2885 * and audiobook tracks on the iPod alphabetically as follows:
2886 * 1. Command 0x0016: ResetDBSelection
2887 * 2. Command 0x0018: GetNumberCategorizedDBRecords for the Playlist
2888 * category.
2889 * 3. SelectSortDBRecord based on the Playlist category, using a
2890 * record index of 0 to select the All Tracks
2891 * playlist and the sort by name (0x04) sort
2892 * order.
2893 * 4. GetNumberCategorizedDBRecords for the Track category.
2894 * 5. Command 0x001A :RetrieveCategorizedDatabaseRecords based on
2895 * the Track category, using a start index of 0
2896 * and an end index of the number of records
2897 * returned by the call to
2898 * GetNumberCategorizedDBRecords in step 4.
2899 *
2900 * The sort order of artist names ignores certain articles such
2901 * that the artist “The Doors” is sorted under the letter ‘D’ and
2902 * not ‘T’; this matches the behavior of iTunes. The sort order is
2903 * different depending on the language setting used in the iPod.
2904 * The list of ignored articles may change in the future without
2905 * notice.
2906 * The SelectDBRecord command may also be used to select a database
2907 * record with the default sort order.
2908 *
2909 * Note: The sort order field is ignored for the Audiobook category.
2910 * Audiobooks are automatically sorted by track title. The sort
2911 * order for podcast tracks defaults to release date, with the
2912 * newest track coming first.
2913 *
2914 * Selects one or more records in the iPod database, based on a
2915 * category-relative index. This appears to be hardcoded hierarchy
2916 * decided by Apple that the external devices follow.
2917 * This is as follows for all except podcasts,
2918 *
2919 * All (highest level),
2920 * Playlist,
2921 * Genre or Media Kind,
2922 * Artist or Composer,
2923 * Album,
2924 * Track or Audiobook (lowest)
2925 *
2926 * for Podcasts, the order is
2927 *
2928 * All (highest),
2929 * Podcast,
2930 * Episode
2931 * Track (lowest)
2932 *
2933 * Categories are
2934 *
2935 * 0x00 Reserved
2936 * 0x01 Playlist
2937 * 0x02 Artist
2938 * 0x03 Album
2939 * 0x04 Genre
2940 * 0x05 Track
2941 * 0x06 Composer
2942 * 0x07 Audiobook
2943 * 0x08 Podcast
2944 * 0x09 - 0xff Reserved
2945 *
2946 * Sort Order optiona and codes are
2947 *
2948 * 0x00 Sort by Genre
2949 * 0x01 Sort by Artist
2950 * 0x02 Sort by Composer
2951 * 0x03 Sort by Album
2952 * 0x04 Sort by Name (Song Title)
2953 * 0x05 Sort by Playlist
2954 * 0x06 Sort by Release Date
2955 * 0x07 - 0xfe Reserved
2956 * 0xff Use default Sort Type
2957 *
2958 * Packet format (offset in data[]: Description)
2959 * 0x00: Lingo ID: Extended Interface Protocol Lingo, always 0x04
2960 * 0x01-0x02: Command, always 0x0038
2961 * 0x03: Database Category Type
2962 * 0x04-0x07: Category Record Index
2963 * 0x08 Database Sort Type
2964 *
2965 * On Rockbox, if the recordtype is playlist, we load the selected
2966 * playlist and start playing from the first track.
2967 * If the recordtype is track, we play that track from the current
2968 * playlist.
2969 * On anything else we just play the current track from the current
2970 * playlist.
2971 * cur_dbrecord[0] is the recordtype
2972 * cur_dbrecord[1-4] is the u32 of the record number requested
2973 * which might be a playlist or a track number depending on
2974 * the value of cur_dbrecord[0]
2975 */
2976 {
2977 memcpy(cur_dbrecord, buf + 3, 5);
2978
2979 int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
2980 unsigned int number_of_playlists = nbr_total_playlists();
2981 uint32_t index;
2982 uint32_t trackcount;
2983 index = get_u32(&cur_dbrecord[1]);
2984 trackcount = playlist_amount();
2985 if ((cur_dbrecord[0] == 0x05) && (index > trackcount))
2986 {
2987 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2988 break;
2989 }
2990 if ((cur_dbrecord[0] == 0x01) && (index > (number_of_playlists + 1)))
2991 {
2992 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
2993 break;
2994 }
2995 switch (cur_dbrecord[0])
2996 {
2997 case 0x01: /* Playlist*/
2998 {
2999 if (index != 0x00) /* 0x00 is the On-The-Go Playlist and
3000 we do nothing with it */
3001 {
3002 audio_pause();
3003 audio_skip(-iap_get_trackindex());
3004 playlist_sort(NULL, true);
3005 last_selected_playlist = index;
3006 seek_to_playlist(last_selected_playlist);
3007 }
3008 break;
3009 }
3010 case 0x02: /* Artist Do Nothing */
3011 case 0x03: /* Album Do Nothing */
3012 case 0x04: /* Genre Do Nothing */
3013 case 0x06: /* Composer Do Nothing */
3014 break;
3015 case 0x05: /* Track*/
3016 {
3017 audio_pause();
3018 audio_skip(-iap_get_trackindex());
3019 playlist_sort(NULL, true);
3020 audio_skip(index - playlist_next(0));
3021 break;
3022 }
3023 }
3024 if (!paused)
3025 audio_resume();
3026 /* respond with cmd ok packet */
3027 cmd_ok(cmd);
3028 break;
3029 }
3030 case 0x0039: /* GetColorDisplayImageLimits */
3031 /* The following is the description for the Apple Firmware
3032 *
3033 * Requests the limiting characteristics of the color image that
3034 * can be sent to the iPod for display while it is connected to
3035 * the device. It can be used to determine the display pixel format
3036 * and maximum width and height of a color image to be set using
3037 * the Command 0x0032: SetDisplayImage telegram. In response, the
3038 * iPod sends a Command 0x003A: ReturnColorDisplayImageLimits
3039 * telegram to the device with the requested display information.
3040 * This command is supported only by iPods with color displays.
3041 *
3042 * Byte Value Meaning
3043 * 0 0xFF Sync byte (required only for UART serial)
3044 * 1 0x55 Start of telegram
3045 * 2 0x03 Telegram payload length
3046 * 3 0x04 Lingo ID: Extended Interface lingo
3047 * 4 0x00 Command ID (bits 15:8)
3048 * 5 0x39 Command ID (bits 7:0)
3049 * 6 0xC0 Telegram payload checksum byte
3050 *
3051 */
3052 {
3053 /* Set the same as the ReturnMonoDisplayImageLimits */
3054 unsigned char data[] = {0x04, 0x00, 0x3A,
3055 LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
3056 LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
3057 0x01};
3058 iap_send_pkt(data, sizeof(data));
3059 break;
3060 }
3061 case 0x003A: /* ReturnColorDisplayImageLimits See Above */
3062 /* The following is the description for the Apple Firmware
3063 *
3064 * Returns the limiting characteristics of the color image that can
3065 * be sent to the iPod for display while it is connected to the
3066 * device. The iPod sends this telegram in response to the Command
3067 * 0x0039: GetColorDisplayImageLimits telegram. Display
3068 * characteristics include maximum image width and height and the
3069 * display pixel format.
3070 *
3071 * Note: If the iPod supports multiple display image formats, a five
3072 * byte block of additional image width, height, and pixel format
3073 * information is appended to the payload for each supported display
3074 * format. The list of supported color display image formats
3075 * returned by the iPod may change in future software versions.
3076 * Devices must be able to parse a variable length list of supported
3077 * color display formats to search for compatible formats.
3078 *
3079 * Byte Value Meaning
3080 * 0 0xFF Sync byte (required only for UART serial)
3081 * 1 0x55 Start of telegram
3082 * 2 0xNN Telegram payload length
3083 * 3 0x04 Lingo ID: Extended Interface lingo
3084 * 4 0x00 Command ID (bits 15:8)
3085 * 5 0x3A Command ID (bits 7:0)
3086 * 6 0xNN Maximum image width in pixels (bits 15:8)
3087 * 7 0xNN Maximum image width in pixels (bits 7:0)
3088 * 8 0xNN Maximum image height in pixels (bits 15:8)
3089 * 9 0xNN Maximum image height in pixels (bits 7:0)
3090 * 10 0xNN Display pixel format (see Table 6-70 (page 99)).
3091 * 11-N 0xNN Optional display image width, height, and pixel format
3092 * for the second to nth supported display formats,
3093 * if present.
3094 * NN 0xNN Telegram payload checksum byte
3095 *
3096 */
3097 {
3098 /* We should NEVER receive this command so ERROR if we do */
3099 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
3100 break;
3101 }
3102 case 0x003B: /* ResetDBSelectionHierarchy */
3103 {
3104 /* The following is the description for the Apple Firmware
3105 *
3106 * This command carries a single byte in its payload (byte 6). A
3107 * hierarchy selection value of 0x01 means that the accessory wants
3108 * to navigate the music hierarchy; a hierarchy selection value of
3109 * 0x02 means that the accessory wants to navigate the video
3110 * hierarchy.
3111 *
3112 * Byte Value Meaning
3113 * 0 0xFF Sync byte (required only for UART serial)
3114 * 1 0x55 Start of telegram
3115 * 2 0x04 Telegram payload length
3116 * 3 0x04 Lingo ID: Extended Interface lingo
3117 * 4 0x00 Command ID (bits 15:8)
3118 * 5 0x3B Command ID (bits 7:0)
3119 * 6 0x01 or 0x02 Hierarchy selection
3120 * 7 0xNN Telegram payload checksum byte
3121 *
3122 * Note: The iPod will return an error if a device attempts to
3123 * enable an unsupported hierarchy, such as a video hierarchy on an
3124 * iPod model that does not support video.
3125 */
3126 dbrecordcount = 0;
3127 cur_dbrecord[0] = 0;
3128 put_u32(&cur_dbrecord[1],0);
3129 switch (buf[3])
3130 {
3131 case 0x01: /* Music */
3132 {
3133 cmd_ok(cmd);
3134 break;
3135 }
3136 default: /* Anything else */
3137 {
3138 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
3139 break;
3140 }
3141 }
3142 }
3143 default:
3144 {
3145#ifdef LOGF_ENABLE
3146 logf("iap: Unsupported Mode04 Command");
3147#endif
3148 /* The default response is IAP_ACK_BAD_PARAM */
3149 cmd_ack(cmd, IAP_ACK_BAD_PARAM);
3150 break;
3151 }
3152 }
3153}
diff --git a/apps/misc.c b/apps/misc.c
index 8dff227bc1..e746c432e6 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -607,14 +607,6 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
607 unplug_change(false); 607 unplug_change(false);
608 return SYS_PHONE_UNPLUGGED; 608 return SYS_PHONE_UNPLUGGED;
609#endif 609#endif
610#ifdef IPOD_ACCESSORY_PROTOCOL
611 case SYS_IAP_PERIODIC:
612 iap_periodic();
613 return SYS_IAP_PERIODIC;
614 case SYS_IAP_HANDLEPKT:
615 iap_handlepkt();
616 return SYS_IAP_HANDLEPKT;
617#endif
618#if CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO) 610#if CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO)
619 /* stop playback if we receive a call */ 611 /* stop playback if we receive a call */
620 case SYS_CALL_INCOMING: 612 case SYS_CALL_INCOMING:
diff --git a/firmware/export/iap.h b/firmware/export/iap.h
index 8ee26cbe9f..22f36083d1 100644
--- a/firmware/export/iap.h
+++ b/firmware/export/iap.h
@@ -22,7 +22,9 @@
22 22
23#include <stdbool.h> 23#include <stdbool.h>
24 24
25#define RX_BUFLEN 512 25/* This is just the payload size, without sync, length and checksum */
26#define RX_BUFLEN (64*1024)
27/* This is the entire frame length, sync, length, payload and checksum */
26#define TX_BUFLEN 128 28#define TX_BUFLEN 128
27 29
28extern bool iap_getc(unsigned char x); 30extern bool iap_getc(unsigned char x);
diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h
index 76ed96bd14..3cadefdf68 100644
--- a/firmware/export/kernel.h
+++ b/firmware/export/kernel.h
@@ -79,8 +79,6 @@
79#define SYS_REMOTE_PLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 4) 79#define SYS_REMOTE_PLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 4)
80#define SYS_REMOTE_UNPLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 5) 80#define SYS_REMOTE_UNPLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 5)
81#define SYS_CAR_ADAPTER_RESUME MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 0) 81#define SYS_CAR_ADAPTER_RESUME MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 0)
82#define SYS_IAP_PERIODIC MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 1)
83#define SYS_IAP_HANDLEPKT MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 2)
84#define SYS_CALL_INCOMING MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 3) 82#define SYS_CALL_INCOMING MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 3)
85#define SYS_CALL_HUNG_UP MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 4) 83#define SYS_CALL_HUNG_UP MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 4)
86#define SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5) 84#define SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5)
diff --git a/firmware/target/arm/pp/debug-pp.c b/firmware/target/arm/pp/debug-pp.c
index f4ba61cd39..2f57e1ef14 100644
--- a/firmware/target/arm/pp/debug-pp.c
+++ b/firmware/target/arm/pp/debug-pp.c
@@ -155,9 +155,12 @@ bool dbg_ports(void)
155 155
156#if defined(IPOD_ACCESSORY_PROTOCOL) 156#if defined(IPOD_ACCESSORY_PROTOCOL)
157 const unsigned char *serbuf = iap_get_serbuf(); 157 const unsigned char *serbuf = iap_get_serbuf();
158 lcd_putsf(0, line++, "IAP: %02x %02x %02x %02x %02x %02x %02x %02x", 158 if (serbuf)
159 serbuf[0], serbuf[1], serbuf[2], serbuf[3], serbuf[4], serbuf[5], 159 {
160 serbuf[6], serbuf[7]); 160 lcd_putsf(0, line++, "IAP: %02x %02x %02x %02x %02x %02x %02x %02x",
161 serbuf[0], serbuf[1], serbuf[2], serbuf[3], serbuf[4], serbuf[5],
162 serbuf[6], serbuf[7]);
163 }
161#endif 164#endif
162 165
163#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) 166#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
diff --git a/tools/iap/Device/iPod.pm b/tools/iap/Device/iPod.pm
new file mode 100644
index 0000000000..b2d686cce7
--- /dev/null
+++ b/tools/iap/Device/iPod.pm
@@ -0,0 +1,386 @@
1package Device::iPod;
2
3use Device::SerialPort;
4use POSIX qw(isgraph);
5use strict;
6
7sub new {
8 my $class = shift;
9 my $port = shift;
10 my $self = {};
11 my $s;
12
13 $self->{-serial} = undef;
14 $self->{-inbuf} = '';
15 $self->{-error} = undef;
16 $self->{-baudrate} = 57600;
17 $self->{-debug} = 0;
18
19 return bless($self, $class);
20}
21
22sub open {
23 my $self = shift;
24 my $port = shift;
25
26 $self->{-serial} = new Device::SerialPort($port);
27 unless(defined($self->{-serial})) {
28 $self->{-error} = $!;
29 return undef;
30 }
31
32 $self->{-serial}->parity('none');
33 $self->{-serial}->databits(8);
34 $self->{-serial}->stopbits(1);
35 $self->{-serial}->handshake('none');
36 return $self->baudrate($self->{-baudrate});
37}
38
39sub baudrate {
40 my $self = shift;
41 my $baudrate = shift;
42
43 if ($baudrate < 1) {
44 $self->{-error} = "Invalid baudrate";
45 return undef;
46 }
47
48 $self->{-baudrate} = $baudrate;
49 if (defined($self->{-serial})) {
50 $self->{-serial}->baudrate($baudrate);
51 }
52
53 return 1;
54}
55
56sub sendmsg {
57 my $self = shift;
58 my $lingo = shift;
59 my $command = shift;
60 my $data = shift || '';
61
62 return $self->_nosetup() unless(defined($self->{-serial}));
63
64 if (($lingo < 0) || ($lingo > 255)) {
65 $self->{-error} = 'Invalid lingo';
66 return undef;
67 }
68
69 if ($command < 0) {
70 $self->{-error} = 'Invalid command';
71 return undef;
72 }
73
74 if ($lingo == 4) {
75 if ($command > 0xffff) {
76 $self->{-error} = 'Invalid command';
77 return undef;
78 }
79 return $self->_send($self->_frame_cmd(pack("Cn", $lingo, $command) . $data));
80 } else {
81 if ($command > 0xff) {
82 $self->{-error} = 'Invalid command';
83 return undef;
84 }
85 return $self->_send($self->_frame_cmd(pack("CC", $lingo, $command) . $data));
86 }
87}
88
89sub sendraw {
90 my $self = shift;
91 my $data = shift;
92
93 return $self->_nosetup() unless(defined($self->{-serial}));
94
95 return $self->_send($data);
96}
97
98sub recvmsg {
99 my $self = shift;
100 my $m;
101 my @m;
102
103 return $self->_nosetup() unless(defined($self->{-serial}));
104
105 $m = $self->_fillbuf();
106 unless(defined($m)) {
107 # Error was set by lower levels
108 return wantarray?():undef;
109 }
110
111 printf("Fetched %s\n", $self->_hexstring($m)) if $self->{-debug};
112
113 @m = $self->_unframe_cmd($m);
114
115 unless(@m) {
116 return undef;
117 }
118
119 if (wantarray()) {
120 return @m;
121 } else {
122 return {-lingo => $m[0], -cmd => $m[1], -payload => $m[2]};
123 }
124}
125
126sub emptyrecv {
127 my $self = shift;
128 my $m;
129
130 while ($m = $self->_fillbuf()) {
131 printf("Discarded %s\n", $self->_hexstring($m)) if (defined($m) && $self->{-debug});
132 }
133}
134
135sub error {
136 my $self = shift;
137
138 return $self->{-error};
139}
140
141sub _nosetup {
142 my $self = shift;
143
144 $self->{-error} = 'Serial port not setup';
145 return undef;
146}
147
148sub _frame_cmd {
149 my $self = shift;
150 my $data = shift;
151 my $l = length($data);
152 my $csum;
153
154 if ($l > 0xffff) {
155 $self->{-error} = 'Command too long';
156 return undef;
157 }
158
159 if ($l > 255) {
160 $data = pack("Cn", 0, length($data)) . $data;
161 } else {
162 $data = pack("C", length($data)) . $data;
163 }
164
165 foreach (unpack("C" x length($data), $data)) {
166 $csum += $_;
167 }
168 $csum &= 0xFF;
169 $csum = 0x100 - $csum;
170
171 return "\xFF\x55" . $data . pack("C", $csum);
172}
173
174sub _unframe_cmd {
175 my $self = shift;
176 my $data = shift;
177 my $payload = '';
178 my ($count, $length, $csum);
179 my $state = 0;
180 my $c;
181 my ($lingo, $cmd);
182
183 return () unless(defined($data));
184
185 foreach $c (unpack("C" x length($data), $data)) {
186 if ($state == 0) {
187 # Wait for sync
188 next unless($c == 255);
189 $state = 1;
190 } elsif ($state == 1) {
191 # Wait for sop
192 next unless($c == 85);
193 $state = 2;
194 } elsif ($state == 2) {
195 # Length (short frame)
196 $csum = $c;
197 if ($c == 0) {
198 # Large frame
199 $state = 3;
200 } else {
201 $state = 5;
202 }
203 $length = $c;
204 $count = 0;
205 next;
206 } elsif ($state == 3) {
207 # Large frame, hi
208 $csum += $c;
209 $length = ($c << 8);
210 $state = 4;
211 next;
212 } elsif ($state == 4) {
213 # Large frame, lo
214 $csum += $c;
215 $length |= $c;
216 if ($length == 0) {
217 $self->{-error} = 'Length is 0';
218 return ();
219 }
220 $state = 5;
221 next;
222 } elsif ($state == 5) {
223 # Data bytes
224 $csum += $c;
225 $payload .= chr($c);
226 $count += 1;
227 if ($count == $length) {
228 $state = 6;
229 }
230 } elsif ($state == 6) {
231 # Checksum byte
232 $csum += $c;
233 if (($csum & 0xFF) != 0) {
234 $self->{-error} = 'Invalid checksum';
235 return ();
236 }
237 $state = 7;
238 last;
239 } else {
240 $self->{-error} = 'Invalid state';
241 return ();
242 }
243 }
244
245 # If we get here, we either have data or not. Check.
246 if ($state != 7) {
247 $self->{-error} = 'Could not unframe data';
248 return ();
249 }
250
251 $lingo = unpack("C", $payload);
252 if ($lingo == 4) {
253 return unpack("Cna*", $payload);
254 } else {
255 return unpack("CCa*", $payload);
256 }
257}
258
259sub _send {
260 my $self = shift;
261 my $data = shift;
262 my $l = length($data);
263 my $c;
264
265 printf("Sending %s\n", $self->_hexstring($data)) if $self->{-debug};
266
267 $c = $self->{-serial}->write($data);
268 unless(defined($c)) {
269 $self->{-error} = 'write failed';
270 return undef;
271 }
272
273 if ($c != $l) {
274 $self->{-error} = 'incomplete write';
275 return undef;
276 }
277
278 return 1;
279}
280
281sub _fillbuf {
282 my $self = shift;
283 my $timeout = shift || 2;
284 my $to;
285
286 # Read from the port until we have a complete message in the buffer,
287 # or until we haven't read any new data for $timeout seconds, whatever
288 # comes first.
289
290 $to = $timeout;
291
292 while(!$self->_message_in_buffer() && $to > 0) {
293 my ($c, $s) = $self->{-serial}->read(255);
294 if ($c == 0) {
295 # No data read
296 select(undef, undef, undef, 0.1);
297 $to -= 0.1;
298 } else {
299 $self->{-inbuf} .= $s;
300 $to = $timeout;
301 }
302 }
303 if ($self->_message_in_buffer()) {
304 # There is a complete message in the buffer
305 return $self->_message();
306 } else {
307 # Timeout occured
308 $self->{-error} = 'Timeout reading from port';
309 return undef;
310 }
311}
312
313sub _message_in_buffer {
314 my $self = shift;
315 my $sp = 0;
316 my $i;
317
318 $i = index($self->{-inbuf}, "\xFF\x55", $sp);
319 while ($i != -1) {
320 my $header;
321 my $len;
322 my $large = 0;
323
324
325 $header = substr($self->{-inbuf}, $i, 3);
326 if (length($header) != 3) {
327 # Runt frame
328 return ();
329 }
330 $len = unpack("x2C", $header);
331 if ($len == 0) {
332 # Possible large frame
333 $header = substr($self->{-inbuf}, $i, 5);
334 if (length($header) != 5) {
335 # Runt frame
336 return ();
337 }
338 $large = 1;
339 $len = unpack("x3n", $header);
340 }
341
342 # Add framing, checksum and length
343 $len = $len+3+($large?3:1);
344
345 if (length($self->{-inbuf}) < ($i+$len)) {
346 # Buffer too short to hold rest of frame. Try again.
347 $sp = $i+1;
348 $i = index($self->{-inbuf}, "\xFF\x55", $sp);
349 } else {
350 return ($i, $len);
351 }
352 }
353
354 # No complete message found
355 return ();
356}
357
358
359sub _message {
360 my $self = shift;
361 my $start;
362 my $len;
363 my $m;
364
365 # Return the first complete message in the buffer, removing the message
366 # and everything before it from the buffer.
367 ($start, $len) = $self->_message_in_buffer();
368 unless(defined($start)) {
369 $self->{-error} = 'No complete message in buffer';
370 return undef;
371 }
372 $m = substr($self->{-inbuf}, $start, $len);
373 $self->{-inbuf} = substr($self->{-inbuf}, $start+$len);
374
375 return $m;
376}
377
378sub _hexstring {
379 my $self = shift;
380 my $s = shift;
381
382 return join("", map { (($_ == 0x20) || isgraph(chr($_)))?chr($_):sprintf("\\x%02x", $_) }
383 unpack("C" x length($s), $s));
384}
385
3861;
diff --git a/tools/iap/Makefile b/tools/iap/Makefile
new file mode 100644
index 0000000000..86f3760de6
--- /dev/null
+++ b/tools/iap/Makefile
@@ -0,0 +1,7 @@
1default: test
2
3test:
4 perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' ipod*.t
5
6moduletest:
7 perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' device-ipod.t
diff --git a/tools/iap/README b/tools/iap/README
new file mode 100644
index 0000000000..dbd050feb4
--- /dev/null
+++ b/tools/iap/README
@@ -0,0 +1,23 @@
1These are perl test scripts for validating the IAP implementation.
2Also included is a perl class for talking to an iPod via the serial
3port. You will probably need Linux to use this.
4
5Run "make moduletest" to test the perl module itself. This will not
6require any serial connection, or even an iPod, for that matter.
7
8Run "make test" to run the iPod communication tests themselves.
9
10In order to test make sure
11
12- the iPod is connected to a serial port
13- the test scripts assume that this port is /dev/ttyUSB0. Change
14 as neccessary
15
16Sometimes, tests will time out instead of giving the desired result.
17As long as the timeouts are not reproducable this is usually not a
18problem. The serial port is known to be unreliable, and devices will
19retransmit. This happens even with the OF.
20
21The tests were designed against an iPod Touch 2G as a reference device.
22Some older iPods fail some of the test, even with the OF, because of
23behaviour changes in later firmware releases by Apple.
diff --git a/tools/iap/device-ipod.t b/tools/iap/device-ipod.t
new file mode 100644
index 0000000000..607184e331
--- /dev/null
+++ b/tools/iap/device-ipod.t
@@ -0,0 +1,74 @@
1use Test::More qw( no_plan );
2use strict;
3
4BEGIN { use_ok('Device::iPod'); }
5require_ok('Device::iPod');
6
7my $ipod = Device::iPod->new();
8my $m;
9my ($l, $c, $p);
10
11isa_ok($ipod, 'Device::iPod');
12
13# Frame a short command
14$m = $ipod->_frame_cmd("\x00\x02\x00\x06");
15ok(defined($m) && ($m eq "\xFF\x55\x04\x00\x02\x00\x06\xF4"), "Framed command valid");
16
17# Frame a long command
18$m = $ipod->_frame_cmd("\x00" x 1024);
19ok(defined($m) && ($m eq "\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC"), "Long framed command valid");
20
21# Frame an overly long command
22$m = $ipod->_frame_cmd("\x00" x 65537);
23ok(!defined($m) && ($ipod->error() =~ 'Command too long'), "Overly long command failed");
24
25# Unframe a short command
26($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x04\x00\x02\x00\x06\xF4");
27ok(defined($l) && ($l == 0x00) && ($c == 0x02) && ($p eq "\x00\x06"), "Unframed short command");
28
29# Unframe a long command
30($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC");
31ok(defined($l) && ($l == 0x00) && ($c == 0x00) && ($p eq "\x00" x 1022), "Unframed long command");
32
33# Frame without sync byte
34($l, $c, $p) = $ipod->_unframe_cmd("\x00");
35ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without sync byte failed");
36
37# Frame without SOP byte
38($l, $c, $p) = $ipod->_unframe_cmd("\xFF");
39ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without SOP byte failed");
40
41# Frame with length 0
42($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x00\x00");
43ok(!defined($l) && ($ipod->error() =~ /Length is 0/), "Frame with length 0 failed");
44
45# Too short frame
46($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00");
47ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Too short frame failed");
48
49# Invalid checksum
50($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00\x00\x00");
51ok(!defined($l) && ($ipod->error() =~ /Invalid checksum/), "Invalid checksum failed");
52
53# Find a message in a string
54$ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4";
55($c, $l) = $ipod->_message_in_buffer();
56ok(defined($l) && ($c == 6) && ($l == 8), "Found message in buffer");
57
58# Return message from a string
59$ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4\x00";
60$m = $ipod->_message();
61ok(defined($m) && ($ipod->{-inbuf} eq "\x00"), "Retrieved message from buffer");
62
63# Return two messages from buffer
64$ipod->{-inbuf} = "\xffU\x04\x00\x02\x00\x13\xe7\xffU\x02\x00\x14\xea";
65$m = $ipod->_message();
66subtest "First message" => sub {
67 ok(defined($m), "Message received");
68 is($m, "\xffU\x04\x00\x02\x00\x13\xe7");
69};
70$m = $ipod->_message();
71subtest "Second message" => sub {
72 ok(defined($m), "Message received");
73 is($m, "\xffU\x02\x00\x14\xea");
74};
diff --git a/tools/iap/iap-verbose.pl b/tools/iap/iap-verbose.pl
new file mode 100644
index 0000000000..ed25de7548
--- /dev/null
+++ b/tools/iap/iap-verbose.pl
@@ -0,0 +1,1856 @@
1#!/usr/bin/perl -w
2
3package iap::decode;
4
5use Device::iPod;
6use Data::Dumper;
7use strict;
8
9sub new {
10 my $class = shift;
11 my $self = {-state => {}};
12
13 return bless($self, $class);
14}
15
16sub display {
17 my $self = shift;
18 my $lingo = shift;
19 my $command = shift;
20 my $data = shift;
21 my $name;
22 my $handler;
23 my $r;
24
25
26 $name = sprintf("_h_%02x_%04x", $lingo, $command);
27 $handler = $self->can($name);
28 if ($handler) {
29 unless(exists($self->{-state}->{$name})) {
30 $self->{-state}->{$name} = {};
31 }
32 $r = $handler->($self, $data, $self->{-state}->{$name});
33 } else {
34 $r = $self->generic($lingo, $command, $data);
35 }
36
37 printf("\n");
38 return $r;
39}
40
41sub generic {
42 my $self = shift;
43 my $lingo = shift;
44 my $command = shift;
45 my $data = shift;
46
47 printf("Unknown command\n");
48 printf(" Lingo: 0x%02x\n", $lingo);
49 printf(" Command 0x%04x\n", $command);
50 printf(" Data: %s\n", Device::iPod->_hexstring($data));
51
52 exit(1);
53
54 return 1;
55}
56
57sub _h_00_0001 {
58 my $self = shift;
59 my $data = shift;
60 my $state = shift;
61 my $lingo = shift;
62
63 $lingo = unpack("C", $data);
64
65 printf("Identify (0x00, 0x01) D->I\n");
66 printf(" Lingo: 0x%02x\n", $lingo);
67
68 return 1;
69}
70
71sub _h_00_0002 {
72 my $self = shift;
73 my $data = shift;
74 my $state = shift;
75 my $res;
76 my $cmd;
77 my $delay;
78
79 ($res, $cmd, $delay) = unpack("CCN", $data);
80
81 printf("ACK (0x00, 0x02) I->D\n");
82 printf(" Acknowledged command: 0x%02x\n", $cmd);
83 printf(" Status: %s (%d)\n",
84 ("Success",
85 "ERROR: Unknown Database Category",
86 "ERROR: Command Failed",
87 "ERROR: Out Of Resource",
88 "ERROR: Bad Parameter",
89 "ERROR: Unknown ID",
90 "Command Pending",
91 "ERROR: Not Authenticated",
92 "ERROR: Bad Authentication Version",
93 "ERROR: Accessory Power Mode Request Failed",
94 "ERROR: Certificate Invalid",
95 "ERROR: Certificate permissions invalid",
96 "ERROR: File is in use",
97 "ERROR: Invalid file handle",
98 "ERROR: Directory not empty",
99 "ERROR: Operation timed out",
100 "ERROR: Command unavailable in this iPod mode",
101 "ERROR: Invalid accessory resistor ID",
102 "Reserved",
103 "Reserved",
104 "Reserved",
105 "ERROR: Maximum number of accessory connections already reached")[$res], $res);
106 if ($res == 6) {
107 $delay = unpack("xxN", $data);
108 printf(" Delay: %d ms\n", $delay);
109 }
110
111 return 1;
112}
113
114sub _h_00_0005 {
115 my $self = shift;
116 my $data = shift;
117 my $state = shift;
118
119 printf("EnterRemoteUIMode (0x00, 0x05) D->I\n");
120
121 return 1;
122}
123
124sub _h_00_000d {
125 my $self = shift;
126 my $data = shift;
127 my $state = shift;
128
129 printf("RequestiPodModelNum (0x00, 0x0D) D->I\n");
130
131 return 1;
132}
133
134sub _h_00_000e {
135 my $self = shift;
136 my $data = shift;
137 my $state = shift;
138 my ($modelnum, $name);
139
140 ($modelnum, $name) = unpack("NZ*", $data);
141
142 printf("ReturniPodModelNum (0x00, 0x0E) I->D\n");
143 printf(" Model number: %08x\n", $modelnum);
144 printf(" Model name: %s\n", $name);
145
146 return 1;
147}
148
149sub _h_00_000f {
150 my $self = shift;
151 my $data = shift;
152 my $state = shift;
153 my $lingo;
154
155 $lingo = unpack("C", $data);
156
157 printf("RequestLingoProtocolVersion (0x00, 0x0F) D->I\n");
158 printf(" Lingo: 0x%02x\n", $lingo);
159
160 return 1;
161}
162
163sub _h_00_0010 {
164 my $self = shift;
165 my $data = shift;
166 my $state = shift;
167 my ($lingo, $maj, $min);
168
169 ($lingo, $maj, $min) = unpack("CCC", $data);
170
171 printf("ReturnLingoProtocolVersion (0x00, 0x10) I->D\n");
172 printf(" Lingo: 0x%02x\n", $lingo);
173 printf(" Version: %d.%02d\n", $maj, $min);
174
175 return 1;
176}
177
178
179sub _h_00_0013 {
180 my $self = shift;
181 my $data = shift;
182 my $state = shift;
183 my @lingolist;
184 my ($lingoes, $options, $devid);
185
186 ($lingoes, $options, $devid) = unpack("N3", $data);
187
188 foreach (0..31) {
189 push(@lingolist, $_) if ($lingoes & (1 << $_));
190 }
191
192 printf("IdentifyDeviceLingoes (0x00, 0x13) D->I\n");
193 printf(" Supported lingoes: %s\n", join(", ", @lingolist));
194 printf(" Options:\n");
195 printf(" Authentication: %s\n", ("None", "Defer (1.0)", "Immediate (2.0)", "Reserved")[$options & 0x03]);
196 printf(" Power: %s\n", ("Low", "Intermittent high", "Reserved", "Constant high")[($options & 0x0C) >> 2]);
197 printf(" Device ID: 0x%08x\n", $devid);
198
199 delete($self->{-state}->{'_h_00_0015'});
200
201 return 1;
202}
203
204sub _h_00_0014 {
205 my $self = shift;
206 my $data = shift;
207 my $state = shift;
208
209 printf("GetDevAuthenticationInfo (0x00, 0x14) I->D\n");
210
211 return 1;
212}
213
214sub _h_00_0015 {
215 my $self = shift;
216 my $data = shift;
217 my $state = shift;
218 my ($amaj, $amin, $curidx, $maxidx, $certdata);
219
220 $state->{-curidx} = -1 unless(exists($state->{-curidx}));
221 $state->{-maxidx} = -1 unless(exists($state->{-maxidx}));
222 $state->{-cert} = '' unless(exists($state->{-cert}));
223
224 ($amaj, $amin, $curidx, $maxidx, $certdata) = unpack("CCCCa*", $data);
225
226 printf("RetDevAuthenticationInfo (0x00, 0x15) D->I\n");
227 printf(" Authentication version: %d.%d\n", $amaj, $amin);
228 printf(" Segment: %d of %d\n", $curidx, $maxidx);
229
230 if ($curidx-1 != $state->{-curidx}) {
231 printf(" WARNING! Out of order segment\n");
232 return 0;
233 }
234
235 if (($maxidx != $state->{-maxidx}) && ($state->{-maxidx} != -1)) {
236 printf(" WARNING! maxidx changed midstream\n");
237 return 0;
238 }
239
240 if ($curidx > $maxidx) {
241 printf(" WARNING! Too many segments\n");
242 return 0;
243 }
244
245 $state->{-curidx} = $curidx;
246 $state->{-maxidx} = $maxidx;
247 $state->{-cert} .= $certdata;
248
249 if ($curidx == $maxidx) {
250 printf(" Certificate: %s\n", Device::iPod->_hexstring($state->{-cert}));
251 }
252
253 return 1;
254}
255
256sub _h_00_0016 {
257 my $self = shift;
258 my $data = shift;
259 my $state = shift;
260 my $res;
261
262 $res = unpack("C", $data);
263
264 printf("AckDevAuthenticationInfo (0x00, 0x16) I->D\n");
265 printf(" Result: ");
266 if ($res == 0x00) {
267 printf("Authentication information supported\n");
268 } elsif ($res == 0x08) {
269 printf("Authentication information unpported\n");
270 } elsif ($res == 0x0A) {
271 printf("Certificate invalid\n");
272 } elsif ($res == 0x0B) {
273 printf("Certificate permissions are invalid\n");
274 } else {
275 printf("Unknown result 0x%02x\n", $res);
276 }
277
278 return 1;
279}
280
281sub _h_00_0017 {
282 my $self = shift;
283 my $data = shift;
284 my $state = shift;
285 my ($challenge, $retry);
286
287 printf("GetDevAuthenticationSignature (0x00, 0x17) I->D\n");
288
289 if (length($data) == 17) {
290 ($challenge, $retry) = unpack("a16C", $data);
291 } elsif (length($data) == 21) {
292 ($challenge, $retry) = unpack("a20C", $data);
293 } else {
294 printf(" WARNING! Unsupported data length: %d\n", length($data));
295 return 0;
296 }
297
298 printf(" Challenge: %s (%d bytes)\n", Device::iPod->_hexstring($challenge), length($challenge));
299 printf(" Retry counter: %d\n", $retry);
300
301 return 1;
302}
303
304sub _h_00_0018 {
305 my $self = shift;
306 my $data = shift;
307 my $state = shift;
308 my $reply;
309
310 printf("RetDevAuthenticationSignature (0x00, 0x18) D->I\n");
311 printf(" Data: %s\n", Device::iPod->_hexstring($data));
312
313 return 1;
314}
315
316sub _h_00_0019 {
317 my $self = shift;
318 my $data = shift;
319 my $state = shift;
320 my $res;
321
322 $res = unpack("C", $data);
323
324 printf("AckiPodAuthenticationInfo (0x00, 0x19) I->D\n");
325 printf(" Status: %s (%d)\n", (
326 "OK")[$res], $res);
327
328 return 1;
329}
330
331sub _h_00_0024 {
332 my $self = shift;
333 my $data = shift;
334 my $state = shift;
335
336 printf("GetiPodOptions (0x00, 0x24) D->I\n");
337
338 return 1;
339}
340
341sub _h_00_0025 {
342 my $self = shift;
343 my $data = shift;
344 my $state = shift;
345 my ($ophi, $oplo);
346
347 ($ophi, $oplo) = unpack("NN", $data);
348
349 printf("RetiPodOptions (0x00, 0x25) I->D\n");
350 printf(" Options:\n");
351 printf(" iPod supports SetiPodPreferences\n") if ($oplo & 0x02);
352 printf(" iPod supports video\n") if ($oplo & 0x01);
353
354 return 1;
355}
356
357
358sub _h_00_0027 {
359 my $self = shift;
360 my $data = shift;
361 my $state = shift;
362 my $acctype;
363 my $accparam;
364
365 $acctype = unpack("C", $data);
366
367 printf("GetAccessoryInfo (0x00, 0x27) I->D\n");
368 printf(" Accessory Info Type: %s (%d)\n", (
369 "Info capabilities",
370 "Name",
371 "Minimum supported iPod firmware version",
372 "Minimum supported lingo version",
373 "Firmware version",
374 "Hardware version",
375 "Manufacturer",
376 "Model Number",
377 "Serial Number",
378 "Maximum payload size")[$acctype], $acctype);
379 if ($acctype == 0x02) {
380 my ($modelid, $maj, $min, $rev);
381
382 ($modelid, $maj, $min, $rev) = unpack("xNCCC", $data);
383 printf(" Model ID: 0x%04x\n", $modelid);
384 printf(" iPod Firmware: %d.%d.%d\n", $maj, $min, $rev);
385 } elsif ($acctype == 0x03) {
386 my $lingo;
387
388 $lingo = unpack("xC", $data);
389 printf(" Lingo: 0x%02x\n", $lingo);
390 }
391
392 return 1;
393}
394
395sub _h_00_0028 {
396 my $self = shift;
397 my $data = shift;
398 my $state = shift;
399 my $acctype;
400 my $accparam;
401
402 $acctype = unpack("C", $data);
403
404 printf("RetAccessoryInfo (0x00, 0x28) D->I\n");
405 printf(" Accessory Info Type: %s (%d)\n", (
406 "Info capabilities",
407 "Name",
408 "Minimum supported iPod firmware version",
409 "Minimum supported lingo version",
410 "Firmware version",
411 "Hardware version",
412 "Manufacturer",
413 "Model Number",
414 "Serial Number",
415 "Maximum payload size")[$acctype], $acctype);
416
417 if ($acctype == 0x00) {
418 $accparam = unpack("xN", $data);
419 printf(" Accessory Info capabilities\n") if ($accparam & 0x01);
420 printf(" Accessory name\n") if ($accparam & 0x02);
421 printf(" Accessory minimum supported iPod firmware\n") if ($accparam & 0x04);
422 printf(" Accessory minimum supported lingo version\n") if ($accparam & 0x08);
423 printf(" Accessory firmware version\n") if ($accparam & 0x10);
424 printf(" Accessory hardware version\n") if ($accparam & 0x20);
425 printf(" Accessory manufacturer\n") if ($accparam & 0x40);
426 printf(" Accessory model number\n") if ($accparam & 0x80);
427 printf(" Accessory serial number\n") if ($accparam & 0x100);
428 printf(" Accessory incoming max packet size\n") if ($accparam & 0x200);
429 }
430
431 if ($acctype ~~ [0x01, 0x06, 0x07, 0x08]) {
432 $accparam = unpack("xZ*", $data);
433 printf(" Data: %s\n", $accparam);
434 }
435
436 if ($acctype == 0x02) {
437 $accparam = [ unpack("xNCCC", $data) ];
438 printf(" Model ID: %08x\n", $accparam->[0]);
439 printf(" Firmware version: %d.%02d.%02d\n", $accparam->[1], $accparam->[2], $accparam->[3]);
440 }
441
442 if ($acctype == 0x03) {
443 $accparam = [ unpack("xCCC", $data) ];
444 printf(" Lingo: %02x\n", $accparam->[0]);
445 printf(" Version: %d.%02d\n", $accparam->[1], $accparam->[2]);
446 }
447
448 if ($acctype ~~ [0x04, 0x05]) {
449 $accparam = [ unpack("xCCC", $data) ];
450 printf(" Version: %d.%02d.%02d\n", @{$accparam});
451 }
452
453 return 1;
454}
455
456sub _h_00_0029 {
457 my $self = shift;
458 my $data = shift;
459 my $state = shift;
460 my $class;
461
462 $class = unpack("C", $data);
463
464 printf("GetiPodPreferences (0x00, 0x29) D->I\n");
465 printf(" Class: %s (%d)\n", (
466 "Video out setting",
467 "Screen configuration",
468 "Video signal format",
469 "Line Out usage",
470 "(Reserved)",
471 "(Reserved)",
472 "(Reserved)",
473 "(Reserved)",
474 "Video out connection",
475 "Closed captioning",
476 "Video aspect ratio",
477 "(Reserved)",
478 "Subtitles",
479 "Video alternate audio channel")[$class], $class);
480
481 return 1;
482}
483
484sub _h_00_002b {
485 my $self = shift;
486 my $data = shift;
487 my $state = shift;
488 my ($class, $setting);
489
490 ($class, $setting) = unpack("CC", $data);
491
492 printf("SetiPodPreferences (0x00, 0x2B) D->I\n");
493 printf(" Class: %s (%d)\n", (
494 "Video out setting",
495 "Screen configuration",
496 "Video signal format",
497 "Line out usage",
498 "Reserved",
499 "Reserved",
500 "Reserved",
501 "Reserved",
502 "Video-out connection",
503 "Closed captioning",
504 "Video monitor aspect ratio",
505 "Reserved",
506 "Subtitles",
507 "Video alternate audio channel")[$class], $class);
508 printf(" Setting: %s (%d)\n", (
509 ["Off",
510 "On",
511 "Ask user"],
512 ["Fill screen",
513 "Fit to screen edge"],
514 ["NTSC",
515 "PAL"],
516 ["Not used",
517 "Used"],
518 [],
519 [],
520 [],
521 [],
522 ["None",
523 "Composite",
524 "S-video",
525 "Component"],
526 ["Off",
527 "On"],
528 ["4:3",
529 "16:9"],
530 [],
531 ["Off",
532 "On"],
533 ["Off",
534 "On"])[$class][$setting], $setting);
535
536 return 1;
537}
538
539sub _h_00_0038 {
540 my $self = shift;
541 my $data = shift;
542 my $state = shift;
543 my $transid;
544
545 $transid = unpack("n", $data);
546
547 printf("StartIDPS (0x00, 0x38) D->I\n");
548 printf(" TransID: %d\n", $transid);
549
550 return 1;
551}
552
553sub _h_00_003b {
554 my $self = shift;
555 my $data = shift;
556 my $state = shift;
557 my ($transid, $status);
558
559 ($transid, $status) = unpack("nC", $data);
560
561 printf("EndIDPS (0x00, 0x3B) D->I\n");
562 printf(" TransID: %d\n", $transid);
563 printf(" Action: %s (%d)\n", (
564 "Finished",
565 "Reset")[$status], $status);
566
567 return 1;
568}
569
570sub _h_00_004b {
571 my $self = shift;
572 my $data = shift;
573 my $state = shift;
574 my $lingo;
575
576 $lingo = unpack("C", $data);
577
578 printf("GetiPodOptionsForLingo (0x00, 0x4B) D->I\n");
579 printf(" Lingo: 0x%02x\n", $lingo);
580
581 return 1;
582}
583
584sub _h_02_0000 {
585 my $self = shift;
586 my $data = shift;
587 my $state = shift;
588 my @keys;
589
590 @keys = unpack("CCCC", $data);
591
592 printf("ContextButtonStatus (0x02, 0x00) D->I\n");
593 printf(" Buttons:\n");
594 printf(" Play/Pause\n") if ($keys[0] & 0x01);
595 printf(" Volume Up\n") if ($keys[0] & 0x02);
596 printf(" Volume Down\n") if ($keys[0] & 0x04);
597 printf(" Next Track\n") if ($keys[0] & 0x08);
598 printf(" Previous Track\n") if ($keys[0] & 0x10);
599 printf(" Next Album\n") if ($keys[0] & 0x20);
600 printf(" Previous Album\n") if ($keys[0] & 0x40);
601 printf(" Stop\n") if ($keys[0] & 0x80);
602
603 if (exists($keys[1])) {
604 printf(" Play/Resume\n") if ($keys[1] & 0x01);
605 printf(" Pause\n") if ($keys[1] & 0x02);
606 printf(" Mute toggle\n") if ($keys[1] & 0x04);
607 printf(" Next Chapter\n") if ($keys[1] & 0x08);
608 printf(" Previous Chapter\n") if ($keys[1] & 0x10);
609 printf(" Next Playlist\n") if ($keys[1] & 0x20);
610 printf(" Previous Playlist\n") if ($keys[1] & 0x40);
611 printf(" Shuffle Setting Advance\n") if ($keys[1] & 0x80);
612 }
613
614 if (exists($keys[2])) {
615 printf(" Repeat Setting Advance\n") if ($keys[2] & 0x01);
616 printf(" Power On\n") if ($keys[2] & 0x02);
617 printf(" Power Off\n") if ($keys[2] & 0x04);
618 printf(" Backlight for 30 seconds\n") if ($keys[2] & 0x08);
619 printf(" Begin FF\n") if ($keys[2] & 0x10);
620 printf(" Begin REW\n") if ($keys[2] & 0x20);
621 printf(" Menu\n") if ($keys[2] & 0x40);
622 printf(" Select\n") if ($keys[2] & 0x80);
623 }
624
625 if (exists($keys[3])) {
626 printf(" Up Arrow\n") if ($keys[3] & 0x01);
627 printf(" Down Arrow\n") if ($keys[3] & 0x02);
628 printf(" Backlight off\n") if ($keys[3] & 0x04);
629 }
630
631 return 1;
632}
633
634sub _h_02_0001 {
635 my $self = shift;
636 my $data = shift;
637 my $state = shift;
638 my $res;
639 my $cmd;
640
641 ($res, $cmd) = unpack("CC", $data);
642
643 printf("ACK (0x02, 0x01) I->D\n");
644 printf(" Acknowledged command: 0x%02x\n", $cmd);
645 printf(" Status: %s (%d)\n",
646 ("Success",
647 "ERROR: Unknown Database Category",
648 "ERROR: Command Failed",
649 "ERROR: Out Of Resource",
650 "ERROR: Bad Parameter",
651 "ERROR: Unknown ID",
652 "Command Pending",
653 "ERROR: Not Authenticated",
654 "ERROR: Bad Authentication Version",
655 "ERROR: Accessory Power Mode Request Failed",
656 "ERROR: Certificate Invalid",
657 "ERROR: Certificate permissions invalid",
658 "ERROR: File is in use",
659 "ERROR: Invalid file handle",
660 "ERROR: Directory not empty",
661 "ERROR: Operation timed out",
662 "ERROR: Command unavailable in this iPod mode",
663 "ERROR: Invalid accessory resistor ID",
664 "Reserved",
665 "Reserved",
666 "Reserved",
667 "ERROR: Maximum number of accessory connections already reached")[$res], $res);
668
669 return 1;
670}
671
672sub _h_03_0000 {
673 my $self = shift;
674 my $data = shift;
675 my $state = shift;
676 my $res;
677 my $cmd;
678
679 ($res, $cmd) = unpack("CC", $data);
680
681 printf("ACK (0x03, 0x00) I->D\n");
682 printf(" Acknowledged command: 0x%02x\n", $cmd);
683 printf(" Status: %s (%d)\n",
684 ("Success",
685 "ERROR: Unknown Database Category",
686 "ERROR: Command Failed",
687 "ERROR: Out Of Resource",
688 "ERROR: Bad Parameter",
689 "ERROR: Unknown ID",
690 "Command Pending",
691 "ERROR: Not Authenticated",
692 "ERROR: Bad Authentication Version",
693 "ERROR: Accessory Power Mode Request Failed",
694 "ERROR: Certificate Invalid",
695 "ERROR: Certificate permissions invalid",
696 "ERROR: File is in use",
697 "ERROR: Invalid file handle",
698 "ERROR: Directory not empty",
699 "ERROR: Operation timed out",
700 "ERROR: Command unavailable in this iPod mode",
701 "ERROR: Invalid accessory resistor ID",
702 "Reserved",
703 "Reserved",
704 "Reserved",
705 "ERROR: Maximum number of accessory connections already reached")[$res], $res);
706
707 return 1;
708}
709
710sub _h_03_0008 {
711 my $self = shift;
712 my $data = shift;
713 my $state = shift;
714 my $events;;
715
716 $events = unpack("N", $data);
717
718 printf("SetRemoteEventsNotification (0x03, 0x08) D->I\n");
719 printf(" Events:\n");
720 printf(" Track position in ms\n") if ($events & 0x00000001);
721 printf(" Track playback index\n") if ($events & 0x00000002);
722 printf(" Chapter index\n") if ($events & 0x00000004);
723 printf(" Play status\n") if ($events & 0x00000008);
724 printf(" Mute/UI volume\n") if ($events & 0x00000010);
725 printf(" Power/Battery\n") if ($events & 0x00000020);
726 printf(" Equalizer setting\n") if ($events & 0x00000040);
727 printf(" Shuffle setting\n") if ($events & 0x00000080);
728 printf(" Repeat setting\n") if ($events & 0x00000100);
729 printf(" Date and time setting\n") if ($events & 0x00000200);
730 printf(" Alarm setting\n") if ($events & 0x00000400);
731 printf(" Backlight state\n") if ($events & 0x00000800);
732 printf(" Hold switch state\n") if ($events & 0x00001000);
733 printf(" Sound check state\n") if ($events & 0x00002000);
734 printf(" Audiobook speed\n") if ($events & 0x00004000);
735 printf(" Track position in s\n") if ($events & 0x00008000);
736 printf(" Mute/UI/Absolute volume\n") if ($events & 0x00010000);
737
738 return 1;
739}
740
741sub _h_03_0009 {
742 my $self = shift;
743 my $data = shift;
744 my $state = shift;
745 my $event;
746 my $eventdata;
747
748 $event = unpack("C", $data);
749
750 printf("RemoteEventNotification (0x03, 0x09) I->D\n");
751 printf(" Event: %s (%d)\n", (
752 "Track position in ms",
753 "Track playback index",
754 "Chapter index",
755 "Play status",
756 "Mute/UI volume",
757 "Power/Battery",
758 "Equalizer setting",
759 "Shuffle setting",
760 "Repeat setting",
761 "Date and time setting",
762 "Alarm setting",
763 "Backlight state",
764 "Hold switch state",
765 "Sound check state",
766 "Audiobook speed",
767 "Track position in s",
768 "Mute/UI/Absolute volume")[$event], $event);
769
770 if ($event == 0x00) {
771 $eventdata = unpack("xN", $data);
772 printf(" Position: %d ms\n", $eventdata);
773 } elsif ($event == 0x01) {
774 $eventdata = unpack("xN", $data);
775 printf(" Track: %d\n", $eventdata);
776 } elsif ($event == 0x02) {
777 $eventdata = [ unpack("xNnn", $data) ];
778 printf(" Track: %d\n", $eventdata->[0]);
779 printf(" Chapter count: %d\n", $eventdata->[1]);
780 printf(" Chapter index: %d\n", $eventdata->[2]);
781 } elsif ($event == 0x03) {
782 $eventdata = unpack("xC", $data);
783 printf(" Status: %s (%d)\n", (
784 "Stopped",
785 "Playing",
786 "Paused",
787 "FF",
788 "REW",
789 "End FF/REW")[$eventdata], $eventdata);
790 } elsif ($event == 0x04) {
791 $eventdata = [ unpack("xCC") ];
792 printf(" Mute: %s\n", $eventdata->[0]?"Off":"On");
793 printf(" Volume: %d\n", $eventdata->[1]);
794 } elsif ($event == 0x0F) {
795 $eventdata = unpack("xn", $data);
796 printf(" Position: %d s\n", $eventdata);
797 }
798
799 return 1;
800}
801
802sub _h_03_000c {
803 my $self = shift;
804 my $data = shift;
805 my $state = shift;
806 my $info;
807
808 $info = unpack("C", $data);
809
810 printf("GetiPodStateInfo (0x03, 0x0C) D->I\n");
811 printf(" Info to get: %s (%d)\n", (
812 "Track time in ms",
813 "Track playback index",
814 "Chapter information",
815 "Play status",
816 "Mute and volume information",
817 "Power and battery status",
818 "Equalizer setting",
819 "Shuffle setting",
820 "Repeat setting",
821 "Date and time",
822 "Alarm state and time",
823 "Backlight state",
824 "Hold switch state",
825 "Audiobook speed",
826 "Track time in seconds",
827 "Mute/UI/Absolute volume")[$info], $info);
828
829 return 1;
830}
831
832sub _h_03_000d {
833 my $self = shift;
834 my $data = shift;
835 my $state = shift;
836 my $type;
837 my $info;
838
839 $type = unpack("C", $data);
840
841 printf("RetiPodStateInfo (0x03, 0x0E) D->I\n");
842
843 if ($type == 0x00) {
844 $info = unpack("xN", $data);
845 printf(" Type: Track position\n");
846 printf(" Position: %d ms\n", $info);
847 } elsif ($type == 0x01) {
848 $info = unpack("xN", $data);
849 printf(" Type: Track index\n");
850 printf(" Index: %d\n", $info);
851 } elsif ($type == 0x02) {
852 $info = [ unpack("xNnn", $data) ];
853 printf(" Type: Chapter Information\n");
854 printf(" Playing Track: %d\n", $info->[0]);
855 printf(" Chapter count: %d\n", $info->[1]);
856 printf(" Chapter index: %d\n", $info->[2]);
857 } elsif ($type == 0x03) {
858 $info = unpack("xC", $data);
859 printf(" Type: Play status\n");
860 printf(" Status: %s (%d)\n", (
861 "Stopped",
862 "Playing",
863 "Paused",
864 "FF",
865 "REW",
866 "End FF/REW")[$info], $info);
867 } elsif ($type == 0x04) {
868 $info = [unpack("xCC", $data)];
869 printf(" Type: Mute/Volume\n");
870 printf(" Mute State: %s\n", $info->[0]?"On":"Off");
871 printf(" Volume level: %d\n", $info->[1]);
872 } elsif ($type == 0x05) {
873 $info = [unpack("xCC", $data)];
874 printf(" Type: Battery Information\n");
875 printf(" Power state: %s (%d)\n", (
876 "Internal, low power (<30%)",
877 "Internal",
878 "External battery pack, no charging",
879 "External power, no charging",
880 "External power, charging",
881 "External power, charged")[$info->[0]], $info->[0]);
882 printf(" Battery level: %d%%\n", $info->[1]*100/255);
883 } elsif ($type == 0x06) {
884 $info = [unpack("xN", $data)];
885 printf(" Type: Equalizer\n");
886 printf(" Index: %d\n", $info->[0]);
887 } elsif ($type == 0x07) {
888 $info = [unpack("xC", $data)];
889 printf(" Type: Shuffle\n");
890 printf(" Shuffle State: %s\n", $info->[0]?"On":"Off");
891 } elsif ($type == 0x08) {
892 $info = [unpack("xC", $data)];
893 printf(" Type: Repeat\n");
894 printf(" Repeat State: %s\n", $info->[0]?"On":"Off");
895 } elsif ($type == 0x09) {
896 $info = [unpack("xnCCCC", $data)];
897 printf(" Type: Date\n");
898 printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]);
899 } elsif ($type == 0x0A) {
900 $info = [unpack("xCCC", $data)];
901 printf(" Type: Alarm\n");
902 printf(" Alarm State: %s\n", $info->[0]?"On":"Off");
903 printf(" Time: %02d:%02d\n", $info->[1], $info->[2]);
904 } elsif ($type == 0x0B) {
905 $info = [unpack("xC", $data)];
906 printf(" Type: Backlight\n");
907 printf(" Backlight State: %s\n", $info->[0]?"On":"Off");
908 } elsif ($type == 0x0C) {
909 $info = [unpack("xC", $data)];
910 printf(" Type: Hold switch\n");
911 printf(" Switch State: %s\n", $info->[0]?"On":"Off");
912 } elsif ($type == 0x0D) {
913 $info = [unpack("xC", $data)];
914 printf(" Type: Sound check\n");
915 printf(" Sound check: %s\n", $info->[0]?"On":"Off");
916 } elsif ($type == 0x0E) {
917 $info = [unpack("xC", $data)];
918 printf(" Type: Audiobook speed\n");
919 printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved");
920 } elsif ($type == 0x0F) {
921 $info = unpack("xN", $data);
922 printf(" Type: Track position\n");
923 printf(" Position: %d s\n", $info);
924 } elsif ($type == 0x10) {
925 $info = [unpack("xCCC", $data)];
926 printf(" Type: Mute/UI/Absolute volume\n");
927 printf(" Mute State: %s\n", $info->[0]?"On":"Off");
928 printf(" UI Volume level: %d\n", $info->[1]);
929 printf(" Absolute Volume: %d\n", $info->[2]);
930 } else {
931 printf(" Reserved\n");
932 }
933
934 return 1;
935}
936
937
938sub _h_03_000e {
939 my $self = shift;
940 my $data = shift;
941 my $state = shift;
942 my $type;
943 my $info;
944
945 $type = unpack("C", $data);
946
947 printf("SetiPodStateInfo (0x03, 0x0E) D->I\n");
948
949 if ($type == 0x00) {
950 $info = unpack("xN", $data);
951 printf(" Type: Track position\n");
952 printf(" Position: %d ms\n", $info);
953 } elsif ($type == 0x01) {
954 $info = unpack("xN", $data);
955 printf(" Type: Track index\n");
956 printf(" Index: %d\n", $info);
957 } elsif ($type == 0x02) {
958 $info = unpack("xn", $data);
959 printf(" Type: Chapter index\n");
960 printf(" Index: %d\n", $info);
961 } elsif ($type == 0x03) {
962 $info = unpack("xC", $data);
963 printf(" Type: Play status\n");
964 printf(" Status: %s (%d)\n", (
965 "Stopped",
966 "Playing",
967 "Paused",
968 "FF",
969 "REW",
970 "End FF/REW")[$info], $info);
971 } elsif ($type == 0x04) {
972 $info = [unpack("xCCC", $data)];
973 printf(" Type: Mute/Volume\n");
974 printf(" Mute State: %s\n", $info->[0]?"On":"Off");
975 printf(" Volume level: %d\n", $info->[1]);
976 printf(" Restore on exit: %s\n", $info->[2]?"Yes":"No");
977 } elsif ($type == 0x06) {
978 $info = [unpack("xNC", $data)];
979 printf(" Type: Equalizer\n");
980 printf(" Index: %d\n", $info->[0]);
981 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
982 } elsif ($type == 0x07) {
983 $info = [unpack("xCC", $data)];
984 printf(" Type: Shuffle\n");
985 printf(" Shuffle State: %s\n", $info->[0]?"On":"Off");
986 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
987 } elsif ($type == 0x08) {
988 $info = [unpack("xCC", $data)];
989 printf(" Type: Repeat\n");
990 printf(" Repeat State: %s\n", $info->[0]?"On":"Off");
991 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
992 } elsif ($type == 0x09) {
993 $info = [unpack("xnCCCC", $data)];
994 printf(" Type: Date\n");
995 printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]);
996 } elsif ($type == 0x0A) {
997 $info = [unpack("xCCCC", $data)];
998 printf(" Type: Alarm\n");
999 printf(" Alarm State: %s\n", $info->[0]?"On":"Off");
1000 printf(" Time: %02d:%02d\n", $info->[1], $info->[2]);
1001 printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No");
1002 } elsif ($type == 0x0B) {
1003 $info = [unpack("xCC", $data)];
1004 printf(" Type: Backlight\n");
1005 printf(" Backlight State: %s\n", $info->[0]?"On":"Off");
1006 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
1007 } elsif ($type == 0x0D) {
1008 $info = [unpack("xCC", $data)];
1009 printf(" Type: Sound check\n");
1010 printf(" Sound check: %s\n", $info->[0]?"On":"Off");
1011 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
1012 } elsif ($type == 0x0E) {
1013 $info = [unpack("xCC", $data)];
1014 printf(" Type: Audiobook speed\n");
1015 printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved");
1016 printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No");
1017 } elsif ($type == 0x0F) {
1018 $info = unpack("xN", $data);
1019 printf(" Type: Track position\n");
1020 printf(" Position: %d s\n", $info);
1021 } elsif ($type == 0x10) {
1022 $info = [unpack("xCCCC", $data)];
1023 printf(" Type: Mute/UI/Absolute volume\n");
1024 printf(" Mute State: %s\n", $info->[0]?"On":"Off");
1025 printf(" UI Volume level: %d\n", $info->[1]);
1026 printf(" Absolute Volume: %d\n", $info->[2]);
1027 printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No");
1028 } else {
1029 printf(" Reserved\n");
1030 }
1031
1032 return 1;
1033}
1034
1035sub _h_03_000f {
1036 my $self = shift;
1037 my $data = shift;
1038 my $state = shift;
1039
1040 print("GetPlayStatus (0x03, 0x0F) D->I\n");
1041
1042 return 1;
1043}
1044
1045sub _h_03_0010 {
1046 my $self = shift;
1047 my $data = shift;
1048 my $state = shift;
1049 my ($status, $idx, $len, $pos);
1050
1051 ($status, $idx, $len, $pos) = unpack("CNNN", $data);
1052
1053 printf("RetPlayStatus (0x03, 0x10) I->D\n");
1054 printf(" Status: %s (%d)\n", (
1055 "Stopped",
1056 "Playing",
1057 "Paused")[$status], $status);
1058 if ($status != 0x00) {
1059 printf(" Track index: %d\n", $idx);
1060 printf(" Track length: %d\n", $len);
1061 printf(" Track position: %d\n", $pos);
1062 }
1063
1064 return 1;
1065}
1066
1067sub _h_03_0012 {
1068 my $self = shift;
1069 my $data = shift;
1070 my $state = shift;
1071 my ($info, $tidx, $cidx);
1072
1073 ($info, $tidx, $cidx) = unpack("CNn", $data);
1074
1075 printf("GetIndexedPlayingTrackInfo (0x03, 0x12) D->I\n");
1076 printf(" Requested info: %s (%d)\n", (
1077 "Track caps/info",
1078 "Chapter time/name",
1079 "Artist name",
1080 "Album name",
1081 "Genre name",
1082 "Track title",
1083 "Composer name",
1084 "Lyrics",
1085 "Artwork count")[$info], $info);
1086 printf(" Track index: %d\n", $tidx);
1087 printf(" Chapter index: %d\n", $cidx);
1088
1089 return 1;
1090}
1091
1092sub _h_03_0013 {
1093 my $self = shift;
1094 my $data = shift;
1095 my $state = shift;
1096 my $info;
1097
1098 $info = unpack("C", $data);
1099 printf("RetIndexedPlayingTrackInfo (0x03, 0x13) I->D\n");
1100 printf(" Returned info: %s (%d)\n", (
1101 "Track caps/info",
1102 "Chapter time/name",
1103 "Artist name",
1104 "Album name",
1105 "Genre name",
1106 "Track title",
1107 "Composer name",
1108 "Lyrics",
1109 "Artwork count")[$info], $info);
1110 if ($info == 0x00) {
1111 my ($caps, $len, $chap) = unpack("xNNn", $data);
1112 printf(" Track is audiobook\n") if ($caps & 0x01);
1113 printf(" Track has chapters\n") if ($caps & 0x02);
1114 printf(" Track has artwork\n") if ($caps & 0x04);
1115 printf(" Track contains video\n") if ($caps & 0x80);
1116 printf(" Track queued as video\n") if ($caps & 0x100);
1117
1118 printf(" Track length: %d ms\n", $len);
1119 printf(" Track chapters: %d\n", $chap);
1120 } elsif ($info == 0x01) {
1121 my ($len, $name) = unpack("xNZ*");
1122 printf(" Chapter time: %d ms\n", $len);
1123 printf(" Chapter name: %s\n", $name);
1124 } elsif ($info >= 0x02 && $info <= 0x06) {
1125 my $name = unpack("xZ*", $data);
1126 printf(" Name/Title: %s\n", $name)
1127 } elsif ($info == 0x07) {
1128 my ($info, $index, $data) = unpack("xCnZ*");
1129 printf(" Part of multiple packets\n") if ($info & 0x01);
1130 printf(" Is last packet\n") if ($info & 0x02);
1131 printf(" Packet index: %d\n", $index);
1132 printf(" Data: %s\n", $data);
1133 } elsif ($info == 0x08) {
1134
1135 }
1136
1137 return 1;
1138}
1139
1140sub _h_04_0001 {
1141 my $self = shift;
1142 my $data = shift;
1143 my $state = shift;
1144 my $res;
1145 my $cmd;
1146
1147 ($res, $cmd) = unpack("Cn", $data);
1148
1149 printf("ACK (0x04, 0x0001) I->D\n");
1150 printf(" Acknowledged command: 0x%02x\n", $cmd);
1151 printf(" Status: %s (%d)\n",
1152 ("Success",
1153 "ERROR: Unknown Database Category",
1154 "ERROR: Command Failed",
1155 "ERROR: Out Of Resource",
1156 "ERROR: Bad Parameter",
1157 "ERROR: Unknown ID",
1158 "Reserved",
1159 "ERROR: Not Authenticated")[$res], $res);
1160
1161 return 1;
1162}
1163
1164sub _h_04_000c {
1165 my $self = shift;
1166 my $data = shift;
1167 my $state = shift;
1168 my ($info, $track, $chapter);
1169
1170 ($info, $track, $chapter) = unpack("CNn", $data);
1171
1172 printf("GetIndexedPlayingTrackInfo (0x04, 0x000C) D->I\n");
1173 printf(" Track: %d\n", $track);
1174 printf(" Chapter: %d\n", $chapter);
1175 printf(" Info requested: %s (%d)\n", (
1176 "Capabilities and information",
1177 "Podcast name",
1178 "Track release date",
1179 "Track description",
1180 "Track song lyrics",
1181 "Track genre",
1182 "Track Composer",
1183 "Tracn Artwork count")[$info], $info);
1184
1185 return 1;
1186}
1187
1188sub _h_04_000d {
1189 my $self = shift;
1190 my $data = shift;
1191 my $state = shift;
1192 my $info;
1193
1194 $info = unpack("C", $data);
1195
1196 printf("ReturnIndexedPlayingTrackInfo (0x04, 0x000D) I->D\n");
1197 if ($info == 0x00) {
1198 my ($capability, $length, $chapter);
1199
1200 ($capability, $length, $chapter) = unpack("xNNn", $data);
1201 printf(" Capabilities:\n");
1202 printf(" Is audiobook\n") if ($capability & 0x00000001);
1203 printf(" Has chapters\n") if ($capability & 0x00000002);
1204 printf(" Has album artwork\n") if ($capability & 0x00000004);
1205 printf(" Has song lyrics\n") if ($capability & 0x00000008);
1206 printf(" Is a podcast episode\n") if ($capability & 0x00000010);
1207 printf(" Has release date\n") if ($capability & 0x00000020);
1208 printf(" Has description\n") if ($capability & 0x00000040);
1209 printf(" Contains video\n") if ($capability & 0x00000080);
1210 printf(" Queued to play as video\n") if ($capability & 0x00000100);
1211
1212 printf(" Length: %d ms\n", $length);
1213 printf(" Chapters: %d\n", $chapter);
1214 } else {
1215 printf(" WARNING: Unknown info\n");
1216 return 1;
1217 }
1218
1219 return 1;
1220}
1221
1222sub _h_04_0012 {
1223 my $self = shift;
1224 my $data = shift;
1225 my $state = shift;
1226
1227 printf("RequestProtocolVersion (0x04, 0x0012) D->I\n");
1228
1229 return 1;
1230}
1231
1232sub _h_04_0013 {
1233 my $self = shift;
1234 my $data = shift;
1235 my $state = shift;
1236 my ($maj, $min);
1237
1238 ($maj, $min) = unpack("CC", $data);
1239
1240 printf("ReturnProtocolVersion (0x04, 0x0013) I->D\n");
1241 printf(" Lingo 0x04 version: %d.%02d\n", $maj, $min);
1242
1243 return 1;
1244}
1245
1246sub _h_04_0016 {
1247 my $self = shift;
1248 my $data = shift;
1249 my $state = shift;
1250
1251 printf("ResetDBSelection (0x04, 0x0016) D->I\n");
1252
1253 return 1;
1254}
1255
1256sub _h_04_0018 {
1257 my $self = shift;
1258 my $data = shift;
1259 my $state = shift;
1260 my $category;
1261
1262 $category = unpack("C", $data);
1263
1264 printf("GetNumberCategorizedDBRecords (0x04, 0x0018) D->I\n");
1265 printf(" Category: %s (%d)\n", (
1266 "Reserved",
1267 "Playlist",
1268 "Artist",
1269 "Album",
1270 "Genre",
1271 "Track",
1272 "Composer",
1273 "Audiobook",
1274 "Podcast",
1275 "Nested Playlist")[$category], $category);
1276
1277 return 1;
1278}
1279
1280sub _h_04_0019 {
1281 my $self = shift;
1282 my $data = shift;
1283 my $state = shift;
1284 my $count;
1285
1286 $count = unpack("N", $data);
1287
1288 printf("ReturnNumberCategorizedDBRecords (0x04, 0x0019) I->D\n");
1289 printf(" Count: %d\n", $count);
1290
1291 return 1;
1292}
1293
1294sub _h_04_001c {
1295 my $self = shift;
1296 my $data = shift;
1297 my $state = shift;
1298
1299 printf("GetPlayStatus (0x04, 0x001C) D->I\n");
1300
1301 return 1;
1302}
1303
1304sub _h_04_001d {
1305 my $self = shift;
1306 my $data = shift;
1307 my $state = shift;
1308 my ($len, $pos, $s);
1309
1310 ($len, $pos, $s) = unpack("NNC", $data);
1311
1312 printf("ReturnPlayStatus (0x04, 0x001D) I->D\n");
1313 printf(" Song length: %d ms\n", $len);
1314 printf(" Song position: %d ms\n", $pos);
1315 printf(" Player state: ");
1316 if ($s == 0x00) {
1317 printf("Stopped\n");
1318 } elsif ($s == 0x01) {
1319 printf("Playing\n");
1320 } elsif ($s == 0x02) {
1321 printf("Paused\n");
1322 } elsif ($s == 0xFF) {
1323 printf("Error\n");
1324 } else {
1325 printf("Reserved\n");
1326 }
1327
1328 return 1;
1329}
1330
1331sub _h_04_001e {
1332 my $self = shift;
1333 my $data = shift;
1334 my $state = shift;
1335
1336 printf("GetCurrentPlayingTrackIndex (0x04, 0x001E) D->I\n");
1337
1338 return 1;
1339}
1340
1341sub _h_04_001f {
1342 my $self = shift;
1343 my $data = shift;
1344 my $state = shift;
1345 my $num;
1346
1347 $num = unpack("N", $data);
1348
1349 printf("ReturnCurrentPlayingTrackIndex (0x04, 0x001F) I->D\n");
1350 printf(" Index: %d\n", $num);
1351
1352 return 1;
1353}
1354
1355sub _h_04_0020 {
1356 my $self = shift;
1357 my $data = shift;
1358 my $state = shift;
1359 my $track;
1360
1361 $track = unpack("N", $data);
1362
1363 printf("GetIndexedPlayingTrackTitle (0x04, 0x0020) D->I\n");
1364 printf(" Track: %d\n", $track);
1365
1366 return 1;
1367}
1368
1369sub _h_04_0021 {
1370 my $self = shift;
1371 my $data = shift;
1372 my $state = shift;
1373 my $title;
1374
1375 $title = unpack("Z*", $data);
1376
1377 printf("ReturnIndexedPlayingTrackTitle (0x04, 0x0021) I->D\n");
1378 printf(" Title: %s\n", $title);
1379
1380 return 1;
1381}
1382
1383sub _h_04_0022 {
1384 my $self = shift;
1385 my $data = shift;
1386 my $state = shift;
1387 my $track;
1388
1389 $track = unpack("N", $data);
1390
1391 printf("GetIndexedPlayingTrackArtistName (0x04, 0x0022) D->I\n");
1392 printf(" Track: %d\n", $track);
1393
1394 return 1;
1395}
1396
1397sub _h_04_0023 {
1398 my $self = shift;
1399 my $data = shift;
1400 my $state = shift;
1401 my $artist;
1402
1403 $artist = unpack("Z*", $data);
1404
1405 printf("ReturnIndexedPlayingTrackArtistName (0x04, 0x0023) I->D\n");
1406 printf(" Artist: %s\n", $artist);
1407
1408 return 1;
1409}
1410
1411sub _h_04_0024 {
1412 my $self = shift;
1413 my $data = shift;
1414 my $state = shift;
1415 my $track;
1416
1417 $track = unpack("N", $data);
1418
1419 printf("GetIndexedPlayingTrackAlbumName (0x04, 0x0024) D->I\n");
1420 printf(" Track: %d\n", $track);
1421
1422 return 1;
1423}
1424
1425sub _h_04_0025 {
1426 my $self = shift;
1427 my $data = shift;
1428 my $state = shift;
1429 my $title;
1430
1431 $title = unpack("Z*", $data);
1432
1433 printf("ReturnIndexedPlayingTrackAlbumName (0x04, 0x0025) I->D\n");
1434 printf(" Album: %s\n", $title);
1435
1436 return 1;
1437}
1438
1439sub _h_04_0026 {
1440 my $self = shift;
1441 my $data = shift;
1442 my $state = shift;
1443 my $notification;
1444
1445 if (length($data) == 1) {
1446 $notification = unpack("C", $data);
1447 } elsif (length($data) == 4) {
1448 $notification = unpack("N", $data);
1449 }
1450
1451 printf("SetPlayStatusChangeNotification (0x04, 0x0026) D->I\n");
1452
1453 if (length($data) == 1) {
1454 printf(" Events for: %s (%d)\n", (
1455 "Disable all",
1456 "Basic play state, track index, track time position, FFW/REW seek stop, and chapter index changes")[$notification], $notification);
1457 } elsif (length($data) == 4) {
1458 printf(" Events for:\n");
1459 printf(" Basic play state changes\n") if ($notification & 0x00000001);
1460 printf(" Extended play state changes\n") if ($notification & 0x00000002);
1461 printf(" Track index\n") if ($notification & 0x00000004);
1462 printf(" Track time offset (ms)\n") if ($notification & 0x00000008);
1463 printf(" Track time offset (s)\n") if ($notification & 0x00000010);
1464 printf(" Chapter index\n") if ($notification & 0x00000020);
1465 printf(" Chapter time offset (ms)\n") if ($notification & 0x00000040);
1466 printf(" Chapter time offset (s)\n") if ($notification & 0x00000080);
1467 printf(" Track unique identifier\n") if ($notification & 0x00000100);
1468 printf(" Track media tyoe\n") if ($notification & 0x00000200);
1469 printf(" Track lyrics\n") if ($notification & 0x00000400);
1470 } else {
1471 printf(" WARNING: Unknown length for state\n");
1472 return 0;
1473 }
1474
1475 return 1;
1476}
1477
1478sub _h_04_0027 {
1479 my $self = shift;
1480 my $data = shift;
1481 my $state = shift;
1482 my $info;
1483
1484 $info = unpack("C", $data);
1485
1486 printf("PlayStatusChangeNotification (0x04, 0x0029) I->D\n");
1487 printf(" Status:\n");
1488 if ($info == 0x00) {
1489 printf(" Playback stopped\n");
1490 } elsif ($info == 0x01) {
1491 my $index = unpack("xN", $data);
1492
1493 printf(" Track index: %d\n", $index);
1494 } elsif ($info == 0x02) {
1495 printf(" Playback FF seek stop\n");
1496 } elsif ($info == 0x03) {
1497 printf(" Playback REW seek stop\n");
1498 } elsif ($info == 0x04) {
1499 my $offset = unpack("xN", $data);
1500
1501 printf(" Track time offset: %d ms\n", $offset);
1502 } elsif ($info == 0x05) {
1503 my $index = unpack("xN", $data);
1504
1505 printf(" Chapter index: %d\n", $index);
1506 } elsif ($info == 0x06) {
1507 my $status = unpack("xC", $data);
1508
1509 printf(" Playback status extended: %s (%d)\n", (
1510 "Reserved",
1511 "Reserved",
1512 "Stopped",
1513 "Reserved",
1514 "Reserved",
1515 "FF seek started",
1516 "REW seek started",
1517 "FF/REW seek stopped",
1518 "Reserved",
1519 "Reserved",
1520 "Playing",
1521 "Paused")[$status], $status);
1522 } elsif ($info == 0x07) {
1523 my $offset = unpack("xN", $data);
1524
1525 printf(" Track time offset: %d s\n", $offset);
1526 } elsif ($info == 0x08) {
1527 my $offset = unpack("xN", $data);
1528
1529 printf(" Chapter time offset %d ms\n", $offset);
1530 } elsif ($info == 0x09) {
1531 my $offset = unpack("xN", $data);
1532
1533 printf(" Chapter time offset %d s\n", $offset);
1534 } elsif ($info == 0x0A) {
1535 my ($uidhi, $uidlo) = unpack("xNN", $data);
1536
1537 printf(" Track UID: %08x%08x\n", $uidhi, $uidlo);
1538 } elsif ($info == 0x0B) {
1539 my $mode = unpack("xC", $data);
1540
1541 printf(" Track mode: %s (%d)\n", (
1542 "Audio track",
1543 "Video track")[$mode], $mode);
1544 } elsif ($info == 0x0C) {
1545 printf(" Track lyrics ready\n");
1546 } else {
1547 printf(" Reserved\n");
1548 }
1549
1550 return 1;
1551}
1552
1553sub _h_04_0029 {
1554 my $self = shift;
1555 my $data = shift;
1556 my $state = shift;
1557 my $control;
1558
1559 $control = unpack("C", $data);
1560
1561 printf("PlayControl (0x04, 0x0029) D->I\n");
1562 printf(" Command: %s (%d)\n", (
1563 "Reserved",
1564 "Toggle Play/Pause",
1565 "Stop",
1566 "Next track",
1567 "Previous track",
1568 "Start FF",
1569 "Start Rev",
1570 "Stop FF/Rev",
1571 "Next",
1572 "Previous",
1573 "Play",
1574 "Pause",
1575 "Next chapter",
1576 "Previous chapter")[$control], $control);
1577
1578 return 1;
1579}
1580
1581sub _h_04_002c {
1582 my $self = shift;
1583 my $data = shift;
1584 my $state = shift;
1585
1586 printf("GetShuffle (0x04, 0x002C) D->I\n");
1587
1588 return 1;
1589}
1590
1591sub _h_04_002d {
1592 my $self = shift;
1593 my $data = shift;
1594 my $state = shift;
1595 my $mode;
1596
1597 $mode = unpack("C", $data);
1598
1599 printf("ReturnShuffle (0x04, 0x002D) I->D\n");
1600 printf(" Mode: %s (%d)\n", (
1601 "Off",
1602 "Tracks",
1603 "Albums")[$mode], $mode);
1604
1605 return 1;
1606}
1607
1608sub _h_04_002f {
1609 my $self = shift;
1610 my $data = shift;
1611 my $state = shift;
1612
1613 printf("GetRepeat (0x04, 0x002F) D->I\n");
1614
1615 return 1;
1616}
1617
1618sub _h_04_0030 {
1619 my $self = shift;
1620 my $data = shift;
1621 my $state = shift;
1622 my $mode;
1623
1624 $mode = unpack("C", $data);
1625
1626 printf("ReturnRepeat (0x04, 0x0030) I->D\n");
1627 printf(" Mode: %s (%d)\n", (
1628 "Off",
1629 "One Track",
1630 "All Tracks")[$mode], $mode);
1631
1632 return 1;
1633}
1634
1635sub _h_04_0032 {
1636 my $self = shift;
1637 my $data = shift;
1638 my $state = shift;
1639 my ($index, $format, $width, $height, $stride, $imagedata);
1640
1641 $state->{-index} = -1 unless(exists($state->{-index}));
1642 $state->{-image} = '' unless(exists($state->{-image}));
1643
1644 ($index, $format, $width, $height, $stride, $imagedata) = unpack("nCnnNa*", $data);
1645
1646 printf("SetDisplayImage (0x04, 0x0032) D->I\n");
1647 if ($index == 0) {
1648 printf(" Width: %d\n", $width);
1649 printf(" Height: %d\n", $height);
1650 printf(" Stride: %d\n", $stride);
1651 printf(" Format: %s (%d)\n", (
1652 "Reserved",
1653 "Monochrome, 2 bps",
1654 "RGB 565, little endian",
1655 "RGB 565, big endian")[$format], $format);
1656
1657 $state->{-imagelength} = $height * $stride;
1658 } else {
1659 ($index, $imagedata) = unpack("na*", $data);
1660 }
1661
1662 if ($index-1 != $state->{-index}) {
1663 printf(" WARNING! Out of order segment\n");
1664 return 0;
1665 }
1666
1667 $state->{-index} = $index;
1668 $state->{-image} .= $imagedata;
1669
1670 if (length($state->{-image}) >= $state->{-imagelength}) {
1671 printf(" Image data: %s\n", Device::iPod->_hexstring($state->{-image}));
1672 }
1673
1674 return 1;
1675}
1676
1677sub _h_04_0033 {
1678 my $self = shift;
1679 my $data = shift;
1680 my $state = shift;
1681
1682 printf("GetMonoDisplayImageLimits (0x04, 0x0033) D->I\n");
1683
1684 return 1;
1685}
1686
1687sub _h_04_0034 {
1688 my $self = shift;
1689 my $data = shift;
1690 my $state = shift;
1691 my ($width, $height, $format);
1692
1693 ($width, $height, $format) = unpack("nnC", $data);
1694
1695 printf("ReturnMonoDisplayImageLimits (0x04, 0x0034) I->D\n");
1696 printf(" Width: %d\n", $width);
1697 printf(" Height: %d\n", $height);
1698 printf(" Format: %s (%d)\n", (
1699 "Reserved",
1700 "Monochrome, 2 bps",
1701 "RGB 565, little endian",
1702 "RGB 565, big endian")[$format], $format);
1703
1704 return 1;
1705}
1706
1707sub _h_04_0035 {
1708 my $self = shift;
1709 my $data = shift;
1710 my $state = shift;
1711
1712 printf("GetNumPlayingTracks (0x04, 0x0035) D->I\n");
1713
1714 return 1;
1715}
1716
1717sub _h_04_0036 {
1718 my $self = shift;
1719 my $data = shift;
1720 my $state = shift;
1721 my $num;
1722
1723 $num = unpack("N", $data);
1724
1725 printf("ReturnNumPlayingTracks (0x04, 0x0036) I->D\n");
1726 printf(" Number: %d\n", $num);
1727
1728 return 1;
1729}
1730
1731sub _h_04_0037 {
1732 my $self = shift;
1733 my $data = shift;
1734 my $state = shift;
1735 my $num;
1736
1737 $num = unpack("N", $data);
1738
1739 printf("SetCurrentPlayingTrack (0x04, 0x0037) D->I\n");
1740 printf(" Track: %d\n", $num);
1741
1742 return 1;
1743}
1744
1745sub _h_07_0005 {
1746 my $self = shift;
1747 my $data = shift;
1748 my $state = shift;
1749 my $control;
1750
1751 $control = unpack("C", $data);
1752
1753 printf("SetTunerCtrl (0x07, 0x05) I->D\n");
1754 printf(" Options:\n");
1755 printf(" Power %s\n", ($control & 0x01)?"on":"off");
1756 printf(" Status change notifications %s\n", ($control & 0x02)?"on":"off");
1757 printf(" Raw mode %s\n", ($control & 0x04)?"on":"off");
1758}
1759
1760sub _h_07_0020 {
1761 my $self = shift;
1762 my $data = shift;
1763 my $state = shift;
1764 my $options;
1765
1766 $options = unpack("N", $data);
1767
1768 printf("SetRDSNotifyMask (0x07, 0x20) I->D\n");
1769 printf(" Options:\n");
1770 printf(" Radiotext\n") if ($options & 0x00000010);
1771 printf(" Program Service Name\n") if ($options & 0x40000000);
1772 printf(" Reserved\n") if ($options & 0xBFFFFFEF);
1773
1774 return 1;
1775}
1776
1777sub _h_07_0024 {
1778 my $self = shift;
1779 my $data = shift;
1780 my $state = shift;
1781
1782 printf("Reserved command (0x07, 0x24) I->D\n");
1783
1784 return 1;
1785}
1786
1787
1788package main;
1789
1790use Device::iPod;
1791use Getopt::Long;
1792use strict;
1793
1794my $decoder;
1795my $device;
1796my $unpacker;
1797my $line;
1798
1799sub unpack_hexstring {
1800 my $line = shift;
1801 my $m;
1802 my @m;
1803
1804 $line =~ s/(..)/chr(hex($1))/ge;
1805 $device->{-inbuf} = $line;
1806
1807 $m = $device->_message();
1808 next unless defined($m);
1809 @m = $device->_unframe_cmd($m);
1810 unless(@m) {
1811 printf("Line %d: Error decoding frame: %s\n", $., $device->error());
1812 return ();
1813 }
1814
1815 return @m;
1816}
1817
1818sub unpack_iaplog {
1819 my $line = shift;
1820 my @m;
1821
1822 unless ($line =~ /^(?:\[\d+\] )?[RT]::? /) {
1823 printf("Skipped: %s\n", $line);
1824 return ();
1825 }
1826
1827 $line =~ s/^(?:\[\d+\] )?[RT]::? //;
1828 $line =~ s/\\x(..)/chr(hex($1))/ge;
1829 $line =~ s/\\\\/\\/g;
1830
1831 @m = unpack("CCa*", $line);
1832 if ($m[0] == 0x04) {
1833 @m = unpack("Cna*", $line);
1834 }
1835
1836 return @m;
1837}
1838
1839
1840$decoder = iap::decode->new();
1841$device = Device::iPod->new();
1842$unpacker = \&unpack_iaplog;
1843
1844GetOptions("hexstring" => sub {$unpacker = \&unpack_hexstring});
1845
1846while ($line = <>) {
1847 my @m;
1848
1849 chomp($line);
1850
1851 @m = $unpacker->($line);
1852 next unless (@m);
1853
1854 printf("Line %d: ", $.);
1855 $decoder->display(@m);
1856}
diff --git a/tools/iap/ipod-001-general.t b/tools/iap/ipod-001-general.t
new file mode 100644
index 0000000000..f2b5451dbc
--- /dev/null
+++ b/tools/iap/ipod-001-general.t
@@ -0,0 +1,133 @@
1use Test::More qw( no_plan );
2use strict;
3
4BEGIN { use_ok('Device::iPod'); }
5require_ok('Device::iPod');
6
7my $ipod = Device::iPod->new();
8my $m;
9my ($l, $c, $p);
10
11isa_ok($ipod, 'Device::iPod');
12
13$ipod->{-debug} = 1;
14$ipod->open("/dev/ttyUSB0");
15
16$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
17ok($m == 1, "Wakeup sent");
18
19# Empty the buffer
20$ipod->emptyrecv();
21
22# Send a command with wrong checksum
23# We expect no response (Timeout)
24$m = $ipod->sendraw("\xff\x55\x02\x00\x03\x00");
25ok($m == 1, "Broken checksum sent");
26($l, $c, $p) = $ipod->recvmsg();
27subtest "Timeout" => sub {
28 ok(!defined($l), "No response received");
29 like($ipod->error(), '/Timeout/', "Timeout reading response");
30};
31
32# Empty the buffer
33$ipod->emptyrecv();
34
35# Send a too short command
36# We expect an ACK Bad Parameter as response
37$m = $ipod->sendmsg(0x00, 0x13, "");
38ok($m == 1, "Short command sent");
39($l, $c, $p) = $ipod->recvmsg();
40subtest "ACK Bad Parameter" => sub {
41 ok(defined($l), "Response received");
42 is($l, 0x00, "Response lingo");
43 is($c, 0x02, "Response command");
44 is($p, "\x04\x13", "Response payload");
45};
46
47# Empty the buffer
48$ipod->emptyrecv();
49
50# Send an undefined lingo
51# We expect a timeout
52$m = $ipod->sendmsg(0x1F, 0x00);
53ok($m == 1, "Undefined lingo sent");
54($l, $c, $p) = $ipod->recvmsg();
55subtest "Timeout" => sub {
56 ok(!defined($l), "No response received");
57 like($ipod->error(), '/Timeout/', "Timeout reading response");
58};
59
60# Empty the buffer
61$ipod->emptyrecv();
62
63# IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00)
64# We expect an ACK Command Failed message as response
65$m = $ipod->sendmsg(0x00, 0x13, "\x80\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00");
66ok($m == 1, "IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00) sent");
67($l, $c, $p) = $ipod->recvmsg();
68subtest "ACK Command Failed" => sub {
69 ok(defined($l), "Response received");
70 is($l, 0x00, "Response lingo");
71 is($c, 0x02, "Response command");
72 is($p, "\x02\x13", "Response payload");
73};
74
75# Empty the buffer
76$ipod->emptyrecv();
77
78# Identify(lingo=0xFF)
79# We expect no response (timeout)
80$m = $ipod->sendmsg(0x00, 0x01, "\xFF");
81ok($m == 1, "Identify(lingo=0xFF) sent");
82($l, $c, $p) = $ipod->recvmsg();
83subtest "Timeout" => sub {
84 ok(!defined($l), "No response received");
85 like($ipod->error(), '/Timeout/', "Timeout reading response");
86};
87
88# Empty the buffer
89$ipod->emptyrecv();
90
91# IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00)
92# We expect an ACK Command Failed message as response
93$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00");
94ok($m == 1, "IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00) sent");
95($l, $c, $p) = $ipod->recvmsg();
96subtest "ACK Command Failed" => sub {
97 ok(defined($l), "Response received");
98 is($l, 0x00, "Response lingo");
99 is($c, 0x02, "Response command");
100 is($p, "\x02\x13", "Response payload");
101};
102
103# Empty the buffer
104$ipod->emptyrecv();
105# IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00)
106# We expect an ACK Command Failed message as response
107$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
108ok($m == 1, "IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00) sent");
109($l, $c, $p) = $ipod->recvmsg();
110subtest "ACK Command Failed" => sub {
111 ok(defined($l), "Response received");
112 is($l, 0x00, "Response lingo");
113 is($c, 0x02, "Response command");
114 is($p, "\x02\x13", "Response payload");
115};
116
117# Empty the buffer
118$ipod->emptyrecv();
119
120# RequestLingoProtocolVersion(lingo=0xFF)
121# We expect an ACK Bad Parameter message as response
122$m = $ipod->sendmsg(0x00, 0x0F, "\xFF");
123ok($m == 1, "RequestLingoProtocolVersion(lingo=0xFF) sent");
124($l, $c, $p) = $ipod->recvmsg();
125subtest "ACK Bad Parameter" => sub {
126 ok(defined($l), "Response received");
127 is($l, 0x00, "Response lingo");
128 is($c, 0x02, "Response command");
129 is($p, "\x04\x0F", "Response payload");
130};
131
132# Empty the buffer
133$ipod->emptyrecv();
diff --git a/tools/iap/ipod-002-lingo0.t b/tools/iap/ipod-002-lingo0.t
new file mode 100644
index 0000000000..c3bb676553
--- /dev/null
+++ b/tools/iap/ipod-002-lingo0.t
@@ -0,0 +1,277 @@
1use Test::More qw( no_plan );
2use strict;
3
4BEGIN { use_ok('Device::iPod'); }
5require_ok('Device::iPod');
6
7my $ipod = Device::iPod->new();
8my $m;
9my ($l, $c, $p);
10
11isa_ok($ipod, 'Device::iPod');
12
13$ipod->{-debug} = 1;
14$ipod->open("/dev/ttyUSB0");
15
16$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
17ok($m == 1, "Wakeup sent");
18
19# Empty the buffer
20$ipod->emptyrecv();
21
22# IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00)
23# We expect an ACK OK message as response
24$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00");
25ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent");
26($l, $c, $p) = $ipod->recvmsg();
27subtest "ACK OK" => sub {
28 ok(defined($l), "Response received");
29 is($l, 0x00, "Response lingo");
30 is($c, 0x02, "Response command");
31 is($p, "\x00\x13", "Response payload");
32};
33
34# Empty the buffer
35$ipod->emptyrecv();
36
37# RequestRemoteUIMode
38# We expect an ACK Bad Parameter as response, as we have not
39# negotiated lingo 0x04
40$m = $ipod->sendmsg(0x00, 0x03);
41ok($m == 1, "RequestRemoteUIMode sent");
42($l, $c, $p) = $ipod->recvmsg();
43subtest "ACK Bad Parameter" => sub {
44 ok(defined($l), "Response received");
45 is($l, 0x00, "Response lingo");
46 is($c, 0x02, "Response command");
47 is($p, "\x04\x03", "Response payload");
48};
49
50# Empty the buffer
51$ipod->emptyrecv();
52
53# EnterRemoteUIMode
54# We expect an ACK Bad Parameter as response, as we have not
55# negotiated lingo 0x04
56$m = $ipod->sendmsg(0x00, 0x05);
57ok($m == 1, "EnterRemoteUIMode sent");
58($l, $c, $p) = $ipod->recvmsg();
59subtest "ACK Bad Parameter" => sub {
60 ok(defined($l), "Response received");
61 is($l, 0x00, "Response lingo");
62 is($c, 0x02, "Response command");
63 is($p, "\x04\x05", "Response payload");
64};
65
66# Empty the buffer
67$ipod->emptyrecv();
68
69# ExitRemoteUIMode
70# We expect an ACK Bad Parameter as response, as we have not
71# negotiated lingo 0x04
72$m = $ipod->sendmsg(0x00, 0x06);
73ok($m == 1, "ExitRemoteUIMode sent");
74($l, $c, $p) = $ipod->recvmsg();
75subtest "ACK Bad Parameter" => sub {
76 ok(defined($l), "Response received");
77 is($l, 0x00, "Response lingo");
78 is($c, 0x02, "Response command");
79 is($p, "\x04\x06", "Response payload");
80};
81
82# Empty the buffer
83$ipod->emptyrecv();
84
85# RequestiPodName
86# We expect a ReturniPodName packet
87$m = $ipod->sendmsg(0x00, 0x07);
88ok($m == 1, "RequestiPodName sent");
89($l, $c, $p) = $ipod->recvmsg();
90subtest "ReturniPodName" => sub {
91 ok(defined($l), "Response received");
92 is($l, 0x00, "Response lingo");
93 is($c, 0x08, "Response command");
94 like($p, "/^[^\\x00]*\\x00\$/", "Response payload");
95};
96
97# Empty the buffer
98$ipod->emptyrecv();
99
100# RequestiPodSoftwareVersion
101# We expect a ReturniPodSoftwareVersion packet
102$m = $ipod->sendmsg(0x00, 0x09);
103ok($m == 1, "RequestiPodSoftwareVersion sent");
104($l, $c, $p) = $ipod->recvmsg();
105subtest "ReturniPodSoftwareVersion" => sub {
106 ok(defined($l), "Response received");
107 is($l, 0x00, "Response lingo");
108 is($c, 0x0A, "Response command");
109 like($p, "/^...\$/", "Response payload");
110};
111
112# Empty the buffer
113$ipod->emptyrecv();
114
115# RequestiPodSerialNumber
116# We expect a ReturniPodSerialNumber packet
117$m = $ipod->sendmsg(0x00, 0x0B);
118ok($m == 1, "RequestiPodSerialNumber sent");
119($l, $c, $p) = $ipod->recvmsg();
120subtest "ReturniPodSerialNumber" => sub {
121 ok(defined($l), "Response received");
122 is($l, 0x00, "Response lingo");
123 is($c, 0x0C, "Response command");
124 like($p, "/^[^\\x00]*\\x00\$/", "Response payload");
125};
126
127# Empty the buffer
128$ipod->emptyrecv();
129
130# RequestiPodModelNum
131# We expect a ReturniPodModelNum packet
132$m = $ipod->sendmsg(0x00, 0x0D);
133ok($m == 1, "RequestiPodModelNum sent");
134($l, $c, $p) = $ipod->recvmsg();
135subtest "ReturniPodModelNum" => sub {
136 ok(defined($l), "Response received");
137 is($l, 0x00, "Response lingo");
138 is($c, 0x0E, "Response command");
139 like($p, "/^....[^\\x00]*\\x00\$/", "Response payload");
140};
141
142# Empty the buffer
143$ipod->emptyrecv();
144
145# RequestLingoProtocolVersion(lingo=0x00)
146# We expect a ReturnLingoProtocolVersion packet
147$m = $ipod->sendmsg(0x00, 0x0F, "\x00");
148ok($m == 1, "RequestLingoProtocolVersion(lingo=0x00) sent");
149($l, $c, $p) = $ipod->recvmsg();
150subtest "ReturnLingoProtocolVersion" => sub {
151 ok(defined($l), "Response received");
152 is($l, 0x00, "Response lingo");
153 is($c, 0x10, "Response command");
154 like($p, "/^\\x00..\$/", "Response payload");
155};
156
157# Empty the buffer
158$ipod->emptyrecv();
159
160# IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00)
161# We expect an ACK OK message as response
162$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00");
163ok($m == 1, "IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00) sent");
164($l, $c, $p) = $ipod->recvmsg();
165subtest "ACK OK" => sub {
166 ok(defined($l), "Response received");
167 is($l, 0x00, "Response lingo");
168 is($c, 0x02, "Response command");
169 is($p, "\x00\x13", "Response payload");
170};
171
172# Empty the buffer
173$ipod->emptyrecv();
174
175# RequestRemoteUIMode
176# We expect an ReturnRemoteUIMode packet specifying standard mode
177$m = $ipod->sendmsg(0x00, 0x03);
178ok($m == 1, "RequestRemoteUIMode sent");
179($l, $c, $p) = $ipod->recvmsg();
180subtest "ReturnRemoteUIMode" => sub {
181 ok(defined($l), "Response received");
182 is($l, 0x00, "Response lingo");
183 is($c, 0x04, "Response command");
184 is($p, "\x00", "Response payload");
185};
186
187# Empty the buffer
188$ipod->emptyrecv();
189
190# EnterRemoteUIMode
191# We expect an ACK Pending packet, followed by an ACK OK packet
192$m = $ipod->sendmsg(0x00, 0x05);
193ok($m == 1, "EnterRemoteUIMode sent");
194($l, $c, $p) = $ipod->recvmsg();
195subtest "ACK Pending" => sub {
196 ok(defined($l), "Response received");
197 is($l, 0x00, "Response lingo");
198 is($c, 0x02, "Response command");
199 like($p, "/^\\x06\\x05/", "Response payload");
200};
201($l, $c, $p) = $ipod->recvmsg();
202subtest "ACK OK" => sub {
203 ok(defined($l), "Response received");
204 is($l, 0x00, "Response lingo");
205 is($c, 0x02, "Response command");
206 is($p, "\x00\x05", "Response payload");
207};
208
209# Empty the buffer
210$ipod->emptyrecv();
211
212# RequestRemoteUIMode
213# We expect an ReturnRemoteUIMode packet specifying extended mode
214$m = $ipod->sendmsg(0x00, 0x03);
215ok($m == 1, "RequestRemoteUIMode sent");
216($l, $c, $p) = $ipod->recvmsg();
217subtest "ReturnRemoteUIMode" => sub {
218 ok(defined($l), "Response received");
219 is($l, 0x00, "Response lingo");
220 is($c, 0x04, "Response command");
221 isnt($p, "\x00", "Response payload");
222};
223
224# Empty the buffer
225$ipod->emptyrecv();
226
227# ExitRemoteUIMode
228# We expect an ACK Pending packet, followed by an ACK OK packet
229$m = $ipod->sendmsg(0x00, 0x06);
230ok($m == 1, "ExitRemoteUIMode sent");
231($l, $c, $p) = $ipod->recvmsg();
232subtest "ACK Pending" => sub {
233 ok(defined($l), "Response received");
234 is($l, 0x00, "Response lingo");
235 is($c, 0x02, "Response command");
236 like($p, "/^\\x06\\x06/", "Response payload");
237};
238($l, $c, $p) = $ipod->recvmsg();
239subtest "ACK OK" => sub {
240 ok(defined($l), "Response received");
241 is($l, 0x00, "Response lingo");
242 is($c, 0x02, "Response command");
243 is($p, "\x00\x06", "Response payload");
244};
245
246# Empty the buffer
247$ipod->emptyrecv();
248
249# RequestRemoteUIMode
250# We expect an ReturnRemoteUIMode packet specifying standard mode
251$m = $ipod->sendmsg(0x00, 0x03);
252ok($m == 1, "RequestRemoteUIMode sent");
253($l, $c, $p) = $ipod->recvmsg();
254subtest "ReturnRemoteUIMode" => sub {
255 ok(defined($l), "Response received");
256 is($l, 0x00, "Response lingo");
257 is($c, 0x04, "Response command");
258 is($p, "\x00", "Response payload");
259};
260
261# Empty the buffer
262$ipod->emptyrecv();
263
264# Send an undefined command
265# We expect an ACK Bad Parameter as response
266$m = $ipod->sendmsg(0x00, 0xFF);
267ok($m == 1, "Undefined command sent");
268($l, $c, $p) = $ipod->recvmsg();
269subtest "ACK Bad Parameter" => sub {
270 ok(defined($l), "Response received");
271 is($l, 0x00, "Response lingo");
272 is($c, 0x02, "Response command");
273 is($p, "\x04\xFF", "Response payload");
274};
275
276# Empty the buffer
277$ipod->emptyrecv();
diff --git a/tools/iap/ipod-003-lingo2.t b/tools/iap/ipod-003-lingo2.t
new file mode 100644
index 0000000000..ee0bd6972e
--- /dev/null
+++ b/tools/iap/ipod-003-lingo2.t
@@ -0,0 +1,220 @@
1use Test::More qw( no_plan );
2use strict;
3
4BEGIN { use_ok('Device::iPod'); }
5require_ok('Device::iPod');
6
7my $ipod = Device::iPod->new();
8my $m;
9my ($l, $c, $p);
10
11isa_ok($ipod, 'Device::iPod');
12
13$ipod->{-debug} = 1;
14$ipod->open("/dev/ttyUSB0");
15
16$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
17ok($m == 1, "Wakeup sent");
18
19# Empty the buffer
20$ipod->emptyrecv();
21
22# IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00)
23# We expect an ACK OK message as response
24$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00");
25ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent");
26($l, $c, $p) = $ipod->recvmsg();
27subtest "ACK OK" => sub {
28 ok(defined($l), "Response received");
29 is($l, 0x00, "Response lingo");
30 is($c, 0x02, "Response command");
31 is($p, "\x00\x13", "Response payload");
32};
33
34# Empty the buffer
35$ipod->emptyrecv();
36
37# ContextButtonStatus(0x00)
38# We expect an ACK Bad Parameter message as response
39$m = $ipod->sendmsg(0x02, 0x00, "\x00");
40ok($m == 1, "ContextButtonStatus(0x00)");
41($l, $c, $p) = $ipod->recvmsg();
42subtest "ACK Bad Parameter" => sub {
43 ok(defined($l), "Response received");
44 is($l, 0x02, "Response lingo");
45 is($c, 0x01, "Response command");
46 is($p, "\x04\x00", "Response payload");
47};
48
49# Empty the buffer
50$ipod->emptyrecv();
51
52# Identify(lingo=0x00)
53# We expect no response (timeout)
54$m = $ipod->sendmsg(0x00, 0x01, "\x00");
55ok($m == 1, "Identify(lingo=0x00) sent");
56($l, $c, $p) = $ipod->recvmsg();
57subtest "Timeout" => sub {
58 ok(!defined($l), "No response received");
59 like($ipod->error(), '/Timeout/', "Timeout reading response");
60};
61
62# Empty the buffer
63$ipod->emptyrecv();
64
65# ContextButtonStatus(0x00)
66# We expect a timeout as response
67$m = $ipod->sendmsg(0x02, 0x00, "\x00");
68ok($m == 1, "ContextButtonStatus(0x00)");
69($l, $c, $p) = $ipod->recvmsg();
70subtest "Timeout" => sub {
71 ok(!defined($l), "Response received");
72 like($ipod->error(), '/Timeout/', "Timeout reading response");
73};
74
75# Empty the buffer
76$ipod->emptyrecv();
77
78# Identify(lingo=0x02)
79# We expect no response (timeout)
80$m = $ipod->sendmsg(0x00, 0x01, "\x02");
81ok($m == 1, "Identify(lingo=0x02) sent");
82($l, $c, $p) = $ipod->recvmsg();
83subtest "Timeout" => sub {
84 ok(!defined($l), "No response received");
85 like($ipod->error(), '/Timeout/', "Timeout reading response");
86};
87
88# Empty the buffer
89$ipod->emptyrecv();
90
91# ContextButtonStatus(0x00)
92# We expect a timeout as response
93$m = $ipod->sendmsg(0x02, 0x00, "\x00");
94ok($m == 1, "ContextButtonStatus(0x00)");
95($l, $c, $p) = $ipod->recvmsg();
96subtest "Timeout" => sub {
97 ok(!defined($l), "Response received");
98 like($ipod->error(), '/Timeout/', "Timeout reading response");
99};
100
101# Empty the buffer
102$ipod->emptyrecv();
103
104# IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00)
105# We expect an ACK OK message as response
106$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00");
107ok($m == 1, "IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00) sent");
108($l, $c, $p) = $ipod->recvmsg();
109subtest "ACK OK" => sub {
110 ok(defined($l), "Response received");
111 is($l, 0x00, "Response lingo");
112 is($c, 0x02, "Response command");
113 is($p, "\x00\x13", "Response payload");
114};
115
116# Empty the buffer
117$ipod->emptyrecv();
118
119# RequestLingoProtocolVersion(lingo=0x02)
120# We expect a ReturnLingoProtocolVersion packet
121$m = $ipod->sendmsg(0x00, 0x0F, "\x02");
122ok($m == 1, "RequestLingoProtocolVersion(lingo=0x02) sent");
123($l, $c, $p) = $ipod->recvmsg();
124subtest "ReturnLingoProtocolVersion" => sub {
125 ok(defined($l), "Response received");
126 is($l, 0x00, "Response lingo");
127 is($c, 0x10, "Response command");
128 like($p, "/^\\x02..\$/", "Response payload");
129};
130
131# Empty the buffer
132$ipod->emptyrecv();
133
134# Send an undefined command
135# We expect an ACK Bad Parameter as response
136$m = $ipod->sendmsg(0x02, 0xFF);
137ok($m == 1, "Undefined command sent");
138($l, $c, $p) = $ipod->recvmsg();
139subtest "ACK Bad Parameter" => sub {
140 ok(defined($l), "Response received");
141 is($l, 0x02, "Response lingo");
142 is($c, 0x01, "Response command");
143 is($p, "\x04\xFF", "Response payload");
144};
145
146# Empty the buffer
147$ipod->emptyrecv();
148
149# ContextButtonStatus(0x00)
150# We expect a timeout as response
151$m = $ipod->sendmsg(0x02, 0x00, "\x00");
152ok($m == 1, "ContextButtonStatus(0x00)");
153($l, $c, $p) = $ipod->recvmsg();
154subtest "Timeout" => sub {
155 ok(!defined($l), "Response received");
156 like($ipod->error(), '/Timeout/', "Timeout reading response");
157};
158
159# Empty the buffer
160$ipod->emptyrecv();
161
162# Send a too short command
163# We expect an ACK Bad Parameter as response
164$m = $ipod->sendmsg(0x02, 0x00, "");
165ok($m == 1, "Short command sent");
166($l, $c, $p) = $ipod->recvmsg();
167subtest "ACK Bad Parameter" => sub {
168 ok(defined($l), "Response received");
169 is($l, 0x02, "Response lingo");
170 is($c, 0x01, "Response command");
171 is($p, "\x04\x00", "Response payload");
172};
173
174# Empty the buffer
175$ipod->emptyrecv();
176
177# ImageButtonStatus(0x00)
178# We expect an ACK Not Authenticated as response
179$m = $ipod->sendmsg(0x02, 0x02, "\x00");
180ok($m == 1, "ImageButtonStatus(0x00)");
181($l, $c, $p) = $ipod->recvmsg();
182subtest "ACK Not Authenticated" => sub {
183 ok(defined($l), "Response received");
184 is($l, 0x02, "Response lingo");
185 is($c, 0x01, "Response command");
186 is($p, "\x07\x02", "Response payload");
187};
188
189# Empty the buffer
190$ipod->emptyrecv();
191
192# VideoButtonStatus(0x00)
193# We expect an ACK Not Authenticated as response
194$m = $ipod->sendmsg(0x02, 0x03, "\x00");
195ok($m == 1, "VideoButtonStatus(0x00)");
196($l, $c, $p) = $ipod->recvmsg();
197subtest "ACK Not Authenticated" => sub {
198 ok(defined($l), "Response received");
199 is($l, 0x02, "Response lingo");
200 is($c, 0x01, "Response command");
201 is($p, "\x07\x03", "Response payload");
202};
203
204# Empty the buffer
205$ipod->emptyrecv();
206
207# AudioButtonStatus(0x00)
208# We expect an ACK Not Authenticated as response
209$m = $ipod->sendmsg(0x02, 0x04, "\x00");
210ok($m == 1, "AudioButtonStatus(0x00)");
211($l, $c, $p) = $ipod->recvmsg();
212subtest "ACK Not Authenticated" => sub {
213 ok(defined($l), "Response received");
214 is($l, 0x02, "Response lingo");
215 is($c, 0x01, "Response command");
216 is($p, "\x07\x04", "Response payload");
217};
218
219# Empty the buffer
220$ipod->emptyrecv();