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
This commit is contained in:
LiveboxAndy 2018-08-04 18:02:21 +01:00 committed by Solomon Peachy
parent dcdf2713f6
commit 77f8c9c9f1
11 changed files with 896 additions and 196 deletions

View file

@ -70,8 +70,10 @@ iap/iap-lingo1.c
iap/iap-lingo2.c
iap/iap-lingo3.c
iap/iap-lingo4.c
#if CONFIG_TUNER
iap/iap-lingo7.c
#endif
#endif
screen_access.c
#ifdef HAVE_BUTTONBAR
gui/buttonbar.c

View file

@ -45,7 +45,9 @@
#include "usb.h"
#include "tuner.h"
#if CONFIG_TUNER
#include "ipod_remote_tuner.h"
#endif
/* MS_TO_TICKS converts a milisecond time period into the
@ -164,7 +166,13 @@ unsigned char lingo_versions[32][2] = {
{1, 5}, /* Display remote lingo, 0x03 */
{1, 12}, /* Extended Interface lingo, 0x04 */
{1, 1}, /* RF/BT Transmitter lingo, 0x05 */
{} /* All others are unsupported */
{0, 0}, /* USB Host lingo, 0x06, disabled */
#if CONFIG_TUNER
{1, 0}, /* RF Receiver lingo, 0x07 */
#else
{0, 0}, /* RF Receiver lingo, 0x07 disabled */
#endif
{} /* every other lingo, disabled */
};
/* states of the iap de-framing state machine */
@ -308,6 +316,17 @@ static int iap_task(struct timeout *tmo)
return MS_TO_TICKS(100);
}
void iap_set_remote_volume(void)
{
IAP_TX_INIT(0x03, 0x0D);
IAP_TX_PUT(0x04);
IAP_TX_PUT(0x00);
IAP_TX_PUT(0xFF & (int)((global_settings.volume + 90) * 2.65625));
iap_send_tx();
}
/* This thread is waiting for events posted to iap_queue and calls
* the appropriate subroutines in response
*/
@ -859,11 +878,15 @@ void iap_periodic(void)
/* Volume change notifications are sent every 100ms */
if (device.notifications & (BIT_N(4) | BIT_N(16))) {
/* Currently we do not track volume changes, so this is
* never sent.
/* Currently we do not track volume changes for BIT_N(16),
*
* TODO: Fix volume tracking
*/
IAP_TX_INIT(0x03, 0x09);
IAP_TX_PUT(0x04);
IAP_TX_PUT(0x00);
IAP_TX_PUT(0xFF &(int)((global_settings.volume + 90) * 2.65625));
device.changed_notifications |= BIT_N(4);
iap_send_tx();
}
/* All other events are sent every 500ms */
@ -972,9 +995,15 @@ void iap_periodic(void)
unsigned char play_status;
play_status = audio_status();
if (device.play_status != play_status)
{
/* If play_status = PAUSE/STOP we should mute else
* we should unmute
* 0 = Stopped
* 1 = Playing
* 2 = Pause
* 3 = Play/Pause
*/
IAP_TX_INIT(0x03, 0x09);
IAP_TX_PUT(0x03);
if (play_status & AUDIO_STATUS_PLAY) {
@ -994,6 +1023,23 @@ void iap_periodic(void)
iap_send_tx();
device.play_status = play_status;
if (play_status != 1) {
/* Not Playing */
audio_pause();
#if CONFIG_TUNER
if (radio_present==1) {
tuner_set(RADIO_MUTE,1);
}
#endif
} else {
/* Playing */
audio_resume();
#if CONFIG_TUNER
if (radio_present==1) {
tuner_set(RADIO_MUTE,0);
}
#endif
}
}
}
@ -1212,43 +1258,6 @@ static void iap_handlepkt_mode5(const unsigned int len, const unsigned char *buf
}
}
#if 0
static void iap_handlepkt_mode7(const unsigned int len, const unsigned char *buf)
{
unsigned int cmd = buf[1];
switch (cmd)
{
/* RetTunerCaps */
case 0x02:
{
/* do nothing */
/* GetAccessoryInfo */
unsigned char data[] = {0x00, 0x27, 0x00};
iap_send_pkt(data, sizeof(data));
break;
}
/* RetTunerFreq */
case 0x0A:
/* fall through */
/* TunerSeekDone */
case 0x13:
{
rmt_tuner_freq(len, buf);
break;
}
/* RdsReadyNotify, RDS station name 0x21 1E 00 + ASCII text*/
case 0x21:
{
rmt_tuner_rds_data(len, buf);
break;
}
}
}
#endif
void iap_handlepkt(void)
{
int level;
@ -1271,17 +1280,21 @@ void iap_handlepkt(void)
logf("R: %s", hexstring(iap_rxstart+2, (length)));
#endif
unsigned char mode = *(iap_rxstart+2);
switch (mode) {
case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break;
if (length != 0) {
unsigned char mode = *(iap_rxstart+2);
switch (mode) {
case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break;
#ifdef HAVE_LINE_REC
case 1: iap_handlepkt_mode1(length, iap_rxstart+2); break;
case 1: iap_handlepkt_mode1(length, iap_rxstart+2); break;
#endif
case 2: iap_handlepkt_mode2(length, iap_rxstart+2); break;
case 3: iap_handlepkt_mode3(length, iap_rxstart+2); break;
case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break;
case 5: iap_handlepkt_mode5(length, iap_rxstart+2); break;
/* case 7: iap_handlepkt_mode7(length, iap_rxstart+2); break; */
case 2: iap_handlepkt_mode2(length, iap_rxstart+2); break;
case 3: iap_handlepkt_mode3(length, iap_rxstart+2); break;
case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break;
case 5: iap_handlepkt_mode5(length, iap_rxstart+2); break;
#if CONFIG_TUNER
case 7: iap_handlepkt_mode7(length, iap_rxstart+2); break;
#endif
}
}
/* Remove the handled packet from the RX buffer

View file

@ -34,6 +34,7 @@
/* The Model ID of the iPod we emulate. Currently a 160GB classic */
#define IAP_IPOD_MODEL (0x00130200U)
#define IAP_IPOD_VARIANT "MC293"
/* The firmware version we emulate. Currently 2.0.3 */
#define IAP_IPOD_FIRMWARE_MAJOR (2)
@ -73,14 +74,15 @@ enum interface_state {
/* States of the authentication state machine */
enum authen_state {
AUST_NONE, /* Initial state, no message sent */
AUST_INIT, /* Remote side has requested authentication */
AUST_CERTREQ, /* Remote certificate requested */
AUST_CERTBEG, /* Certificate is being received */
AUST_CERTDONE, /* Certificate received */
AUST_CHASENT, /* Challenge sent */
AUST_CHADONE, /* Challenge response received */
AUST_AUTH, /* Authentication complete */
AUST_NONE, /* Initial state, no message sent */
AUST_INIT, /* Remote side has requested authentication */
AUST_CERTREQ, /* Remote certificate requested */
AUST_CERTBEG, /* Certificate is being received */
AUST_CERTALLRECEIVED, /* Certificate all Received */
AUST_CERTDONE, /* Certificate all Done */
AUST_CHASENT, /* Challenge sent */
AUST_CHADONE, /* Challenge response received */
AUST_AUTH, /* Authentication complete */
};
/* State of authentication */
@ -237,6 +239,7 @@ void iap_repeat_next(void);
void iap_fill_power_state(void);
void iap_send_tx(void);
void iap_set_remote_volume(void);
extern enum interface_state interface_state;
void iap_interface_state_change(const enum interface_state new);

View file

@ -24,3 +24,6 @@ void iap_handlepkt_mode1(const unsigned int len, const unsigned char *buf);
void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf);
void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf);
void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf);
#if CONFIG_TUNER
void iap_handlepkt_mode7(const unsigned int len, const unsigned char *buf);
#endif

