summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Gautier <creposucre@rockbox.org>2009-12-01 17:54:40 +0000
committerLaurent Gautier <creposucre@rockbox.org>2009-12-01 17:54:40 +0000
commit0260852771aef7a6e9045684f0c3d0d7e01909f7 (patch)
tree6fa4b42230d1289857f84405defad94cc2de86b8
parent63d79148fd07aebd2b425c0414b7b9622b312399 (diff)
downloadrockbox-0260852771aef7a6e9045684f0c3d0d7e01909f7.tar.gz
rockbox-0260852771aef7a6e9045684f0c3d0d7e01909f7.tar.bz2
rockbox-0260852771aef7a6e9045684f0c3d0d7e01909f7.zip
Add support for the ipod FM remote to the 4G, Color, 5G, nano 1G with RDS
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23805 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/iap.c208
-rw-r--r--apps/keymaps/keymap-ipod.c52
-rw-r--r--apps/recorder/radio.c63
-rw-r--r--docs/COMMITTERS1
-rw-r--r--firmware/SOURCES5
-rw-r--r--firmware/drivers/tuner/ipod_remote_tuner.c444
-rw-r--r--firmware/export/config-ipod4g.h6
-rw-r--r--firmware/export/config-ipodcolor.h6
-rw-r--r--firmware/export/config-ipodnano.h6
-rw-r--r--firmware/export/config-ipodvideo.h6
-rw-r--r--firmware/export/config.h1
-rw-r--r--firmware/export/iap.h5
-rw-r--r--firmware/export/ipod_remote_tuner.h75
-rw-r--r--firmware/export/tuner.h21
-rw-r--r--firmware/target/arm/audio-pp.c7
-rw-r--r--firmware/tuner.c19
16 files changed, 876 insertions, 49 deletions
diff --git a/apps/iap.c b/apps/iap.c
index 29cee22ab5..741ff9fb0c 100644
--- a/apps/iap.c
+++ b/apps/iap.c
@@ -37,11 +37,12 @@
#include "settings.h"
#include "metadata.h"
#include "wps.h"
-
+#include "sound.h"
#include "action.h"
+#include "powermgmt.h"
-#define RX_BUFLEN 260
-#define TX_BUFLEN 128
+#include "tuner.h"
+#include "ipod_remote_tuner.h"
static volatile int iap_pollspeed = 0;
static volatile bool iap_remotetick = true;
@@ -115,7 +116,7 @@ void iap_bitrate_set(int ratenum)
checksum (length+mode+parameters+checksum == 0)
*/
-static void iap_send_pkt(const unsigned char * data, int len)
+void iap_send_pkt(const unsigned char * data, int len)
{
int i, chksum;
@@ -192,15 +193,15 @@ void iap_periodic(void)
unsigned long time_elapsed = audio_current_track()->elapsed;
time_elapsed += wps_get_ff_rewind_count();
-
- data[3] = 0x04; // playing
+
+ data[3] = 0x04; /* playing */
/* If info has changed, don't flag it right away */
if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
- {
+ {
/* track info has changed */
iap_changedctr = 0;
- data[3] = 0x01; // 0x02 has same effect?
+ data[3] = 0x01; /* 0x02 has same effect? */
iap_updateflag = true;
}
@@ -211,12 +212,19 @@ void iap_periodic(void)
iap_send_pkt(data, sizeof(data));
}
+void iap_set_remote_volume(void)
+{
+ unsigned char data[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
+ data[4] = (char)((global_settings.volume+58) * 4);
+ iap_send_pkt(data, sizeof(data));
+}
+
void iap_handlepkt(void)
{
-
+
if(!iap_setupflag) return;
if(serbuf[0] == 0) return;
-
+
/* if we are waiting for a remote button to go out,
delay the handling of the new packet */
if(!iap_remotetick)
@@ -224,63 +232,140 @@ void iap_handlepkt(void)
queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
return;
}
-
+
/* Handle Mode 0 */
if (serbuf[1] == 0x00)
{
switch (serbuf[2])
{
- /* get model info */
- case 0x0D:
+ case 0x24:
{
- unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
- 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
+ /* ipod video send this */
+ unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,0x01};
iap_send_pkt(data, sizeof(data));
break;
}
- /* No idea ??? */
- case 0x0F:
+
+ case 0x18:
{
- unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
- iap_send_pkt(data, sizeof(data));
+ /* ciphered authentication command */
+ /* Isn't used since we don't send the 0x00 0x17 command */
break;
}
- /* FM transmitter sends this: FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
- case 0x01:
+
+ case 0x15:
{
- if(serbuf[3] == 0x05)
+ unsigned char data0[] = {0x00, 0x16, 0x00};
+ iap_send_pkt(data0, sizeof(data0));
+ unsigned char data1[] = {0x00, 0x27, 0x00};
+ iap_send_pkt(data1, sizeof(data1));
+ /* authentication ack, mandatory to enable some hardware */
+ unsigned char data2[] = {0x00, 0x19, 0x00};
+ iap_send_pkt(data2, sizeof(data2));
+ if (radio_present == 1)
{
- sleep(HZ/3);
- unsigned char data[] = {0x05, 0x02};
- iap_send_pkt(data, sizeof(data));
+ /* get tuner capacities */
+ unsigned char data3[] = {0x07, 0x01};
+ iap_send_pkt(data3, sizeof(data3));
}
+ iap_set_remote_volume();
break;
}
- /* FM transmitter sends this: FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (???)*/
+
case 0x13:
{
unsigned char data[] = {0x00, 0x02, 0x00, 0x13};
iap_send_pkt(data, sizeof(data));
- unsigned char data2[] = {0x00, 0x27, 0x00};
- iap_send_pkt(data2, sizeof(data2));
- unsigned char data3[] = {0x05, 0x02};
- iap_send_pkt(data3, sizeof(data3));
+
+ if (serbuf[6] == 0x35)
+ /* FM transmitter sends this: */
+ /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
+ {
+ unsigned char data2[] = {0x00, 0x27, 0x00};
+ iap_send_pkt(data2, sizeof(data2));
+ unsigned char data3[] = {0x05, 0x02};
+ iap_send_pkt(data3, sizeof(data3));
+ }
+
+ else
+ {
+ /* ipod fm remote sends this: */
+ /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
+ if (serbuf[6] |= 0x80)
+ radio_present = 1;
+ unsigned char data4[] = {0x00, 0x14};
+ iap_send_pkt(data4, sizeof(data4));
+ }
break;
}
- /* FM transmitter sends this: FF 55 02 00 05 F9 (mode switch: AiR mode) */
+
+ /* Init */
+ case 0x0F:
+ {
+ unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
+ data[2] = serbuf[3];
+ iap_send_pkt(data, sizeof(data));
+ break;
+ }
+
+ /* get model info */
+ case 0x0D:
+ {
+ /* ipod is supposed to work only with 5G and nano 2G */
+ /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34,
+ 0x37, 0x4C, 0x4C, 0x00}; PA147LL (IPOD 5G 60 GO) */
+ unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
+ 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
+ iap_send_pkt(data, sizeof(data));
+ break;
+ }
+
+ /* Ipod FM remote sends this: FF 55 02 00 09 F5 */
+ case 0x09:
+ {
+ /* ipod5G firmware version */
+ unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01 };
+ iap_send_pkt(data, sizeof(data));
+ break;
+ }
+
+ /* FM transmitter sends this: */
+ /* FF 55 02 00 05 F9 (mode switch: AiR mode) */
case 0x05:
{
- unsigned char data[] = {0x00, 0x02, 0x06, 0x05, 0x00, 0x00, 0x0B, 0xB8, 0x28};
+ unsigned char data[] = {0x00, 0x02, 0x06,
+ 0x05, 0x00, 0x00, 0x0B, 0xB8, 0x28};
iap_send_pkt(data, sizeof(data));
unsigned char data2[] = {0x00, 0x02, 0x00, 0x05};
iap_send_pkt(data2, sizeof(data2));
break;
- }
+ }
+
+ case 0x01:
+ {
+ /* FM transmitter sends this: */
+ /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
+ if(serbuf[3] == 0x05)
+ {
+ sleep(HZ/3);
+ unsigned char data[] = {0x05, 0x02};
+ iap_send_pkt(data, sizeof(data));
+ }
+ /* FM remote sends this: */
+ /* FF 55 03 00 01 02 FA (1st thing sent) */
+ else if(serbuf[3] == 0x02)
+ {
+ /* useful only for apple firmware */
+ }
+ break;
+ }
+
/* default response is with cmd ok packet */
default:
{
unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
- data[3] = serbuf[2]; //respond with cmd
+ data[3] = serbuf[2]; /* respond with cmd */
iap_send_pkt(data, sizeof(data));
break;
}
@@ -395,6 +480,30 @@ void iap_handlepkt(void)
iap_send_pkt(data, sizeof(data));
break;
}
+
+ case 0x08:
+ {
+ /* ACK */
+ unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
+ iap_send_pkt(data, sizeof(data));
+ break;
+ }
+
+ case 0x0C:
+ {
+ /* request ipod volume */
+ if (serbuf[3] == 0x04)
+ {
+ iap_set_remote_volume();
+ }
+ break;
+ }
+ /* get volume from accessory */
+ case 0x0E:
+ if (serbuf[3] == 0x04)
+ global_settings.volume = (-58)+((int)serbuf[5]+1)/4;
+ sound_set_volume(global_settings.volume);
+ break;
}
}
/* Handle Mode 4 */
@@ -712,6 +821,37 @@ void iap_handlepkt(void)
}
}
}
+ /* Handle Mode 7 */
+ else if (serbuf[1] == 0x07)
+ {
+ switch(serbuf[2])
+ {
+ /* tuner capabilities */
+ case 0x02:
+ {
+ /* do nothing */
+
+ unsigned char data[] = {0x00, 0x27, 0x00};
+ iap_send_pkt(data, sizeof(data));
+ break;
+ }
+ /* actual tuner frequency */
+ case 0x0A:
+ /* fall through */
+ /* tuner frequency from scan */
+ case 0x13:
+ {
+ rmt_tuner_freq();
+ break;
+ }
+ /* RDS station name 0x21 1E 00 + ASCII text*/
+ case 0x21:
+ {
+ rmt_tuner_rds_data();
+ break;
+ }
+ }
+ }
serbuf[0] = 0;
}
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c
index 1a8f7a25ff..9b3323bb82 100644
--- a/apps/keymaps/keymap-ipod.c
+++ b/apps/keymaps/keymap-ipod.c
@@ -193,6 +193,26 @@ const struct button_mapping button_context_recscreen[] = {
}; /* button_context_recscreen */
#endif
+/** FM Radio Screen **/
+ #if CONFIG_TUNER
+ static const struct button_mapping button_context_radio[] = {
+ { ACTION_FM_MENU, BUTTON_SELECT | BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_FM_STOP, BUTTON_PLAY | BUTTON_REPEAT, BUTTON_PLAY },
+ { ACTION_FM_MODE, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_FM_EXIT, BUTTON_MENU | BUTTON_REL, BUTTON_NONE },
+ { ACTION_FM_PLAY, BUTTON_PLAY | BUTTON_REL, BUTTON_PLAY },
+ { ACTION_SETTINGS_INC, BUTTON_SCROLL_FWD, BUTTON_NONE },
+ { ACTION_SETTINGS_INCREPEAT,BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_SETTINGS_DEC, BUTTON_SCROLL_BACK, BUTTON_NONE },
+ { ACTION_SETTINGS_DECREPEAT,BUTTON_SCROLL_BACK|BUTTON_REPEAT,BUTTON_NONE },
+ { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
+ { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE },
+ { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
+ }; /* button_context_radio */
+ #endif
+
#ifdef USB_ENABLE_HID
static const struct button_mapping button_context_usb_hid[] = {
{ ACTION_USB_HID_MODE_SWITCH_NEXT, BUTTON_SELECT|BUTTON_RIGHT|BUTTON_REL, BUTTON_SELECT|BUTTON_RIGHT },
@@ -311,6 +331,26 @@ static const struct button_mapping remote_button_context_wps[] = {
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_wps */
+static const struct button_mapping remote_button_context_tree[] = {
+ { ACTION_TREE_WPS, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY },
+ { ACTION_TREE_STOP, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY },
+
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* remote_button_context_tree */
+
+#if CONFIG_TUNER
+ static const struct button_mapping remote_button_context_radio[] = {
+ { ACTION_FM_STOP, BUTTON_RC_PLAY | BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_FM_PLAY, BUTTON_RC_PLAY | BUTTON_REL, BUTTON_RC_PLAY },
+ { ACTION_STD_NEXT, BUTTON_RC_RIGHT|BUTTON_REL, BUTTON_RC_RIGHT },
+ { ACTION_STD_NEXTREPEAT, BUTTON_RC_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_PREV, BUTTON_RC_LEFT|BUTTON_REL, BUTTON_RC_LEFT },
+ { ACTION_STD_PREVREPEAT, BUTTON_RC_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+
+ LAST_ITEM_IN_LIST
+ }; /* remote_button_context_radio */
+#endif
+
static const struct button_mapping* get_context_mapping_remote( int context )
{
context ^= CONTEXT_REMOTE;
@@ -319,6 +359,14 @@ static const struct button_mapping* get_context_mapping_remote( int context )
{
case CONTEXT_WPS:
return remote_button_context_wps;
+ case CONTEXT_TREE:
+ case CONTEXT_CUSTOM|CONTEXT_TREE:
+ return remote_button_context_tree;
+
+#ifdef CONFIG_TUNER
+ case CONTEXT_FM:
+ return remote_button_context_radio;
+#endif
default:
return remote_button_context_standard;
}
@@ -371,6 +419,10 @@ const struct button_mapping* get_context_mapping(int context)
case CONTEXT_RECSCREEN:
return button_context_recscreen;
#endif
+#if CONFIG_TUNER
+ case CONTEXT_FM:
+ return button_context_radio;
+#endif
#ifdef USB_ENABLE_HID
case CONTEXT_USB_HID:
return button_context_usb_hid;
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c
index b70c682922..87614aec15 100644
--- a/apps/recorder/radio.c
+++ b/apps/recorder/radio.c
@@ -49,6 +49,9 @@
#ifdef HAVE_RECORDING
#include "recording.h"
#endif
+#ifdef IPOD_ACCESSORY_PROTOCOL
+#include "iap.h"
+#endif
#include "talk.h"
#include "tuner.h"
#include "power.h"
@@ -114,6 +117,15 @@
#define FM_MODE
#define FM_EXIT
#define FM_PLAY
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+#define FM_MENU
+#define FM_STOP
+#define FM_EXIT
+#define FM_PLAY
+#define FM_MODE
+
#endif
#define RADIO_SCAN_MODE 0
@@ -586,7 +598,6 @@ int radio_screen(void)
end_search();
talk = true;
}
-
trigger_cpu_boost();
}
@@ -873,6 +884,33 @@ int radio_screen(void)
default:
default_event_handler(button);
+#ifdef HAVE_RDS_CAP
+ if (tuner_get(RADIO_EVENT))
+ update_screen = true;
+#endif
+ if (!tuner_get(RADIO_PRESENT))
+ {
+#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
+ if(audio_status() == AUDIO_STATUS_RECORD)
+ audio_stop();
+#endif
+ keep_playing = false;
+ done = true;
+ ret_val = GO_TO_ROOT;
+ if(presets_changed)
+ {
+ if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
+ {
+ if(filepreset[0] == '\0')
+ save_preset_list();
+ else
+ radio_save_presets();
+ }
+ }
+
+ /* Clear the preset list on exit. */
+ clear_preset_list();
+ }
break;
} /*switch(button)*/
@@ -895,11 +933,11 @@ int radio_screen(void)
{
screens[i].set_viewport(&vp[i]);
peak_meter_screen(&screens[i],0,
- STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
- fh);
+ STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
+ fh);
screens[i].update_rect(0,
- STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
- screens[i].getwidth(), fh);
+ STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
+ screens[i].getwidth(), fh);
screens[i].set_viewport(NULL);
}
}
@@ -963,7 +1001,18 @@ int radio_screen(void)
str(LANG_RADIO_SCAN_MODE));
FOR_NB_SCREENS(i)
screens[i].puts_scroll(0, top_of_screen + 3, buf);
+#ifndef SIMULATOR
+#ifdef HAVE_RDS_CAP
+ snprintf(buf, 128, "%s",tuner_get_rds_info(RADIO_RDS_NAME));
+ FOR_NB_SCREENS(i)
+ screens[i].puts_scroll(0, top_of_screen + 4, buf);
+ snprintf(buf, 128, "%s",tuner_get_rds_info(RADIO_RDS_TEXT));
+ FOR_NB_SCREENS(i)
+ screens[i].puts_scroll(0, top_of_screen + 5, buf);
+#endif
+#endif /* SIMULATOR */
+
#if CONFIG_CODEC != SWCODEC
if(audio_status() == AUDIO_STATUS_RECORD)
{
@@ -1498,6 +1547,7 @@ static int scan_presets(void *viewports)
curr_freq = fmr->freq_min;
num_presets = 0;
memset(presets, 0, sizeof(presets));
+
tuner_set(RADIO_MUTE, 1);
while(curr_freq <= fmr->freq_max)
@@ -1563,7 +1613,6 @@ static int fm_recording_screen(void)
/* switch recording source to FMRADIO for the duration */
int rec_source = global_settings.rec_source;
global_settings.rec_source = AUDIO_SRC_FMRADIO;
-
ret = recording_screen(true);
/* safe to reset as changing sources is prohibited here */
@@ -1649,7 +1698,7 @@ MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
static bool radio_menu(void)
{
return do_menu(&radio_settings_menu, NULL, NULL, false) ==
- MENU_ATTACHED_USB;
+ MENU_ATTACHED_USB;
}
#endif
diff --git a/docs/COMMITTERS b/docs/COMMITTERS
index 0b107e1c0a..705891a543 100644
--- a/docs/COMMITTERS
+++ b/docs/COMMITTERS
@@ -61,6 +61,7 @@ kjer Kjell Ericson
kkurbjun Karl Kurbjun
kugel Thomas Martitz
lamed Shachar Liberman
+laurent Gautier
learman Magnus Holmgren
len0x Anton Oleynikov
lenzone10 Alessio Lenzi
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 061c6323c8..1a078ada29 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -201,6 +201,7 @@ drivers/rtc/rtc_d2.c
#endif /* (CONFIG_RTC == RTC_) */
#endif /* SIMULATOR */
+#ifndef BOOTLOADER
/* Tuner */
#if CONFIG_TUNER
tuner.c
@@ -221,8 +222,12 @@ drivers/tuner/tea5767.c
#if (CONFIG_TUNER & SI4700)
drivers/tuner/si4700.c
#endif /* (CONFIG_TUNER & SI4700) */
+#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
+drivers/tuner/ipod_remote_tuner.c
+#endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */
#endif /*SIMULATOR */
#endif /* CONFIG_TUNER */
+#endif /* BOOTLOADER */
/* Sound */
#if CONFIG_CODEC != SWCODEC
diff --git a/firmware/drivers/tuner/ipod_remote_tuner.c b/firmware/drivers/tuner/ipod_remote_tuner.c
new file mode 100644
index 0000000000..07a5eeb9a6
--- /dev/null
+++ b/firmware/drivers/tuner/ipod_remote_tuner.c
@@ -0,0 +1,444 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ * tuner for the ipod fm remote and other ipod remote tuners
+ *
+ * Copyright (C) 2009 Laurent Gautier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include "kernel.h"
+#include "iap.h"
+#include "tuner.h" /* tuner abstraction interface */
+#include "adc.h"
+#include "settings.h"
+
+static bool powered = false;
+
+static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF;
+/* temp var for tests to avoid looping execution in submenus settings*/
+int mono_mode = -1, old_region = -1;
+
+int radio_present = 0;
+int tuner_frequency = 0;
+int tuner_signal_power = 0;
+int radio_tuned = 0;
+int rds_event = 0;
+
+char rds_radioname[9];
+char rds_radioinfo[70]; /* do we need more? */
+
+union FRQ {
+ unsigned long int frequency_radio;
+ char data_frequency[4];
+}Frequency;
+
+void rmt_tuner_freq(void)
+{
+ char tempdata[4];
+ tempdata[0] = serbuf[6];
+ tempdata[1] = serbuf[5];
+ tempdata[2] = serbuf[4];
+ tempdata[3] = serbuf[3];
+
+ memcpy(Frequency.data_frequency,tempdata,4);
+ tuner_frequency = (Frequency.frequency_radio*1000);
+ radio_tuned = 1;
+ rmt_tuner_signal_power(serbuf[7]);
+}
+
+void rmt_tuner_set_freq(int curr_freq)
+{
+ if (curr_freq != tuner_frequency)
+ {
+ radio_tuned = 0;
+ tuner_signal_power = 0;
+ /* clear rds name and info */
+ memset(rds_radioname,' ',sizeof(rds_radioname));
+ memset(rds_radioinfo,' ',sizeof(rds_radioinfo));
+ /* ex: 00 01 63 14 = 90.9MHz */
+ unsigned char data[] = {0x07, 0x0B, 0x00, 0x01, 0x63, 0x14};
+
+ if (curr_freq != 0)
+ {
+ curr_freq = curr_freq / 1000;
+ char tempdata[4];
+
+ Frequency.frequency_radio = curr_freq;
+ tempdata[0] = Frequency.data_frequency[3];
+ tempdata[1] = Frequency.data_frequency[2];
+ tempdata[2] = Frequency.data_frequency[1];
+ tempdata[3] = Frequency.data_frequency[0];
+
+ memcpy(data+2,tempdata,4);
+ iap_send_pkt(data, sizeof(data));
+ }
+ }
+}
+
+void rmt_tuner_signal_power(unsigned char value)
+{
+ tuner_signal_power = (int)(value);
+}
+
+void rmt_tuner_sleep(int state)
+{
+ if (state == 0)
+ {
+ /* tuner HW on */
+ unsigned char data[] = {0x07, 0x05, 0x01};
+ iap_send_pkt(data, sizeof(data));
+ /* boost gain */
+ unsigned char data1[] = {0x07, 0x24, 0x06 };
+ iap_send_pkt(data1, sizeof(data1));
+ /* set volume */
+ unsigned char data2[] = {0x03, 0x09, 0x04, 0x00, 0x77 };
+ iap_send_pkt(data2, sizeof(data2));
+ /* set rds on */
+ unsigned char data3[] = {0x07, 0x20, 0x40, 0x00, 0x00, 0x10 };
+ iap_send_pkt(data3, sizeof(data3));
+ }
+ else
+ {
+ /* unbooste gain */
+ unsigned char data[] = {0x07, 0x24, 0x00};
+ iap_send_pkt(data, sizeof(data));
+ /* set rds off */
+ unsigned char data1[] = {0x07, 0x20, 0x00, 0x00, 0x00, 0x00 };
+ iap_send_pkt(data1, sizeof(data1));
+ /* stop tuner HW */
+ unsigned char data2[] = {0x07, 0x05, 0x00};
+ iap_send_pkt(data2, sizeof(data2));
+ }
+}
+
+void rmt_tuner_scan(int param)
+{
+ unsigned char data[] = {0x07, 0x11, 0x08}; /* RSSI level */
+ unsigned char updown = 0x00;
+ radio_tuned = 0;
+ iap_send_pkt(data, sizeof(data));
+
+ if (param == 1)
+ {
+ updown = 0x07; /* scan up */
+ }
+ else if (param == -1)
+ {
+ updown = 0x08; /* scan down */
+ }
+ else if (param == 10)
+ {
+ updown = 0x01; /* scan up starting from beginning of the band */
+ }
+ unsigned char data1[] = {0x07, 0x12, updown};
+ iap_send_pkt(data1, sizeof(data1));
+}
+
+void rmt_tuner_mute(int value)
+{
+ /* mute flag off (play) */
+ unsigned char data[] = {0x03, 0x09, 0x03, 0x01};
+ if (value)
+ {
+ /* mute flag on (pause) */
+ data[3] = 0x02;
+ }
+ iap_send_pkt(data, sizeof(data));
+}
+
+void rmt_tuner_region(int region)
+{
+ if (region != old_region)
+ {
+ unsigned char data[] = {0x07, 0x08, 0x00};
+ if (region == 2)
+ {
+ data[2] = 0x02; /* japan band */
+ }
+ else
+ {
+ data[2] = 0x01; /* us eur band */
+ }
+ iap_send_pkt(data, sizeof(data));
+ sleep(HZ/100);
+ old_region = region;
+ }
+}
+
+/* set stereo/mono, deemphasis, delta freq... */
+void rmt_tuner_set_param(unsigned char tuner_param)
+{
+ if(tuner_param != old_tuner_param)
+ {
+ unsigned char data[] = {0x07, 0x0E, 0x00};
+
+ data[2] = tuner_param;
+ iap_send_pkt(data, sizeof(data));
+ old_tuner_param = tuner_param;
+ }
+}
+
+void set_deltafreq(int delta)
+{
+ tuner_param &= 0xFC;
+ switch (delta)
+ {
+ case 1:
+ {
+ /* 100KHz */
+ tuner_param |= 0x01;
+ break;
+ }
+ case 2:
+ {
+ /* 50KHz */
+ tuner_param |= 0x02;
+ break;
+ }
+
+ default:
+ {
+ /* 200KHz */
+ tuner_param |= 0x00;
+ break;
+ }
+ }
+}
+
+void set_deemphasis(int deemphasis)
+{
+ tuner_param &= 0xBF;
+ switch (deemphasis)
+ {
+ case 1:
+ {
+ tuner_param |= 0x40;
+ break;
+ }
+ default:
+ {
+ tuner_param |= 0x00;
+ break;
+ }
+ }
+}
+
+void set_mono(int value)
+{
+ tuner_param &= 0xEF;
+
+ if (value != mono_mode)
+ {
+ tuner_param |= 0x10;
+ rmt_tuner_set_param(tuner_param);
+ sleep(HZ/100);
+ mono_mode = value;
+ }
+}
+
+bool reply_timeout(void)
+{
+ int timeout = 0;
+
+ sleep(HZ/50);
+ do
+ {
+ iap_handlepkt();
+ sleep(HZ/50);
+ timeout++;
+ }
+ while((ipod_rmt_tuner_get(RADIO_TUNED) == 0) && (timeout < TIMEOUT_VALUE));
+
+ if (timeout >= TIMEOUT_VALUE)
+ return true;
+ else return false;
+}
+
+void rmt_tuner_rds_data(void)
+{
+ if (serbuf[3] == 0x1E)
+ {
+ strlcpy(rds_radioname,serbuf+5,8);
+ }
+ else if(serbuf[3] == 0x04)
+ {
+ strlcpy(rds_radioinfo,serbuf+5,(serbuf[0]-4));
+ }
+ rds_event = 1;
+}
+
+/* tuner abstraction layer: set something to the tuner */
+int ipod_rmt_tuner_set(int setting, int value)
+{
+ switch(setting)
+ {
+ case RADIO_SLEEP:
+ {
+ rmt_tuner_sleep(value);
+ sleep(HZ/2);
+ break;
+ }
+
+ case RADIO_FREQUENCY:
+ {
+ rmt_tuner_set_freq(value);
+ if (reply_timeout() == true)
+ return 0;
+ break;
+ }
+
+ case RADIO_SCAN_FREQUENCY:
+ {
+ const struct fm_region_data * const fmr =
+ &fm_region_data[global_settings.fm_region];
+
+ /* case: scan for presets, back to beginning of the band */
+ if ((radio_tuned == 1) && (value == fmr->freq_min))
+ {
+ tuner_set(RADIO_FREQUENCY,value);
+ }
+
+ /* scan through frequencies */
+ if (radio_tuned == 1)
+ {
+ /* scan down */
+ if(value < tuner_frequency)
+ rmt_tuner_scan(-1);
+ /* scan up */
+ else
+ rmt_tuner_scan(1);
+
+ if (reply_timeout() == true)
+ return 0;
+ radio_tuned = 0;
+ }
+
+ if (tuner_frequency == value)
+ {
+ radio_tuned = 1;
+ return 1;
+ }
+ else
+ {
+ radio_tuned = 0;
+ return 0;
+ }
+ }
+
+ case RADIO_MUTE:
+ {
+ /* mute flag sent to accessory */
+ /* rmt_tuner_mute(value); */
+ break;
+ }
+
+ case RADIO_REGION:
+ {
+ const struct rmt_tuner_region_data *rd =
+ &rmt_tuner_region_data[value];
+
+ rmt_tuner_region(rd->band);
+ set_deltafreq(rd->spacing);
+ set_deemphasis(rd->deemphasis);
+ rmt_tuner_set_param(tuner_param);
+ break;
+ }
+
+ case RADIO_FORCE_MONO:
+ {
+ set_mono(value);
+ break;
+ }
+
+ default:
+ return -1;
+ }
+ return 1;
+}
+
+/* tuner abstraction layer: read something from the tuner */
+int ipod_rmt_tuner_get(int setting)
+{
+ int val = -1; /* default for unsupported query */
+
+ switch(setting)
+ {
+ case RADIO_PRESENT:
+ val = radio_present;
+ if (val)
+ {
+ /* if accessory disconnected */
+ if(adc_read(ADC_ACCESSORY) >= 10)
+ {
+ radio_present = 0;
+ val = 0;
+ }
+ }
+ break;
+
+ /* radio tuned: yes no */
+ case RADIO_TUNED:
+ val = 0;
+ if (radio_tuned == 1)
+ val = 1;
+ break;
+
+ /* radio is always stereo */
+ /* we can't know when it's in mono mode, depending of signal quality */
+ /* except if it is forced in mono mode */
+ case RADIO_STEREO:
+ val = true;
+ break;
+
+ case RADIO_EVENT:
+ if (rds_event)
+ {
+ val = 1;
+ rds_event = 0;
+ }
+ break;
+ }
+ return val;
+}
+
+char* ipod_get_rds_info(int setting)
+{
+ char *text = NULL;
+
+ switch(setting)
+ {
+ case RADIO_RDS_NAME:
+ text = rds_radioname;
+ break;
+
+ case RADIO_RDS_TEXT:
+ text = rds_radioinfo;
+ break;
+ }
+ return text;
+}
+
+bool tuner_power(bool status)
+{
+ bool oldstatus = powered;
+ powered = status;
+ return oldstatus;
+}
diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h
index c00a6788c6..e6bdc35bd8 100644
--- a/firmware/export/config-ipod4g.h
+++ b/firmware/export/config-ipod4g.h
@@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
@@ -144,6 +144,10 @@
#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif
+/* Define Apple remote tuner */
+#define CONFIG_TUNER IPOD_REMOTE_TUNER
+#define HAVE_RDS_CAP
+
/* Define this if you have a PortalPlayer PP5020 */
#define CONFIG_CPU PP5020
diff --git a/firmware/export/config-ipodcolor.h b/firmware/export/config-ipodcolor.h
index 751522d47b..0f1de4fdba 100644
--- a/firmware/export/config-ipodcolor.h
+++ b/firmware/export/config-ipodcolor.h
@@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
@@ -121,6 +121,10 @@
/* define this if the unit can be powered or charged via USB */
#define HAVE_USB_POWER
+/* Define Apple remote tuner */
+#define CONFIG_TUNER IPOD_REMOTE_TUNER
+#define HAVE_RDS_CAP
+
/* Define this if you have a PortalPlayer PP5020 */
#define CONFIG_CPU PP5020
diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h
index 68ed152cda..e1ec1afa86 100644
--- a/firmware/export/config-ipodnano.h
+++ b/firmware/export/config-ipodnano.h
@@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
@@ -134,6 +134,10 @@
#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif
+/* Define Apple remote tuner */
+#define CONFIG_TUNER IPOD_REMOTE_TUNER
+#define HAVE_RDS_CAP
+
/* Define this if you have a PortalPlayer PP5022 */
#define CONFIG_CPU PP5022
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h
index f00de9cbfd..59ac6466e5 100644
--- a/firmware/export/config-ipodvideo.h
+++ b/firmware/export/config-ipodvideo.h
@@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
@@ -155,6 +155,10 @@
#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif
+/* Define Apple remote tuner */
+#define CONFIG_TUNER IPOD_REMOTE_TUNER
+#define HAVE_RDS_CAP
+
/* Define this if you have a PortalPlayer PP5022 */
#define CONFIG_CPU PP5022
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 9c7beca2bd..088cab7212 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -40,6 +40,7 @@
#define SI4700 0x08 /* Silicon Labs */
#define TEA5760 0x10 /* Philips */
#define LV240000 0x20 /* Sanyo */
+#define IPOD_REMOTE_TUNER 0x40 /* Apple */
/* CONFIG_CODEC */
#define MAS3587F 3587
diff --git a/firmware/export/iap.h b/firmware/export/iap.h
index e945d0c979..d00e5f398a 100644
--- a/firmware/export/iap.h
+++ b/firmware/export/iap.h
@@ -20,6 +20,9 @@
#ifndef __IAP_H__
#define __IAP_H__
+#define RX_BUFLEN 260
+#define TX_BUFLEN 128
+
extern int iap_getc(unsigned char x);
extern void iap_write_pkt(unsigned char data, int len);
extern void iap_setup(int ratenum);
@@ -27,5 +30,7 @@ extern void iap_bitrate_set(int ratenum);
extern void iap_periodic(void);
extern void iap_handlepkt(void);
extern void iap_track_changed(void *ignored);
+extern void iap_send_pkt(const unsigned char * data, int len);
+extern unsigned char serbuf[RX_BUFLEN];
#endif
diff --git a/firmware/export/ipod_remote_tuner.h b/firmware/export/ipod_remote_tuner.h
new file mode 100644
index 0000000000..28fcfe1e93
--- /dev/null
+++ b/firmware/export/ipod_remote_tuner.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipod_remote_tuner.h
+ * Tuner header for the ipod remote tuner and others remote tuners
+ *
+ * Copyright (C) 2009 Laurent Gautier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _IPOD_REMOTE_TUNER_H_
+#define _IPOD_REMOTE_TUNER_H_
+
+#define HAVE_RADIO_REGION
+#define TIMEOUT_VALUE 20
+
+extern int radio_present;
+extern int tuner_frequency;
+extern int tuner_signal_power;
+extern int radio_tuned;
+
+/* update tuner state: plugged or unplugged when in radio mode */
+extern void rmt_tuner_region(int region);
+extern void rmt_tuner_set_freq(int curr_freq);
+extern void rmt_tuner_freq(void);
+extern void rmt_tuner_scan(int direction);
+
+/* tuner mode state: ON or OFF */
+extern void rmt_tuner_sleep(int state);
+
+/* parameters are stereo/mono, deemphasis, delta freq... */
+extern void rmt_tuner_set_param(unsigned char tuner_param);
+
+extern void rmt_tuner_mute(int value);
+extern void rmt_tuner_signal_power(unsigned char value);
+
+extern void rmt_tuner_rds_data(void);
+
+struct rmt_tuner_region_data
+{
+ /* 0: 50us, 1: 75us */
+ unsigned char deemphasis;
+ /* 0: europe, 1: japan (BL in TEA spec)*/
+ unsigned char band;
+ /* 0: us/australia (200kHz), 1: europe/japan (100kHz), 2: (50kHz) */
+ unsigned char spacing;
+} __attribute__((packed));
+
+extern const struct rmt_tuner_region_data
+ rmt_tuner_region_data[TUNER_NUM_REGIONS];
+
+int ipod_rmt_tuner_set(int setting, int value);
+int ipod_rmt_tuner_get(int setting);
+char* ipod_get_rds_info(int setting);
+
+
+#ifndef CONFIG_TUNER_MULTI
+#define tuner_set ipod_rmt_tuner_set
+#define tuner_get ipod_rmt_tuner_get
+#define tuner_get_rds_info ipod_get_rds_info
+#endif
+
+#endif /* _IPOD_REMOTE_TUNER_H_ */
diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h
index b27e660667..63392160e5 100644
--- a/firmware/export/tuner.h
+++ b/firmware/export/tuner.h
@@ -33,7 +33,7 @@ enum
RADIO_MUTE,
RADIO_FORCE_MONO,
RADIO_SCAN_FREQUENCY,
-
+
/* Put new general-purpose settings above this line */
__RADIO_SET_STANDARD_LAST
};
@@ -44,11 +44,25 @@ enum
RADIO_PRESENT = 0,
RADIO_TUNED,
RADIO_STEREO,
+ /* RADIO_EVENT is an event that requests a screen update */
+ RADIO_EVENT,
/* Put new general-purpose readback values above this line */
__RADIO_GET_STANDARD_LAST
};
+#ifdef HAVE_RDS_CAP
+/** Readback from the tuner RDS layer **/
+enum
+{
+ RADIO_RDS_NAME,
+ RADIO_RDS_TEXT,
+
+ /* Put new general-purpose readback values above this line */
+ __RADIO_GET_RDS_INFO_STANDARD_LAST
+};
+#endif
+
/** Tuner regions **/
/* Basic region information */
@@ -114,6 +128,11 @@ extern int (*tuner_get)(int setting);
#include "si4700.h"
#endif
+/* Apple remote tuner */
+#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
+#include "ipod_remote_tuner.h"
+#endif
+
#endif /* SIMULATOR */
/* Additional messages that get enumerated after tuner driver headers */
diff --git a/firmware/target/arm/audio-pp.c b/firmware/target/arm/audio-pp.c
index 1a4952fcc3..8f22bab765 100644
--- a/firmware/target/arm/audio-pp.c
+++ b/firmware/target/arm/audio-pp.c
@@ -104,11 +104,17 @@ void audio_input_mux(int source, unsigned flags)
if (!recording)
audiohw_set_recvol(0x17, 0x17, AUDIO_GAIN_LINEIN);
#endif
+
if (source == last_source && recording == last_recording)
break;
last_recording = recording;
+#if defined(IPOD_REMOTE_TUNER)
+ /* Ipod FM tuner is in the remote connected to line-in */
+ audiohw_enable_recording(false); /* source line */
+ audiohw_set_monitor(true); /* enable bypass mode */
+#else
if (recording)
{
audiohw_set_monitor(false); /* disable bypass mode */
@@ -119,6 +125,7 @@ void audio_input_mux(int source, unsigned flags)
audiohw_disable_recording();
audiohw_set_monitor(true); /* enable bypass mode */
}
+#endif
break;
#endif
} /* end switch */
diff --git a/firmware/tuner.c b/firmware/tuner.c
index 5fd7fa1f38..cca5cf2491 100644
--- a/firmware/tuner.c
+++ b/firmware/tuner.c
@@ -89,17 +89,31 @@ const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS] =
};
#endif /* (CONFIG_TUNER & SI4700) */
+#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
+const struct rmt_tuner_region_data
+ rmt_tuner_region_data[TUNER_NUM_REGIONS] =
+{
+ [REGION_EUROPE] = { 1, 0, 1 }, /* 50uS, US/Europe band, 100kHz spacing */
+ [REGION_US_CANADA] = { 0, 0, 0 }, /* 75uS, US/Europe band, 200kHz spacing */
+ [REGION_JAPAN] = { 1, 2, 1 }, /* 50uS, Japanese band, 100kHz spacing */
+ [REGION_KOREA] = { 1, 0, 0 }, /* 50uS, US/Europe band, 200kHz spacing */
+ [REGION_ITALY] = { 1, 0, 2 }, /* 50uS, US/Europe band, 50kHz spacing */
+ [REGION_OTHER] = { 1, 0, 2 }, /* 50uS, US/Europe band, 50kHz spacing */
+};
+#endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */
+
#ifdef CONFIG_TUNER_MULTI
int (*tuner_set)(int setting, int value);
int (*tuner_get)(int setting);
-#define TUNER_TYPE_CASE(type, set, get, ...) \
+
+#define TUNER_TYPE_CASE(type, set, get, ...) \
case type: \
tuner_set = set; \
tuner_get = get; \
__VA_ARGS__; \
break;
#else
-#define TUNER_TYPE_CASE(type, set, get, ...) \
+#define TUNER_TYPE_CASE(type, set, get, ...) \
__VA_ARGS__;
#endif /* CONFIG_TUNER_MULTI */
@@ -139,5 +153,4 @@ void tuner_init(void)
#endif
}
}
-
#endif /* SIMULATOR */