rockbox/apps/iap/iap-lingo3.c
LiveboxAndy 77f8c9c9f1 Update to allow the Apple Radio Remote to function on iPod Video 5G.
This was broken when the major update to iap was comitted.
ia-lingo7.c created and various iap related files modified.
On 4G, 6G and Nano 1/2Gen iPods the remote will function
even though the radio won't.
Tested on 4G Greyscale, 4G Color, 4G Photo, 4G Mini 1st Gen,
4G Mini 2Gen, Nano 1G, Nano 2G, Video 5G, Video 5.5G

Change-Id: Ia74e3d07d9ab5edc6da8eafa96801ede722be331
2020-07-09 18:02:07 +00:00

1535 lines
43 KiB
C

/***************************************************************************
* __________ __ ___.
* 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"
#if CONFIG_TUNER
#include "ipod_remote_tuner.h"
#endif
/*
* 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 = global_settings.volume;
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:
{
if (device.mute == false) {
/* Mute status False*/
IAP_TX_PUT(0x00);
/* Volume */
IAP_TX_PUT(0xFF & (int)((global_settings.volume + 90) * 2.65625));
} else {
/* Mute status True*/
IAP_TX_PUT(0x01);
/* Volume should be 0 if muted */
IAP_TX_PUT(0x00);
}
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:
{
if (device.mute == false) {
/* Mute status False*/
IAP_TX_PUT(0x00);
/* Volume */
IAP_TX_PUT(0xFF & (int)((global_settings.volume + 90) * 2.65625));
IAP_TX_PUT(0xFF & (int)((global_settings.volume + 90) * 2.65625));
} else {
/* Mute status True*/
IAP_TX_PUT(0x01);
/* Volume should be 0 if muted */
IAP_TX_PUT(0x00);
IAP_TX_PUT(0x00);
}
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;
}
case 0x04:
{
CHECKLEN(5);
if (buf[0x03]==0x00){
/* Not Muted */
global_settings.volume = (int) (buf[0x04]/2.65625)-90;
device.mute = false;
}
else {
device.mute = true;
}
cmd_ok(cmd);
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);
if (buf[0x03]==0x00){
/* Not Muted */
global_settings.volume = (int) (buf[0x04]/2.65625)-90;
device.mute = false;
}
else {
device.mute = true;
}
cmd_ok(cmd);
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");
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}
}