View file

@ -21,6 +21,10 @@
#include "iap-lingo.h"
#include "kernel.h"
#include "system.h"
#include "tuner.h"
#if CONFIG_TUNER
#include "ipod_remote_tuner.h"
#endif
/*
* This macro is meant to be used inside an IAP mode message handler.
@ -45,10 +49,13 @@
static void cmd_ack(const unsigned char cmd, const unsigned char status)
{
IAP_TX_INIT(0x00, 0x02);
IAP_TX_PUT(status);
IAP_TX_PUT(cmd);
iap_send_tx();
if (cmd != 0){
IAP_TX_INIT(0x00, 0x02);
IAP_TX_PUT(status);
IAP_TX_PUT(cmd);
iap_send_tx();
}
}
#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
@ -59,6 +66,7 @@ static void cmd_pending(const unsigned char cmd, const uint32_t msdelay)
IAP_TX_PUT(0x06);
IAP_TX_PUT(cmd);
IAP_TX_PUT_U32(msdelay);
iap_send_tx();
}
@ -387,7 +395,7 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
{
IAP_TX_INIT(0x00, 0x0E);
IAP_TX_PUT_U32(IAP_IPOD_MODEL);
IAP_TX_PUT_STRING("ROCKBOX");
IAP_TX_PUT_STRING(IAP_IPOD_VARIANT);
iap_send_tx();
break;
@ -476,6 +484,59 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
/* Issuing this command exits any extended interface states */
iap_interface_state_change(IST_STANDARD);
/*
* Actions by remote listed Apple Firmware Rockbox Firmware
* Apple remote on Radio pause/play - Mutes Mutes
* vol up/down - Vol Up/Dn Vol Up/Dn
* FF/FR - Station Up/Dn Station Up/Dn
* iPod Pause/Play - Mutes Mutes
* Vol up/down - Vol Up/Dn Vol Up/Dn
* FF/FR - Station Up/Dn Station Up/Dn
* Remote pause/play - Pause/Play Pause/Play
* vol up/down - Vol Up/Dn Vol Up/Dn
* FF/FR - Next/Prev Track Next/Prev Track
* iPod Pause/Play - Pause/Play Pause/Play
* Vol up/down - Vol Up/Dn Vol Up/Dn
* FF/FR - Next/Prev Track Next/Prev Track
*
* The following bytes are returned by the accessories listed
* FF 55 0E 00 13 00 00 00 3D 00 00 00 04 00 00 00 00 9E robi DAB Radio Remote
* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??) FM Transmitter
* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 Apple Radio Remote
*
* Bytes 9-12 = Options 11111100 0000 00 00
* 54321098 7654 32 10
* 00000004 = 00000000 00000000 00000000 0000 01 00 Bits 2
* 00000004 = 00000000 00000000 00000000 0000 01 00 Bits 2
* 0000000E = 00000000 00000000 00000000 0000 01 10 Bits 12
*
* Bit 0: Authentication 00 = No Authentication
* 01 = Defer Auth until required (V1)
* Bit 1: 10 = Authenticate Immediately (V2)
* 11 = Reserved
* Bit 2: Power Requirements 00 = Low Power Only 10 = Reserved
* Bit 3: 01 = Int High Power 11 = Reserved
*
* Bytes 13-16 = Device ID
* 00000000
* 00000000
* 00000003
*
* Bytes 5-8 = lingoes spoken 11111100 00000000
* 54321098 76543210
* 0000003D = 00000000 00000000 00000000 00111101 Bits 2345
* 00000035 = 00000000 00000000 00000000 00110101 Bits 245
* 0000008D = 00000000 00000000 00000000 10001101 Bits 237
*
*
* Bit 0: Must be set by all devices. See above
* Bit 1: Microphone Lingo
* Bit 2: Simple Remote
* Bit 3: Display Remote
* Bit 4: Extended Remote
* Bit 5: RF Transmitter lingo
*/
/* Loop through the lingoes advertised by the device.
* If it tries to use a lingo we do not support, return
* a Command Failed ACK.
@ -531,33 +592,49 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
cmd_ok(cmd);
/* Bit 0: Must be set by all devices. See above*/
/* Bit 1: Microphone Lingo */
/* Bit 2: Simple Remote */
/* Bit 3: Display Remote */
/* Bit 4: Extended Remote */
/* Bit 5: RF Transmitter lingo */
if (lingoes & (1 << 5))
{
/* FM transmitter sends this: */
/* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
/* 0x00000035 = 00000000 00000000 00000000 00110101 */
/* 1<<5 1 */
/* GetAccessoryInfo */
unsigned char data2[] = {0x00, 0x27, 0x00};
iap_send_pkt(data2, sizeof(data2));
IAP_TX_INIT(0x00, 0x27);
IAP_TX_PUT(0x00);
iap_send_tx();
/* RF Transmitter: Begin transmission */
unsigned char data3[] = {0x05, 0x02};
iap_send_pkt(data3, sizeof(data3));
IAP_TX_INIT(0x05, 0x02);
iap_send_tx();
}
#if 0
/* Bit 6: USB Host Control */
/* Bit 7: RF Tuner lingo */
#if CONFIG_TUNER
if (lingoes & (1 << 7))
{
/* ipod fm remote sends this: */
/* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
/* ipod fm radio remote sends this: */
/* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 */
/* 0x0000008D = 00000000 00000000 00000000 00011101 */
/* 1<<7 */
radio_present = 1;
/* GetDevAuthenticationInfo */
unsigned char data4[] = {0x00, 0x14};
iap_send_pkt(data4, sizeof(data4));
}
#endif
/* Bit 8: Accessory Equalizer Lingo */
/* Bit 9: Reserved */
/* Bit 10: Digial Audio Lingo */
/* Bit 11: Reserved */
/* Bit 12: Storage Lingo */
/* Bit 13: Reserved */
/* .................*/
/* Bit 31: Reserved */
break;
}
@ -594,7 +671,8 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
{
/* There are two formats of this packet. One with only
* the version information bytes (for Auth version 1.0)
* and the long form shown above
* and the long form shown above but it must be at least 4
* bytes long
*/
CHECKLEN(4);
@ -605,25 +683,8 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
device.auth.version = (buf[2] << 8) | buf[3];
/* We support authentication versions 1.0 and 2.0 */
if (device.auth.version == 0x100) {
/* If we could really do authentication we'd have to
* check the certificate here. Since we can't, just acknowledge
* the packet with an "everything OK" AckDevAuthenticationInfo
*
* Skip GetAccessoryInfo process, this command together with
* authentication level 2 were added in iAP release 24, it is
* not be supported by devices authenticating at level 1.
*/
IAP_TX_INIT(0x00, 0x16);
IAP_TX_PUT(0x00);
iap_send_tx();
device.auth.state = AUST_CERTDONE;
break;
}
if (device.auth.version != 0x200) {
/* We only support authentication versions 1.0 and 2.0 */
if ((device.auth.version != 0x100) && (device.auth.version != 0x200)) {
/* Version mismatches are signalled by AckDevAuthenticationInfo
* with the status set to Authentication Information unsupported
*/
@ -635,61 +696,77 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
iap_send_tx();
break;
}
/* There must be at least one byte of certificate data
* in the packet
*/
CHECKLEN(7);
switch (device.auth.state)
{
/* This is the first packet. Note the maximum section number
* so we can check it later.
if (device.auth.version == 0x100) {
/* If we could really do authentication we'd have to
* check the certificate here. Since we can't, just acknowledge
* the packet later with an "everything OK" AckDevAuthenticationInfo
* and change device.auth.state to AuthenticateState_CertificateDone
*/
case AUST_CERTREQ:
device.auth.state = AUST_CERTALLRECEIVED;
} else {
/* Version 2.00 requires at least one byte of certificate data
* in the packet
*/
CHECKLEN(7);
switch (device.auth.state)
{
device.auth.max_section = buf[5];
device.auth.state = AUST_CERTBEG;
/* This is the first packet. Note the maximum section number
* so we can check it later.
*/
case AUST_CERTREQ:
{
device.auth.max_section = buf[5];
device.auth.state = AUST_CERTBEG;
/* Intentional fall-through */
}
/* All following packets */
case AUST_CERTBEG:
{
/* Check if this is the expected section */
if (buf[4] != device.auth.next_section) {
/* Intentional fall-through */
}
/* All following packets */
case AUST_CERTBEG:
{
/* Check if this is the expected section */
if (buf[4] != device.auth.next_section) {
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
/* Is this the last section? */
if (device.auth.next_section == device.auth.max_section) {
/* If we could really do authentication we'd have to
* check the certificate here. Since we can't, just acknowledge
* the packet later with an "everything OK" AckDevAuthenticationInfo
* and change device.auth.state to AuthenticateState_CertificateDone
*/
device.auth.state = AUST_CERTALLRECEIVED;
} else {
device.auth.next_section++;
cmd_ok(cmd);
}
break;
}
default:
{
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
/* Is this the last section? */
if (device.auth.next_section == device.auth.max_section) {
/* If we could really do authentication we'd have to
* check the certificate here. Since we can't, just acknowledge
* the packet with an "everything OK" AckDevAuthenticationInfo
*
* Also, start GetAccessoryInfo process
*/
IAP_TX_INIT(0x00, 0x16);
IAP_TX_PUT(0x00);
iap_send_tx();
device.auth.state = AUST_CERTDONE;
device.accinfo = ACCST_INIT;
} else {
device.auth.next_section++;
cmd_ok(cmd);
}
break;
}
default:
{
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}
if (device.auth.state == AUST_CERTALLRECEIVED) {
/* We've received all the certificate data so just
*Acknowledge everything OK
*/
IAP_TX_INIT(0x00, 0x16);
IAP_TX_PUT(0x00);
iap_send_tx();
/* GetAccessoryInfo*/
IAP_TX_INIT(0x00, 0x27);
IAP_TX_PUT(0x00);
iap_send_tx();
device.auth.state = AUST_CERTDONE;
}
break;
}
@ -744,6 +821,17 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
iap_send_tx();
device.auth.state = AUST_AUTH;
#if CONFIG_TUNER
if (radio_present == 1)
{
/* GetTunerCaps */
IAP_TX_INIT(0x07, 0x01);
iap_send_tx();
}
#endif
iap_set_remote_volume();
break;
}
@ -926,7 +1014,9 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
CHECKLEN(7);
device.capabilities = get_u32(&buf[0x03]);
/* Type 0x00 was already queried, that's where this information comes from */
/* Type 0x00 was already queried, that's where this
* information comes from
*/
device.capabilities_queried = 0x01;
device.capabilities &= ~0x01;
break;
@ -1045,9 +1135,8 @@ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf)
{
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode00 Command");
#else
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}

View file

@ -212,6 +212,7 @@ void iap_handlepkt_mode1(const unsigned int len, const unsigned char *buf)
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode1 Command");
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}

View file

@ -30,6 +30,10 @@
#include "button.h"
#include "audio.h"
#include "settings.h"
#include "tuner.h"
#if CONFIG_TUNER
#include "ipod_remote_tuner.h"
#endif
/*
* This macro is meant to be used inside an IAP mode message handler.
@ -57,6 +61,9 @@ static void cmd_ack(const unsigned char cmd, const unsigned char status)
void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
{
static bool poweron_pressed = false;
#if CONFIG_TUNER
static bool remote_mute = false;
#endif
unsigned int cmd = buf[1];
/* We expect at least three bytes in the buffer, one for the
@ -95,7 +102,21 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
if(buf[2] != 0)
{
if(buf[2] & 1)
{
REMOTE_BUTTON(BUTTON_RC_PLAY);
#if CONFIG_TUNER
if (radio_present == 1) {
if (remote_mute == 0) {
/* Not Muted so radio on*/
tuner_set(RADIO_MUTE,0);
} else {
/* Muted so radio off*/
tuner_set(RADIO_MUTE,1);
}
remote_mute = !remote_mute;
}
#endif
}
if(buf[2] & 2)
REMOTE_BUTTON(BUTTON_RC_VOL_UP);
if(buf[2] & 4)
@ -111,11 +132,21 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
{
if (audio_status() != AUDIO_STATUS_PLAY)
REMOTE_BUTTON(BUTTON_RC_PLAY);
#if CONFIG_TUNER
if (radio_present == 1) {
tuner_set(RADIO_MUTE,0);
}
#endif
}
if(buf[3] & 2) /* pause */
{
if (audio_status() == AUDIO_STATUS_PLAY)
REMOTE_BUTTON(BUTTON_RC_PLAY);
#if CONFIG_TUNER
if (radio_present == 1) {
tuner_set(RADIO_MUTE,1);
}
#endif
}
if(buf[3] & 128) /* Shuffle */
{
@ -295,9 +326,8 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
{
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode02 Command");
#else
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}

View file

@ -38,6 +38,9 @@
#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.
@ -262,7 +265,7 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
device.play_status = audio_status();
/* TODO: Fix this */
device.mute = false;
device.volume = 0x80;
device.volume = global_settings.volume;
device.power_state = charger_input_state;
device.battery_level = battery_level();
/* TODO: Fix this */
@ -299,8 +302,8 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
/* GetRemoteEventStatus (0x0A)
*
* Request the events changed since the last call to GetREmoteEventStatus
* or SetRemoteEventNotification
* 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
@ -434,15 +437,18 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
*/
case 0x04:
{
/* Figuring out what the current volume is
* seems to be tricky.
* TODO: Fix.
*/
if (device.mute == false) {
/* Mute status False*/
IAP_TX_PUT(0x00);
/* Volume */
IAP_TX_PUT(0xFF & (int)((global_settings.volume + 90) * 2.65625));
/* Mute status */
IAP_TX_PUT(0x00);
/* Volume */
IAP_TX_PUT(0x80);
} else {
/* Mute status True*/
IAP_TX_PUT(0x01);
/* Volume should be 0 if muted */
IAP_TX_PUT(0x00);
}
iap_send_tx();
break;
@ -620,15 +626,24 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
*/
case 0x10:
{
/* TODO: See volume above */
IAP_TX_PUT(0x00);
IAP_TX_PUT(0x80);
IAP_TX_PUT(0x80);
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);
@ -746,14 +761,18 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
break;
}
/* Volume/Mute
* Data length: 2
* TODO: Fix this
*/
case 0x04:
{
CHECKLEN(5);
cmd_ack(cmd, IAP_ACK_CMD_FAILED);
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;
}
@ -919,7 +938,16 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
case 0x10:
{
CHECKLEN(7);
cmd_ack(cmd, IAP_ACK_CMD_FAILED);
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;
}
@ -1499,9 +1527,8 @@ void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf)
{
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode03 Command");
#else
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}

