summaryrefslogtreecommitdiffstats
path: root/apps
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 /apps
parent63d79148fd07aebd2b425c0414b7b9622b312399 (diff)
downloadrockbox-0260852771aef7a6e9045684f0c3d0d7e01909f7.tar.gz
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
Diffstat (limited to 'apps')
-rw-r--r--apps/iap.c208
-rw-r--r--apps/keymaps/keymap-ipod.c52
-rw-r--r--apps/recorder/radio.c63
3 files changed, 282 insertions, 41 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