diff options
author | Laurent Gautier <creposucre@rockbox.org> | 2009-12-01 17:54:40 +0000 |
---|---|---|
committer | Laurent Gautier <creposucre@rockbox.org> | 2009-12-01 17:54:40 +0000 |
commit | 0260852771aef7a6e9045684f0c3d0d7e01909f7 (patch) | |
tree | 6fa4b42230d1289857f84405defad94cc2de86b8 /apps | |
parent | 63d79148fd07aebd2b425c0414b7b9622b312399 (diff) | |
download | rockbox-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.c | 208 | ||||
-rw-r--r-- | apps/keymaps/keymap-ipod.c | 52 | ||||
-rw-r--r-- | apps/recorder/radio.c | 63 |
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 |