View file

@ -109,7 +109,6 @@ static void seek_to_playlist(unsigned long index)
ft_play_playlist(selected_playlist,
global_settings.playlist_catalog_dir,
strrchr(selected_playlist, '/') + 1);
}
static unsigned long nbr_total_playlists(void)
@ -2019,8 +2018,8 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
playlist_randomise(NULL, current_tick, true);
}
else
{
playlist_sort(NULL, true);
{
playlist_sort(NULL, true);
}
audio_skip(index - playlist_next(0));
if (!paused)

491
apps/iap/iap-lingo7.c Normal file
View file

@ -0,0 +1,491 @@
/***************************************************************************
* __________ __ ___.
* 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.
*
****************************************************************************/
#include "iap-core.h"
#include "iap-lingo.h"
#include "kernel.h"
#include "system.h"
#include "tuner.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 inbufferfer.
* If the inbufferfer 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(0x07, 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_mode7(const unsigned int len, const unsigned char *inbuffer)
{
/* Note that some of the Lingo Mode 7 commands are handled by
* ../firmware/drivers/tuner/ipod_remote_tuner.c as some of the
* commands are sourced with the remote as the master with the ipod acting
* as the slave.
*/
unsigned char cmd = inbuffer[1];
unsigned char statusnotifymaskbyte = 0;
/* We expect at least two bytes in the inbuffer, one for the
* lingo and one for the command
*/
CHECKLEN(2);
/* Lingo 0x07 must have been negotiated */
if (!DEVICE_LINGO_SUPPORTED(0x07)) {
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
return;
}
switch (cmd)
{
/* case 00 ToIpod Ack 2/6 bytes*/
case 0x00:
{
/* 0x00 OK
* 0x01 Unknown Track Category
* 0x02 Command Failed. Command is valid but did not succeed
* 0x03 Out Of Resources
* 0x04 Bad Parameter
* 0x05 Unknown Track ID
* 0x06 Command Pending.
* 0x07 Not Authenticated
*
* byte 1 is ID of command being acknowledged
* bytes 2-5 only if status byte is pending. timeout in ms.
*/
break;
}
/* case 0x01 ToAccessory GetTunerCaps
* This is sent by iap-lingo0.c through case 0x15 after device
* has been authenticated FF55020701F6
*/
/* case 02 ToIpod RetTunerCaps 8 bytes */
case 0x02:
{
/* Capabilities are stored as bits in first 4 bytes,
* inbuffer[2] byte is bits 31:24
* inbuffer[3] byte is bits 23:16
* inbuffer[4] byte is bits 15:08
* inbuffer[5] byte is bits 07:00
* inbuffer[6] and inbuffer[7] are all reserved bits
* Bit 0 = AM Band 520-1710 Khz
* Bit 1 = FM Europe/US 87.5 - 108.0 Mhz
* Bit 2 = FM Japan 76.0 - 90.0 Mhz
* Bit 3 = FM Wide 76.0 - 108.0 Mhz
* Bit 4 = HD Radio Capable
* Bit 5:7 Reserved
* Bit 8 = Tuner Power On/Off Control Capable
* Bit 9 = Status Change Notification Capable
* Bit 10:15 Reserved
* Bit 17:16 Minimum FM Resolution ID Bits
* 00 = 200Khz, 01 = 100Khz, 10 = 50Khz, 11 = reserved
* Bit 18 = Tuner Seek Up/Down Capable
* Bit 19 = Tuner Seek RSSI Threshold. Only if 18=1
* Bit 20 = Force Monophonic mode capable
* Bit 21 = Stero Blend Capable
* Bit 22 = FM Tuner deemphasis select capable
* Bit 23 = AM Tuner Resolution 9Khz (0=10Khz Only) capable
* Bit 24 = Radio Data System (RDS/RBDS) data capable
* Bit 25 = Tuner Channel RSSI indicator capable
* Bit 26 = Stero Source Indicator capable
* Bit 27 = RDS/RBDS Raw mode capable
* Bit 31:28 Reserved
*
* ipod Tuner returns 07 5E 07 0E 10 4B
* Bytes 6,7 Reserved
* ???????? ????????
* ???????? ????????
* 00010000 01001011
*
* Byte 5 - 0E
* 00000000
* 76543210
* 00001110
* AM
* FM Europe/US
* FM Japan
* FM Wide
*
* Byte 4 - 07
* 11111100
* 54321098
* 00000111
* Tuner Power On/Off
* Status Change Notification
* ?? Should be reserved
*
* Byte 3 - 5E
* 22221111
* 32109876
* 01011110
* Tuner Seek Up/Down
* Tuner Seek RSSI Threshold
* Force Mono Mode Capable
* Stereo Blend Capable
* FM Tuner deemphasis select capable
*
* Byte 2 - 07
* 33222222
* 10987654
* 00000111
* RDS/RBDS Capable
* Tuner Channel RSSI Indicator
* Stereo Source
*
* Just need to see what we can use this data for
* Make a selection for the tuner mode to select
* Preference is
* 1st - 76 to 108 FM
* 2nd - 87.5 to 108 Fm
* 3rd - 76 to 90 Fm
* 4th - AM
*
*/
if ((inbuffer[4] & 0x03) >0) {
statusnotifymaskbyte = 0;
if ((inbuffer[4] >> 0) & 0x01) {
/* Supports Tuner Power On/Off, so set ON */
statusnotifymaskbyte = 1;
}
if ((inbuffer[4] >> 1) & 0x01) {
/* Supports Status Change Notification so set ON */
/* Apple 5/6/7G firmware does NOT enable this bit */
/* statusnotifymaskbyte += 2; */
}
IAP_TX_INIT(0x07, 0x05);
IAP_TX_PUT(statusnotifymaskbyte);
iap_send_tx();
}
if ((inbuffer[5] >> 1) & 0x01) {
/* Supports FM Europe/US Tuner 87.5 - 108.0 Mhz */
/* Apple firmware sends this before setting region */
IAP_TX_INIT(0x07, 0x0E);
IAP_TX_PUT(0x00);
iap_send_tx();
/* Apple firmware then sends region */
IAP_TX_INIT(0x07, 0x08);
IAP_TX_PUT(0x02);
iap_send_tx();
} else if ((inbuffer[5] >> 3) & 0x01) {
/* Supports FM Wide Tuner 76 - 108.0 Mhz */
/* apple firmware send this before setting region */
IAP_TX_INIT(0x07, 0x0E);
IAP_TX_PUT(0x00);
iap_send_tx();
/* Apple firmware then send region */
IAP_TX_INIT(0x07, 0x08);
IAP_TX_PUT(0x08);
iap_send_tx();
} else if ((inbuffer[5] >> 2) & 0x01) {
/* Supports FM Japan Tuner 76 - 90.0 Mhz */
/* apple firmware send this before setting region */
IAP_TX_INIT(0x07, 0x0E);
IAP_TX_PUT(0x41);
iap_send_tx();
/* Apple firmware then send region */
IAP_TX_INIT(0x07, 0x08);
IAP_TX_PUT(0x04);
iap_send_tx();
} else if ((inbuffer[5] >> 0) & 0x01) {
/* Supports AM Tuner */
IAP_TX_INIT(0x07, 0x08);
IAP_TX_PUT(0x01);
iap_send_tx();
}
if ((inbuffer[2] & 0x03) > 0) {
statusnotifymaskbyte = 0;
if ((inbuffer[2] >> 0) & 0x01) {
/* Supports RDS/RBDS Capable so set
*StatusChangeNotify for RDS/RBDS Data
*/
statusnotifymaskbyte = 1;
}
if ((inbuffer[2] >> 1) & 0x01) {
/* Supports Tuner Channel RSSi Indicator Capable so set */
/* StatusChangeNotify for RSSI */
/* Apple 5G firmware does NOT enable this bit so we wont */
/* statusnotifymaskbyte += 2; */
}
IAP_TX_INIT(0x07, 0x18);
IAP_TX_PUT(statusnotifymaskbyte);
iap_send_tx();
}
if ((inbuffer[4] >> 2) & 0x01) {
/* Reserved */
}
if ((inbuffer[4] >> 3) & 0x01) {
/* Reserved */
}
if ((inbuffer[3] >> 1) & 0x01) {
/* Tuner Seek Up/Down` */
}
if ((inbuffer[3] >> 2) & 0x01) {
/* Tuner Seek RSSI Threshold */
}
if ((inbuffer[3] >> 3) & 0x01) {
/* Force Mono Mode */
}
if ((inbuffer[3] >> 4) & 0x01) {
/* Stereo Blend */
}
if ((inbuffer[3] >> 6) & 0x01) {
/* FM Tuner deemphasis */
}
if ((inbuffer[2] >> 2) & 0x01) {
/* Stereo Source */
}
break;
}
/* case 03 ToAccessory GetTunerCtrl 2 bytes */
/* case 04 ToIpod RetTunerCtrl 3 bytes
* Bit 0 power is on (1) or Off (0)
* Bit 1 StatusChangeNotify is enabled (1) or disabled (0)
* Bit 3 RDS/RBDS Raw mode enabled
*
* Should/Can we do something with these?
*/
/* case 05 ToAccessory SetTunerCtrl 3 bytes
* Bits as per 0x04 above
* Bit 0/1 set through Lingo7 Cmd02 */
/* case 06 ToAccessory GetTunerBand 2 bytes */
/* case 07 ToIpod RetTunerBand 3 bytes
* Returns current band for Tuner. See 0x08 below
*
* Should/Can we do something with these?
*/
/* case 08 ToAccessory SetTuneBand
* Set Bit 0 for AM
* Set Bit 1 for FM Europe/U S 87.5-108Mhz
* Set Bit 2 for FM JApan 76.0-90.0Mhz
* Set Bit 3 for FM Wide 76.0-108Mhz
* Currently we send this after receiving capabilities
* on 0x02 above
*/
/* case 09 ToAccessory GetTunerFreq 2 bytes */
/* case 0A ToIpod RetTunerFreq 7 bytes */
case 0x0A:
{
/* Returns Frequency set and RSSI Power Levels
* These are sent as is to rmt_tuner_freq() in
* ../firmware/drivers/tuner/ipod_remote_tuner.c */
rmt_tuner_freq(len, inbuffer);
break;
}
/* case 0B ToAccessory SetTunerFreq 6 bytes */
/* case 0C ToAccessory GetTunerMode 2 bytes */
/* case 0D ToIpod RetTunerMode 3 bytes
* Returns Tuner Mode Status in 8 bits as follows
* Bit 1:0 - FM Tuner Resolution
* Bit 2 Tuner is seeking up or down
* Bit 3 Tuner is seeking with RSSI min theshold enabled
* Bit 4 Force Mono Mode (1) or allow stereo (0)
* Bit 5 Stereo Blend enabled. Valid only if Bit 4 is 0
* Bit 6 FM Tuner Deemphasis 50uS (1) or 75uS (0)
* Bit 7 Reserved 0
*/
/* case 0E ToAccessory SetTunerMode 3 bytes
* See 0x0D for Bit Descriptions
* Bits set by Cmd 02
*/
/* case 0F ToAccessory GetTunerSeekRssi 2 bytes */
/* case 10 ToIpod RetTunerSeekRssi 3 bytes
* Returns RSSI Value for seek operations
* value is 0 (min) - 255 (max)
*/
/* case 11 ToAccessory SetTunerSeekRssi 3 bytes */
/* case 12 ToAccessory TunerSeekStart 3 bytes */
/* case 13 ToIpod TunerSeekDone 7 bytes */
case 0x13:
{
rmt_tuner_freq(len, inbuffer);
break;
}
/* case 14 ToAccessory GetTunerStatus 2 bytes */
/* case 15 ToIpod RetTunerStatus 3 bytes */
/* case 16 ToAccessory GetStatusNotifyMask 2 bytes */
/* case 17 ToIpod RetStatusNotifyMask 3 bytes */
/* case 18 ToAccessory SetStatusNotifyMask 3 bytes
* This is set by Cmd 02
*/
/* case 19 ToIpod StatusChangeNotify 3 bytes */
case 0x19:
{
/* Returns StatusChangeNotify bits to ipod.
* Bit 0 set for RDS/RBDS data ready
* Bit 1 set for Tuner RSSI level change
* Bit 2 for Stereo Indicator changed
* If any of these are set we will request the data
* need to look at using these
*/
break;
}
/* case 1A ToAccessory GetRdsReadyStatus 2 bytes */
/* case 1B ToIpod RetRdsReadyStatus 6 bytes */
case 0x1B:
{
break;
}
/* case 1C ToAccessory GetRdsData 3 bytes */
/* case 1D ToIpod RetRdsData NN bytes */
case 0x1D:
{
rmt_tuner_rds_data(len, inbuffer);
break;
}
/* case 1E ToAccessory GetRdsNotifyMask 2 bytes*/
/* case 1F ToIpod RetRdsNotifyMask 6 Bytes*/
case 0x1F:
{
break;
}
/* case 20 ToAccessory SetRdsNotifyMask 6 bytes */
/* case 21 ToIpod RdsReadyNotify NN bytes */
case 0x21:
{
rmt_tuner_rds_data(len, inbuffer);
break;
}
/* case 22 Reserved */
/* case 23 Reserved */
/* case 24 Reserved */
/* case 25 ToAccessory GetHDProgramServiceCount 0 bytes */
/* case 26 ToIpod RetHDProgramServiceCount 1 bytes */
case 0x26:
{
break;
}
/* case 27 ToAccessory GetHDProgramService 0 bytes */
/* case 28 ToIpod RetHDProgramService 1 bytes */
case 0x28:
{
break;
}
/* case 29 ToAccessory SetHDProgramService 1 bytes */
/* case 2A ToAccessory GetHDDataReadyStatus 0 bytes */
/* case 2B ToIpod RetHDDataReadyStatus 4 bytes */
case 0x2B:
{
break;
}
/* case 2C ToAccessory GetHDData 1 bytes */
/* case 2D ToIpod RetHDData NN bytes */
case 0x2D:
{
break;
}
/* case 2E ToAccessory GetHDDataNotifyMask 0 bytes */
/* case 2F ToIpod RetHDDataNotifyMask 4 bytes */
case 0x2F:
{
break;
}
/* case 30 ToAccessory SetHDDataNotifyMask 4 bytes */
/* case 31 ToIpod HDDataReadyNotify NN bytes */
case 0x31:
{
break;
}
/* The default response is IAP_ACK_BAD_PARAM */
default:
{
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode07 Command");
#endif
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
break;
}
}
}

View file

@ -97,16 +97,19 @@ static void rmt_tuner_sleep(int state)
/* tuner HW on */
const unsigned char data[] = {0x07, 0x05, 0x01};
iap_send_pkt(data, sizeof(data));
/* set rds on */
const unsigned char data3[] = {0x07, 0x20, 0x40, 0x00, 0x00, 0x10 };
iap_send_pkt(data3, sizeof(data3));
/* boost gain */
const unsigned char data1[] = {0x07, 0x24, 0x06 };
iap_send_pkt(data1, sizeof(data1));
/* tuner mode */
const unsigned char data4[] = {0x07, 0x0E, 0x00 };
iap_send_pkt(data4, sizeof(data3));
/* set volume */
unsigned char data2[] = {0x03, 0x09, 0x04, 0x00, 0x00 };
data2[4] = (char)((global_settings.volume+58) * 4);
iap_send_pkt(data2, sizeof(data2));
/* set rds on */
const unsigned char data3[] = {0x07, 0x20, 0x40, 0x00, 0x00, 0x10 };
iap_send_pkt(data3, sizeof(data3));
}
else
{
@ -122,7 +125,7 @@ static void rmt_tuner_sleep(int state)
}
}
static void rmt_tuner_scan(int param)
void rmt_tuner_scan(int param)
{
const unsigned char data[] = {0x07, 0x11, 0x08}; /* RSSI level */
unsigned char updown = 0x00;
@ -148,13 +151,23 @@ static void rmt_tuner_scan(int param)
static void rmt_tuner_mute(int value)
{
/* mute flag off (play) */
unsigned char data[] = {0x03, 0x09, 0x03, 0x01};
/* The Apple Tuner does NOT appear to support muting. The Apple
* firmware turns the power off when pressing pause on the iPod
* or on the Tuner Remote.
*/
if (value)
{
/* mute flag on (pause) */
data[3] = 0x02;
unsigned char data[] = {0x03, 0x09, 0x03, 0x02};
iap_send_pkt(data, sizeof(data));
rmt_tuner_sleep(1);
}
else
{
unsigned char data[] = {0x03, 0x09, 0x03, 0x01};
iap_send_pkt(data, sizeof(data));
rmt_tuner_sleep(0);
}
iap_send_pkt(data, sizeof(data));
}
static void rmt_tuner_region(int region)
@ -163,13 +176,20 @@ static void rmt_tuner_region(int region)
{
const struct fm_region_data *rd = &fm_region_data[region];
unsigned char data[] = {0x07, 0x08, 0x00};
/* Apple MFi Accessory Firmware Spec R46 now lists
* the following bands
* ID00 AM 520-1710Khz Not Supported
* ID02 Japan 76-90Mkz 100Khz 50/75uS
* ID01 87.5-108Mhz US 200Khz 75uS, EU 100Kz 50uS
* ID03 76-108Mhz Wideband. Not Supported
*/
if (rd->freq_min == 76000000)
{
data[2] = 0x02; /* japan band */
}
else
{
data[2] = 0x01; /* us eur band */
data[2] = 0x01; /* us/europe band */
}
iap_send_pkt(data, sizeof(data));
sleep(HZ/100);
@ -225,11 +245,13 @@ static void set_deemphasis(int deemphasis)
case 1:
{
tuner_param |= 0x40;
/* 50uS */
break;
}
default:
{
tuner_param |= 0x00;
/* 75uS */
break;
}
}
@ -257,7 +279,6 @@ static bool reply_timeout(void)
sleep(HZ/50);
do
{
iap_handlepkt();
sleep(HZ/50);
timeout++;
}
@ -360,6 +381,27 @@ int ipod_rmt_tuner_set(int setting, int value)
case RADIO_REGION:
{
/* The latest MFi Accessory Firmware Document I have lists the
* following regions
* US 87.5-108Mhz 200Khz 75uS
* US/EU 87.5-108Mhz 100Khz 75/50uS
* JP 76.0-90Mhz 100Mhz 50/75uS
*
* with the following bands
* 0x00 AM WordlWide 520-1710Khz
* 0x01 FM EU 87.5-108.0Mhz
* 0x02 FM JP 76.0-90.0Mhz
* 0x03 FM Wide 76.0-108.0Mhz
*
*
* A 7G Classic with the latest Apple Firmware returns the following
* regions with the settings listed
* Americas 87.5-108 200Khz 75uS
* Asia 87.5-108 100Khz 75uS
* Australia 87.5-108 200Khz 75uS
* Europe 87.5-108 100Khz 75uS
* Japan 76.0-90. 100Kz 75uS
*/
const struct fm_region_data *rd = &fm_region_data[value];
int band = (rd->freq_min == 76000000) ? 2 : 0;
int spacing = (100000 / rd->freq_step);