summaryrefslogtreecommitdiffstats
path: root/apps/iap/iap-lingo3.c
diff options
context:
space:
mode:
authorRalf Ertzinger <rockbox@camperquake.de>2013-06-22 10:08:23 +0100
committerFrank Gevaerts <frank@gevaerts.be>2013-11-10 18:41:24 +0100
commitb170c73f922e3457b923b4e7fcbec794a8885c77 (patch)
tree89fbdbd8c25af5101a29a1ede3b896332a4e205c /apps/iap/iap-lingo3.c
parent500b137308a6ee5c2aba873734a8956d70472f56 (diff)
downloadrockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.tar.gz
rockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.tar.bz2
rockbox-b170c73f922e3457b923b4e7fcbec794a8885c77.zip
Updated IAP commands.
Originally written and uploaded by Lalufu (Ralf Ertzinger) in Feb 2012. They have been condensed into a single patch and some further additions by Andy Potter. Currently includes Authentication V2 support from iPod to Accessory, RF/BlueTooth transmitter support, selecting a playlist and selecting a track from the current playlist. Does not support uploading Album Art or podcasts. Has been tested on the following iPods, 4th Gen Grayscale, 4th Gen Color/Photo, Mini 2nd Gen, Nano 1st Gen and Video 5.5Gen. Change-Id: Ie8fc098361844132f0228ecbe3c48da948726f5e Co-Authored by: Andy Potter <liveboxandy@gmail.com> Reviewed-on: http://gerrit.rockbox.org/533 Reviewed-by: Frank Gevaerts <frank@gevaerts.be>
Diffstat (limited to 'apps/iap/iap-lingo3.c')
-rw-r--r--apps/iap/iap-lingo3.c1508
1 files changed, 1508 insertions, 0 deletions
diff --git a/apps/iap/iap-lingo3.c b/apps/iap/iap-lingo3.c
new file mode 100644
index 0000000000..0ed3df118e
--- /dev/null
+++ b/apps/iap/iap-lingo3.c
@@ -0,0 +1,1508 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Alan Korr & Nick Robinson
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Lingo 0x03: Display Remote Lingo
+ *
+ * A bit of a hodgepogde of odds and ends.
+ *
+ * Used to control the equalizer in version 1.00 of the Lingo, but later
+ * grew functions to control album art transfer and check the player
+ * status.
+ *
+ * TODO:
+ * - Actually support multiple equalizer profiles, currently only the
+ * profile 0 (equalizer disabled) is supported
+ */
+
+#include "iap-core.h"
+#include "iap-lingo.h"
+#include "system.h"
+#include "audio.h"
+#include "powermgmt.h"
+#include "settings.h"
+#include "metadata.h"
+#include "playback.h"
+
+/*
+ * This macro is meant to be used inside an IAP mode message handler.
+ * It is passed the expected minimum length of the message buffer.
+ * If the buffer does not have the required lenght an ACK
+ * packet with a Bad Parameter error is generated.
+ */
+#define CHECKLEN(x) do { \
+ if (len < (x)) { \
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
+ return; \
+ }} while(0)
+
+/* Check for authenticated state, and return an ACK Not
+ * Authenticated on failure.
+ */
+#define CHECKAUTH do { \
+ if (!DEVICE_AUTHENTICATED) { \
+ cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \
+ return; \
+ }} while(0)
+
+static void cmd_ack(const unsigned char cmd, const unsigned char status)
+{
+ IAP_TX_INIT(0x03, 0x00);
+ IAP_TX_PUT(status);
+ IAP_TX_PUT(cmd);
+
+ iap_send_tx();
+}
+
+#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
+
+void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
+{
+ unsigned int cmd = buf[1];
+
+ /* We expect at least two bytes in the buffer, one for the
+ * state bits.
+ */
+ CHECKLEN(2);
+
+ /* Lingo 0x03 must have been negotiated */
+ if (!DEVICE_LINGO_SUPPORTED(0x03)) {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ return;
+ }
+
+ switch (cmd)
+ {
+ /* ACK (0x00)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetCurrentEQProfileIndex (0x01)
+ *
+ * Return the index of the current equalizer profile.
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x01
+ *
+ * Returns:
+ * RetCurrentEQProfileIndex
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x02
+ * 0x02-0x05: Index as an unsigned 32bit integer
+ */
+ case 0x01:
+ {
+ IAP_TX_INIT(0x03, 0x02);
+ IAP_TX_PUT_U32(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetCurrentEQProfileIndex (0x02)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* SetCurrentEQProfileIndex (0x03)
+ *
+ * Set the active equalizer profile
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x03
+ * 0x02-0x05: Profile index to activate
+ * 0x06: Whether to restore the previous profile on detach
+ *
+ * Returns on success:
+ * IAP_ACK_OK
+ *
+ * Returns on failure:
+ * IAP_ACK_CMD_FAILED
+ *
+ * TODO: Figure out return code for invalid index
+ */
+ case 0x03:
+ {
+ uint32_t index;
+
+ CHECKLEN(7);
+
+ index = get_u32(&buf[2]);
+
+ if (index > 0) {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+
+ /* Currently, we just ignore the command and acknowledge it */
+ cmd_ok(cmd);
+ break;
+ }
+
+ /* GetNumEQProfiles (0x04)
+ *
+ * Get the number of available equalizer profiles
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x04
+ *
+ * Returns:
+ * RetNumEQProfiles
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x05
+ * 0x02-0x05: Number as an unsigned 32bit integer
+ */
+ case 0x04:
+ {
+ IAP_TX_INIT(0x03, 0x05);
+ /* Return one profile (0, the disabled profile) */
+ IAP_TX_PUT_U32(0x01);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetNumEQProfiles (0x05)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetIndexedEQProfileName (0x06)
+ *
+ * Return the name of the indexed equalizer profile
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x06
+ * 0x02-0x05: Profile index to get the name of
+ *
+ * Returns on success:
+ * RetIndexedEQProfileName
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x06
+ * 0x02-0xNN: Name as an UTF-8 null terminated string
+ *
+ * Returns on failure:
+ * IAP_ACK_BAD_PARAM
+ *
+ * TODO: Figure out return code for out of range index
+ */
+ case 0x06:
+ {
+ uint32_t index;
+
+ CHECKLEN(6);
+
+ index = get_u32(&buf[2]);
+
+ if (index > 0) {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ IAP_TX_INIT(0x03, 0x07);
+ IAP_TX_PUT_STRING("Default");
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetIndexedQUProfileName (0x07)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* SetRemoteEventNotification (0x08)
+ *
+ * Set events the device would like to be notified about
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x08
+ * 0x02-0x05: Event bitmask
+ *
+ * Returns:
+ * IAP_ACK_OK
+ */
+ case 0x08:
+ {
+ struct tm* tm;
+
+ CHECKLEN(6);
+ CHECKAUTH;
+
+ /* Save the current state of the various attributes we track */
+ device.trackpos_ms = iap_get_trackpos();
+ device.track_index = iap_get_trackindex();
+ device.chapter_index = 0;
+ device.play_status = audio_status();
+ /* TODO: Fix this */
+ device.mute = false;
+ device.volume = 0x80;
+ device.power_state = charger_input_state;
+ device.battery_level = battery_level();
+ /* TODO: Fix this */
+ device.equalizer_index = 0;
+ device.shuffle = global_settings.playlist_shuffle;
+ device.repeat = global_settings.repeat_mode;
+ tm = get_time();
+ memcpy(&(device.datetime), tm, sizeof(struct tm));
+ device.alarm_state = 0;
+ device.alarm_hour = 0;
+ device.alarm_minute = 0;
+ /* TODO: Fix this */
+ device.backlight = 0;
+ device.hold = button_hold();
+ device.soundcheck = 0;
+ device.audiobook = 0;
+ device.trackpos_s = (device.trackpos_ms/1000) & 0xFFFF;
+
+ /* Get the notification bits */
+ device.do_notify = false;
+ device.changed_notifications = 0;
+ device.notifications = get_u32(&buf[0x02]);
+ if (device.notifications)
+ device.do_notify = true;
+
+ cmd_ok(cmd);
+ break;
+ }
+
+ /* RemoteEventNotification (0x09)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetRemoteEventStatus (0x0A)
+ *
+ * Request the events changed since the last call to GetREmoteEventStatus
+ * or SetRemoteEventNotification
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0A
+ *
+ * This command requires authentication
+ *
+ * Returns:
+ * RetRemoteEventNotification
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0B
+ * 0x02-0x05: Event status bits
+ */
+ case 0x0A:
+ {
+ CHECKAUTH;
+ IAP_TX_INIT(0x03, 0x0B);
+ IAP_TX_PUT_U32(device.changed_notifications);
+
+ iap_send_tx();
+
+ device.changed_notifications = 0;
+ break;
+ }
+
+ /* RetRemoteEventStatus (0x0B)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetiPodStateInfo (0x0C)
+ *
+ * Request state information from the iPod
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0C
+ * 0x02: Type information
+ *
+ * This command requires authentication
+ *
+ * Returns:
+ * RetiPodStateInfo
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0D
+ * 0x02: Type information
+ * 0x03-0xNN: State information
+ */
+ case 0x0C:
+ {
+ struct mp3entry* id3;
+ struct playlist_info* playlist;
+ int play_status;
+ struct tm* tm;
+
+ CHECKLEN(3);
+ CHECKAUTH;
+
+ IAP_TX_INIT(0x03, 0x0D);
+ IAP_TX_PUT(buf[0x02]);
+
+ switch (buf[0x02])
+ {
+ /* 0x00: Track position
+ * Data length: 4
+ */
+ case 0x00:
+ {
+ id3 = audio_current_track();
+ IAP_TX_PUT_U32(id3->elapsed);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x01: Track index
+ * Data length: 4
+ */
+ case 0x01:
+ {
+ playlist = playlist_get_current();
+ IAP_TX_PUT_U32(playlist->index - playlist->first_index);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x02: Chapter information
+ * Data length: 8
+ */
+ case 0x02:
+ {
+ playlist = playlist_get_current();
+ IAP_TX_PUT_U32(playlist->index - playlist->first_index);
+ /* Indicate that track does not have chapters */
+ IAP_TX_PUT_U16(0x0000);
+ IAP_TX_PUT_U16(0xFFFF);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x03: Play status
+ * Data length: 1
+ */
+ case 0x03:
+ {
+ /* TODO: Handle FF/REW
+ */
+ play_status = audio_status();
+ if (play_status & AUDIO_STATUS_PLAY) {
+ if (play_status & AUDIO_STATUS_PAUSE) {
+ IAP_TX_PUT(0x02);
+ } else {
+ IAP_TX_PUT(0x01);
+ }
+ } else {
+ IAP_TX_PUT(0x00);
+ }
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x04: Mute/UI/Volume
+ * Data length: 2
+ */
+ case 0x04:
+ {
+ /* Figuring out what the current volume is
+ * seems to be tricky.
+ * TODO: Fix.
+ */
+
+ /* Mute status */
+ IAP_TX_PUT(0x00);
+ /* Volume */
+ IAP_TX_PUT(0x80);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x05: Power/Battery
+ * Data length: 2
+ */
+ case 0x05:
+ {
+ iap_fill_power_state();
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x06: Equalizer state
+ * Data length: 4
+ */
+ case 0x06:
+ {
+ /* Currently only one equalizer setting supported, 0 */
+ IAP_TX_PUT_U32(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x07: Shuffle
+ * Data length: 1
+ */
+ case 0x07:
+ {
+ IAP_TX_PUT(global_settings.playlist_shuffle?0x01:0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x08: Repeat
+ * Data length: 1
+ */
+ case 0x08:
+ {
+ switch (global_settings.repeat_mode)
+ {
+ case REPEAT_OFF:
+ {
+ IAP_TX_PUT(0x00);
+ break;
+ }
+
+ case REPEAT_ONE:
+ {
+ IAP_TX_PUT(0x01);
+ break;
+ }
+
+ case REPEAT_ALL:
+ {
+ IAP_TX_PUT(0x02);
+ break;
+ }
+ }
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x09: Data/Time
+ * Data length: 6
+ */
+ case 0x09:
+ {
+ tm = get_time();
+
+ /* Year */
+ IAP_TX_PUT_U16(tm->tm_year);
+
+ /* Month */
+ IAP_TX_PUT(tm->tm_mon+1);
+
+ /* Day */
+ IAP_TX_PUT(tm->tm_mday);
+
+ /* Hour */
+ IAP_TX_PUT(tm->tm_hour);
+
+ /* Minute */
+ IAP_TX_PUT(tm->tm_min);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0A: Alarm
+ * Data length: 3
+ */
+ case 0x0A:
+ {
+ /* Alarm not supported, always off */
+ IAP_TX_PUT(0x00);
+ IAP_TX_PUT(0x00);
+ IAP_TX_PUT(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0B: Backlight
+ * Data length: 1
+ */
+ case 0x0B:
+ {
+ /* TOOD: Find out how to do this */
+ IAP_TX_PUT(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0C: Hold switch
+ * Data length: 1
+ */
+ case 0x0C:
+ {
+ IAP_TX_PUT(button_hold()?0x01:0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0D: Sound check
+ * Data length: 1
+ */
+ case 0x0D:
+ {
+ /* TODO: Find out what the hell this is. Default to off */
+ IAP_TX_PUT(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0E: Audiobook
+ * Data length: 1
+ */
+ case 0x0E:
+ {
+ /* Default to normal */
+ IAP_TX_PUT(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x0F: Track position in seconds
+ * Data length: 2
+ */
+ case 0x0F:
+ {
+ unsigned int pos;
+
+ id3 = audio_current_track();
+ pos = id3->elapsed/1000;
+
+ IAP_TX_PUT_U16(pos);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x10: Mute/UI/Absolute volume
+ * Data length: 3
+ */
+ case 0x10:
+ {
+ /* TODO: See volume above */
+ IAP_TX_PUT(0x00);
+ IAP_TX_PUT(0x80);
+ IAP_TX_PUT(0x80);
+
+ iap_send_tx();
+ break;
+ }
+
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* RetiPodStateInfo (0x0D)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* SetiPodStateInfo (0x0E)
+ *
+ * Set status information to new values
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0E
+ * 0x02: Type of information to change
+ * 0x03-0xNN: New information
+ *
+ * This command requires authentication
+ *
+ * Returns on success:
+ * IAP_ACK_OK
+ *
+ * Returns on failure:
+ * IAP_ACK_CMD_FAILED
+ * IAP_ACK_BAD_PARAM
+ */
+ case 0x0E:
+ {
+ CHECKLEN(3);
+ CHECKAUTH;
+ switch (buf[0x02])
+ {
+ /* Track position (ms)
+ * Data length: 4
+ */
+ case 0x00:
+ {
+ uint32_t pos;
+
+ CHECKLEN(7);
+ pos = get_u32(&buf[0x03]);
+ audio_ff_rewind(pos);
+
+ cmd_ok(cmd);
+ break;
+ }
+
+ /* Track index
+ * Data length: 4
+ */
+ case 0x01:
+ {
+ uint32_t index;
+
+ CHECKLEN(7);
+ index = get_u32(&buf[0x03]);
+ audio_skip(index-iap_get_trackindex());
+
+ cmd_ok(cmd);
+ break;
+ }
+
+ /* Chapter index
+ * Data length: 2
+ */
+ case 0x02:
+ {
+ /* This is not supported */
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Play status
+ * Data length: 1
+ */
+ case 0x03:
+ {
+ CHECKLEN(4);
+ switch(buf[0x03])
+ {
+ case 0x00:
+ {
+ audio_stop();
+ cmd_ok(cmd);
+ break;
+ }
+
+ case 0x01:
+ {
+ audio_resume();
+ cmd_ok(cmd);
+ break;
+ }
+
+ case 0x02:
+ {
+ audio_pause();
+ cmd_ok(cmd);
+ break;
+ }
+
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Volume/Mute
+ * Data length: 2
+ * TODO: Fix this
+ */
+ case 0x04:
+ {
+ CHECKLEN(5);
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Equalizer
+ * Data length: 5
+ */
+ case 0x06:
+ {
+ uint32_t index;
+
+ CHECKLEN(8);
+ index = get_u32(&buf[0x03]);
+ if (index == 0) {
+ cmd_ok(cmd);
+ } else {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ }
+ break;
+ }
+
+ /* Shuffle
+ * Data length: 2
+ */
+ case 0x07:
+ {
+ CHECKLEN(5);
+
+ switch(buf[0x03])
+ {
+ case 0x00:
+ {
+ iap_shuffle_state(false);
+ cmd_ok(cmd);
+ break;
+ }
+ case 0x01:
+ case 0x02:
+ {
+ iap_shuffle_state(true);
+ cmd_ok(cmd);
+ break;
+ }
+
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Repeat
+ * Data length: 2
+ */
+ case 0x08:
+ {
+ CHECKLEN(5);
+
+ switch(buf[0x03])
+ {
+ case 0x00:
+ {
+ iap_repeat_state(REPEAT_OFF);
+ cmd_ok(cmd);
+ break;
+ }
+ case 0x01:
+ {
+ iap_repeat_state(REPEAT_ONE);
+ cmd_ok(cmd);
+ break;
+ }
+ case 0x02:
+ {
+ iap_repeat_state(REPEAT_ALL);
+ cmd_ok(cmd);
+ break;
+ }
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Date/Time
+ * Data length: 6
+ */
+ case 0x09:
+ {
+ CHECKLEN(9);
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Alarm
+ * Data length: 4
+ */
+ case 0x0A:
+ {
+ CHECKLEN(7);
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Backlight
+ * Data length: 2
+ */
+ case 0x0B:
+ {
+ CHECKLEN(5);
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Sound check
+ * Data length: 2
+ */
+ case 0x0D:
+ {
+ CHECKLEN(5);
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Audio book speed
+ * Data length: 1
+ */
+ case 0x0E:
+ {
+ CHECKLEN(4);
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* Track position (s)
+ * Data length: 2
+ */
+ case 0x0F:
+ {
+ uint16_t pos;
+
+ CHECKLEN(5);
+ pos = get_u16(&buf[0x03]);
+ audio_ff_rewind(1000L * pos);
+
+ cmd_ok(cmd);
+ break;
+ }
+
+ /* Volume/Mute/Absolute
+ * Data length: 4
+ * TODO: Fix this
+ */
+ case 0x10:
+ {
+ CHECKLEN(7);
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* GetPlayStatus (0x0F)
+ *
+ * Request the current play status information
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x0F
+ *
+ * This command requires authentication
+ *
+ * Returns:
+ * RetPlayStatus
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x10
+ * 0x02: Play state
+ * 0x03-0x06: Current track index
+ * 0x07-0x0A: Current track length (ms)
+ * 0x0B-0x0E: Current track position (ms)
+ */
+ case 0x0F:
+ {
+ int play_status;
+ struct mp3entry* id3;
+ struct playlist_info* playlist;
+
+ CHECKAUTH;
+
+ IAP_TX_INIT(0x03, 0x10);
+
+ play_status = audio_status();
+
+ if (play_status & AUDIO_STATUS_PLAY) {
+ /* Playing or paused */
+ if (play_status & AUDIO_STATUS_PAUSE) {
+ /* Paused */
+ IAP_TX_PUT(0x02);
+ } else {
+ /* Playing */
+ IAP_TX_PUT(0x01);
+ }
+ playlist = playlist_get_current();
+ IAP_TX_PUT_U32(playlist->index - playlist->first_index);
+ id3 = audio_current_track();
+ IAP_TX_PUT_U32(id3->length);
+ IAP_TX_PUT_U32(id3->elapsed);
+ } else {
+ /* Stopped, all values are 0x00 */
+ IAP_TX_PUT(0x00);
+ IAP_TX_PUT_U32(0x00);
+ IAP_TX_PUT_U32(0x00);
+ IAP_TX_PUT_U32(0x00);
+ }
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetPlayStatus (0x10)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* SetCurrentPlayingTrack (0x11)
+ *
+ * Set the current playing track
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x11
+ * 0x02-0x05: Index of track to play
+ *
+ * This command requires authentication
+ *
+ * Returns on success:
+ * IAP_ACK_OK
+ *
+ * Returns on failure:
+ * IAP_ACK_BAD_PARAM
+ */
+ case 0x11:
+ {
+ uint32_t index;
+ uint32_t trackcount;
+
+ CHECKAUTH;
+ CHECKLEN(6);
+
+ index = get_u32(&buf[0x02]);
+ trackcount = playlist_amount();
+
+ if (index >= trackcount)
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ audio_skip(index-iap_get_trackindex());
+ cmd_ok(cmd);
+
+ break;
+ }
+
+ /* GetIndexedPlayingTrackInfo (0x12)
+ *
+ * Request information about a given track
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x12
+ * 0x02: Type of information to retrieve
+ * 0x03-0x06: Track index
+ * 0x07-0x08: Chapter index
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetIndexedPlayingTrackInfo
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x13
+ * 0x02: Type of information returned
+ * 0x03-0xNN: Information
+ */
+ case 0x12:
+ {
+ /* NOTE:
+ *
+ * Retrieving the track information from a track which is not
+ * the currently playing track can take a seriously long time,
+ * in the order of several seconds.
+ *
+ * This most certainly violates the IAP spec, but there's no way
+ * around this for now.
+ */
+ uint32_t track_index;
+ struct playlist_track_info track;
+ struct mp3entry id3;
+
+ CHECKLEN(0x09);
+ CHECKAUTH;
+
+ track_index = get_u32(&buf[0x03]);
+ if (-1 == playlist_get_track_info(NULL, track_index, &track)) {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+
+ IAP_TX_INIT(0x03, 0x13);
+ IAP_TX_PUT(buf[2]);
+ switch (buf[2])
+ {
+ /* 0x00: Track caps/info
+ * Information length: 10 bytes
+ */
+ case 0x00:
+ {
+ iap_get_trackinfo(track_index, &id3);
+ /* Track capabilities. None of these are supported, yet */
+ IAP_TX_PUT_U32(0x00);
+
+ /* Track length in ms */
+ IAP_TX_PUT_U32(id3.length);
+
+ /* Chapter count, stays at 0 */
+ IAP_TX_PUT_U16(0x00);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x01: Chapter time/name
+ * Information length: 4+variable
+ */
+ case 0x01:
+ {
+ /* Chapter length, set at 0 (no chapters) */
+ IAP_TX_PUT_U32(0x00);
+
+ /* Chapter name, empty */
+ IAP_TX_PUT_STRING("");
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x02, Artist name
+ * Information length: variable
+ */
+ case 0x02:
+ {
+ /* Artist name */
+ iap_get_trackinfo(track_index, &id3);
+ IAP_TX_PUT_STRLCPY(id3.artist);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x03, Album name
+ * Information length: variable
+ */
+ case 0x03:
+ {
+ /* Album name */
+ iap_get_trackinfo(track_index, &id3);
+ IAP_TX_PUT_STRLCPY(id3.album);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x04, Genre name
+ * Information length: variable
+ */
+ case 0x04:
+ {
+ /* Genre name */
+ iap_get_trackinfo(track_index, &id3);
+ IAP_TX_PUT_STRLCPY(id3.genre_string);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x05, Track title
+ * Information length: variable
+ */
+ case 0x05:
+ {
+ /* Track title */
+ iap_get_trackinfo(track_index, &id3);
+ IAP_TX_PUT_STRLCPY(id3.title);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x06, Composer name
+ * Information length: variable
+ */
+ case 0x06:
+ {
+ /* Track Composer */
+ iap_get_trackinfo(track_index, &id3);
+ IAP_TX_PUT_STRLCPY(id3.composer);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x07, Lyrics
+ * Information length: variable
+ */
+ case 0x07:
+ {
+ /* Packet information bits. All 0 (single packet) */
+ IAP_TX_PUT(0x00);
+
+ /* Packet index */
+ IAP_TX_PUT_U16(0x00);
+
+ /* Lyrics */
+ IAP_TX_PUT_STRING("");
+
+ iap_send_tx();
+ break;
+ }
+
+ /* 0x08, Artwork count
+ * Information length: variable
+ */
+ case 0x08:
+ {
+ /* No artwork, return packet containing just the type byte */
+ iap_send_tx();
+ break;
+ }
+
+ default:
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* RetIndexedPlayingTrackInfo (0x13)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetNumPlayingTracks (0x14)
+ *
+ * Request the number of tracks in the current playlist
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x14
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetNumPlayingTracks
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x15
+ * 0x02-0xNN: Number of tracks
+ */
+ case 0x14:
+ {
+ CHECKAUTH;
+
+ IAP_TX_INIT(0x03, 0x15);
+ IAP_TX_PUT_U32(playlist_amount());
+
+ iap_send_tx();
+ }
+
+ /* RetNumPlayingTracks (0x15)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetArtworkFormats (0x16)
+ *
+ * Request a list of supported artwork formats
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x16
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetArtworkFormats
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x17
+ * 0x02-0xNN: list of 7 byte format descriptors
+ */
+ case 0x16:
+ {
+ CHECKAUTH;
+
+ /* We return the empty list, meaning no artwork
+ * TODO: Fix to return actual artwork formats
+ */
+ IAP_TX_INIT(0x03, 0x17);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetArtworkFormats (0x17)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetTrackArtworkData (0x18)
+ *
+ * Request artwork for the given track
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x18
+ * 0x02-0x05: Track index
+ * 0x06-0x07: Format ID
+ * 0x08-0x0B: Track offset in ms
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetTrackArtworkData
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x19
+ * 0x02-0x03: Descriptor index
+ * 0x04: Pixel format code
+ * 0x05-0x06: Image width in pixels
+ * 0x07-0x08: Image height in pixels
+ * 0x09-0x0A: Inset rectangle, top left x
+ * 0x0B-0x0C: Inset rectangle, top left y
+ * 0x0D-0x0E: Inset rectangle, bottom right x
+ * 0x0F-0x10: Inset rectangle, bottom right y
+ * 0x11-0x14: Row size in bytes
+ * 0x15-0xNN: Image data
+ *
+ * If the image data does not fit in a single packet, subsequent
+ * packets omit bytes 0x04-0x14.
+ */
+ case 0x18:
+ {
+ CHECKAUTH;
+ CHECKLEN(0x0C);
+
+ /* No artwork support currently */
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+
+ /* RetTrackArtworkFormat (0x19)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetPowerBatteryState (0x1A)
+ *
+ * Request the current power state
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1A
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetPowerBatteryState
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1B
+ * 0x02: Power state
+ * 0x03: Battery state
+ */
+ case 0x1A:
+ {
+ IAP_TX_INIT(0x03, 0x1B);
+
+ iap_fill_power_state();
+ iap_send_tx();
+ break;
+ }
+
+ /* RetPowerBatteryState (0x1B)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* GetSoundCheckState (0x1C)
+ *
+ * Request the current sound check state
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1C
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetSoundCheckState
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1D
+ * 0x02: Sound check state
+ */
+ case 0x1C:
+ {
+ CHECKAUTH;
+
+ IAP_TX_INIT(0x03, 0x1D);
+ IAP_TX_PUT(0x00); /* Always off */
+
+ iap_send_tx();
+ break;
+ }
+
+ /* RetSoundCheckState (0x1D)
+ *
+ * Sent from the iPod to the device
+ */
+
+ /* SetSoundCheckState (0x1E)
+ *
+ * Set the sound check state
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1E
+ * 0x02: Sound check state
+ * 0x03: Restore on exit
+ *
+ * This command requires authentication.
+ *
+ * Returns on success
+ * IAP_ACK_OK
+ *
+ * Returns on failure
+ * IAP_ACK_CMD_FAILED
+ */
+ case 0x1E:
+ {
+ CHECKAUTH;
+ CHECKLEN(4);
+
+ /* Sound check is not supported right now
+ * TODO: Fix
+ */
+
+ cmd_ack(cmd, IAP_ACK_CMD_FAILED);
+ break;
+ }
+
+ /* GetTrackArtworkTimes (0x1F)
+ *
+ * Request a list of timestamps at which artwork exists in a track
+ *
+ * Packet format (offset in buf[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x1F
+ * 0x02-0x05: Track index
+ * 0x06-0x07: Format ID
+ * 0x08-0x09: Artwork Index
+ * 0x0A-0x0B: Artwork count
+ *
+ * This command requires authentication.
+ *
+ * Returns:
+ * RetTrackArtworkTimes
+ *
+ * Packet format (offset in data[]: Description)
+ * 0x00: Lingo ID: Display Remote Lingo, always 0x03
+ * 0x01: Command, always 0x20
+ * 0x02-0x05: Offset in ms
+ *
+ * Bytes 0x02-0x05 can be repeated multiple times
+ */
+ case 0x1F:
+ {
+ uint32_t index;
+ uint32_t trackcount;
+
+ CHECKAUTH;
+ CHECKLEN(0x0C);
+
+ /* Artwork is currently unsuported, just check for a valid
+ * track index
+ */
+ index = get_u32(&buf[0x02]);
+ trackcount = playlist_amount();
+
+ if (index >= trackcount)
+ {
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+ break;
+ }
+
+ /* Send an empty list */
+ IAP_TX_INIT(0x03, 0x20);
+
+ iap_send_tx();
+ break;
+ }
+
+ /* The default response is IAP_ACK_BAD_PARAM */
+ default:
+ {
+#ifdef LOGF_ENABLE
+ logf("iap: Unsupported Mode03 Command");
+#else
+ cmd_ack(cmd, IAP_ACK_BAD_PARAM);
+#endif
+ break;
+ }
+ }
+}