1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Laurent Gautier 2009-12-01 17:54:40 +00:00
parent 63d79148fd
commit 0260852771
16 changed files with 881 additions and 54 deletions

View file

@ -37,11 +37,12 @@
#include "settings.h" #include "settings.h"
#include "metadata.h" #include "metadata.h"
#include "wps.h" #include "wps.h"
#include "sound.h"
#include "action.h" #include "action.h"
#include "powermgmt.h"
#define RX_BUFLEN 260 #include "tuner.h"
#define TX_BUFLEN 128 #include "ipod_remote_tuner.h"
static volatile int iap_pollspeed = 0; static volatile int iap_pollspeed = 0;
static volatile bool iap_remotetick = true; static volatile bool iap_remotetick = true;
@ -115,7 +116,7 @@ void iap_bitrate_set(int ratenum)
checksum (length+mode+parameters+checksum == 0) 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; int i, chksum;
@ -193,14 +194,14 @@ void iap_periodic(void)
time_elapsed += wps_get_ff_rewind_count(); 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 info has changed, don't flag it right away */
if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2) if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
{ {
/* track info has changed */ /* track info has changed */
iap_changedctr = 0; iap_changedctr = 0;
data[3] = 0x01; // 0x02 has same effect? data[3] = 0x01; /* 0x02 has same effect? */
iap_updateflag = true; iap_updateflag = true;
} }
@ -211,6 +212,13 @@ void iap_periodic(void)
iap_send_pkt(data, sizeof(data)); 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) void iap_handlepkt(void)
{ {
@ -230,57 +238,134 @@ void iap_handlepkt(void)
{ {
switch (serbuf[2]) switch (serbuf[2])
{ {
case 0x24:
{
/* ipod video send this */
unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,0x01};
iap_send_pkt(data, sizeof(data));
break;
}
case 0x18:
{
/* ciphered authentication command */
/* Isn't used since we don't send the 0x00 0x17 command */
break;
}
case 0x15:
{
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)
{
/* get tuner capacities */
unsigned char data3[] = {0x07, 0x01};
iap_send_pkt(data3, sizeof(data3));
}
iap_set_remote_volume();
break;
}
case 0x13:
{
unsigned char data[] = {0x00, 0x02, 0x00, 0x13};
iap_send_pkt(data, sizeof(data));
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;
}
/* 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 */ /* get model info */
case 0x0D: 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, unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00}; 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
iap_send_pkt(data, sizeof(data)); iap_send_pkt(data, sizeof(data));
break; break;
} }
/* No idea ??? */
case 0x0F: /* Ipod FM remote sends this: FF 55 02 00 09 F5 */
case 0x09:
{ {
unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05}; /* ipod5G firmware version */
unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01 };
iap_send_pkt(data, sizeof(data)); iap_send_pkt(data, sizeof(data));
break; break;
} }
/* FM transmitter sends this: FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
/* 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};
iap_send_pkt(data, sizeof(data));
unsigned char data2[] = {0x00, 0x02, 0x00, 0x05};
iap_send_pkt(data2, sizeof(data2));
break;
}
case 0x01: case 0x01:
{ {
/* FM transmitter sends this: */
/* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
if(serbuf[3] == 0x05) if(serbuf[3] == 0x05)
{ {
sleep(HZ/3); sleep(HZ/3);
unsigned char data[] = {0x05, 0x02}; unsigned char data[] = {0x05, 0x02};
iap_send_pkt(data, sizeof(data)); iap_send_pkt(data, sizeof(data));
} }
break; /* FM remote sends this: */
} /* FF 55 03 00 01 02 FA (1st thing sent) */
/* FM transmitter sends this: FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (???)*/ else if(serbuf[3] == 0x02)
case 0x13:
{ {
unsigned char data[] = {0x00, 0x02, 0x00, 0x13}; /* useful only for apple firmware */
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));
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};
iap_send_pkt(data, sizeof(data));
unsigned char data2[] = {0x00, 0x02, 0x00, 0x05};
iap_send_pkt(data2, sizeof(data2));
break; break;
} }
/* default response is with cmd ok packet */ /* default response is with cmd ok packet */
default: default:
{ {
unsigned char data[] = {0x00, 0x02, 0x00, 0x00}; 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)); iap_send_pkt(data, sizeof(data));
break; break;
} }
@ -395,6 +480,30 @@ void iap_handlepkt(void)
iap_send_pkt(data, sizeof(data)); iap_send_pkt(data, sizeof(data));
break; 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 */ /* 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; serbuf[0] = 0;
} }

View file

@ -193,6 +193,26 @@ const struct button_mapping button_context_recscreen[] = {
}; /* button_context_recscreen */ }; /* button_context_recscreen */
#endif #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 #ifdef USB_ENABLE_HID
static const struct button_mapping button_context_usb_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 }, { 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) LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_wps */ }; /* 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 ) static const struct button_mapping* get_context_mapping_remote( int context )
{ {
context ^= CONTEXT_REMOTE; context ^= CONTEXT_REMOTE;
@ -319,6 +359,14 @@ static const struct button_mapping* get_context_mapping_remote( int context )
{ {
case CONTEXT_WPS: case CONTEXT_WPS:
return remote_button_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: default:
return remote_button_context_standard; return remote_button_context_standard;
} }
@ -371,6 +419,10 @@ const struct button_mapping* get_context_mapping(int context)
case CONTEXT_RECSCREEN: case CONTEXT_RECSCREEN:
return button_context_recscreen; return button_context_recscreen;
#endif #endif
#if CONFIG_TUNER
case CONTEXT_FM:
return button_context_radio;
#endif
#ifdef USB_ENABLE_HID #ifdef USB_ENABLE_HID
case CONTEXT_USB_HID: case CONTEXT_USB_HID:
return button_context_usb_hid; return button_context_usb_hid;

View file

@ -49,6 +49,9 @@
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
#include "recording.h" #include "recording.h"
#endif #endif
#ifdef IPOD_ACCESSORY_PROTOCOL
#include "iap.h"
#endif
#include "talk.h" #include "talk.h"
#include "tuner.h" #include "tuner.h"
#include "power.h" #include "power.h"
@ -114,6 +117,15 @@
#define FM_MODE #define FM_MODE
#define FM_EXIT #define FM_EXIT
#define FM_PLAY #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 #endif
#define RADIO_SCAN_MODE 0 #define RADIO_SCAN_MODE 0
@ -586,7 +598,6 @@ int radio_screen(void)
end_search(); end_search();
talk = true; talk = true;
} }
trigger_cpu_boost(); trigger_cpu_boost();
} }
@ -873,6 +884,33 @@ int radio_screen(void)
default: default:
default_event_handler(button); 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; break;
} /*switch(button)*/ } /*switch(button)*/
@ -963,6 +1001,17 @@ int radio_screen(void)
str(LANG_RADIO_SCAN_MODE)); str(LANG_RADIO_SCAN_MODE));
FOR_NB_SCREENS(i) FOR_NB_SCREENS(i)
screens[i].puts_scroll(0, top_of_screen + 3, buf); 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 CONFIG_CODEC != SWCODEC
if(audio_status() == AUDIO_STATUS_RECORD) if(audio_status() == AUDIO_STATUS_RECORD)
@ -1498,6 +1547,7 @@ static int scan_presets(void *viewports)
curr_freq = fmr->freq_min; curr_freq = fmr->freq_min;
num_presets = 0; num_presets = 0;
memset(presets, 0, sizeof(presets)); memset(presets, 0, sizeof(presets));
tuner_set(RADIO_MUTE, 1); tuner_set(RADIO_MUTE, 1);
while(curr_freq <= fmr->freq_max) while(curr_freq <= fmr->freq_max)
@ -1563,7 +1613,6 @@ static int fm_recording_screen(void)
/* switch recording source to FMRADIO for the duration */ /* switch recording source to FMRADIO for the duration */
int rec_source = global_settings.rec_source; int rec_source = global_settings.rec_source;
global_settings.rec_source = AUDIO_SRC_FMRADIO; global_settings.rec_source = AUDIO_SRC_FMRADIO;
ret = recording_screen(true); ret = recording_screen(true);
/* safe to reset as changing sources is prohibited here */ /* safe to reset as changing sources is prohibited here */

View file

@ -61,6 +61,7 @@ kjer Kjell Ericson
kkurbjun Karl Kurbjun kkurbjun Karl Kurbjun
kugel Thomas Martitz kugel Thomas Martitz
lamed Shachar Liberman lamed Shachar Liberman
laurent Gautier
learman Magnus Holmgren learman Magnus Holmgren
len0x Anton Oleynikov len0x Anton Oleynikov
lenzone10 Alessio Lenzi lenzone10 Alessio Lenzi

View file

@ -201,6 +201,7 @@ drivers/rtc/rtc_d2.c
#endif /* (CONFIG_RTC == RTC_) */ #endif /* (CONFIG_RTC == RTC_) */
#endif /* SIMULATOR */ #endif /* SIMULATOR */
#ifndef BOOTLOADER
/* Tuner */ /* Tuner */
#if CONFIG_TUNER #if CONFIG_TUNER
tuner.c tuner.c
@ -221,8 +222,12 @@ drivers/tuner/tea5767.c
#if (CONFIG_TUNER & SI4700) #if (CONFIG_TUNER & SI4700)
drivers/tuner/si4700.c drivers/tuner/si4700.c
#endif /* (CONFIG_TUNER & SI4700) */ #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 /*SIMULATOR */
#endif /* CONFIG_TUNER */ #endif /* CONFIG_TUNER */
#endif /* BOOTLOADER */
/* Sound */ /* Sound */
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC

View file

@ -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;
}

View file

@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ 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 the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44) #define HW_SAMPR_CAPS (SAMPR_CAP_44)
@ -144,6 +144,10 @@
#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ #define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif #endif
/* Define Apple remote tuner */
#define CONFIG_TUNER IPOD_REMOTE_TUNER
#define HAVE_RDS_CAP
/* Define this if you have a PortalPlayer PP5020 */ /* Define this if you have a PortalPlayer PP5020 */
#define CONFIG_CPU PP5020 #define CONFIG_CPU PP5020

View file

@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ 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 the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44) #define HW_SAMPR_CAPS (SAMPR_CAP_44)
@ -121,6 +121,10 @@
/* define this if the unit can be powered or charged via USB */ /* define this if the unit can be powered or charged via USB */
#define HAVE_USB_POWER #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 this if you have a PortalPlayer PP5020 */
#define CONFIG_CPU PP5020 #define CONFIG_CPU PP5020

View file

@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ 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 the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44) #define HW_SAMPR_CAPS (SAMPR_CAP_44)
@ -134,6 +134,10 @@
#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ #define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif #endif
/* Define Apple remote tuner */
#define CONFIG_TUNER IPOD_REMOTE_TUNER
#define HAVE_RDS_CAP
/* Define this if you have a PortalPlayer PP5022 */ /* Define this if you have a PortalPlayer PP5022 */
#define CONFIG_CPU PP5022 #define CONFIG_CPU PP5022

View file

@ -18,7 +18,7 @@
/* Define bitmask of input sources - recordable bitmask can be defined /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ 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 the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ #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 */ #define CURRENT_RECORD 35 /* FIXME: this needs adjusting */
#endif #endif
/* Define Apple remote tuner */
#define CONFIG_TUNER IPOD_REMOTE_TUNER
#define HAVE_RDS_CAP
/* Define this if you have a PortalPlayer PP5022 */ /* Define this if you have a PortalPlayer PP5022 */
#define CONFIG_CPU PP5022 #define CONFIG_CPU PP5022

View file

@ -40,6 +40,7 @@
#define SI4700 0x08 /* Silicon Labs */ #define SI4700 0x08 /* Silicon Labs */
#define TEA5760 0x10 /* Philips */ #define TEA5760 0x10 /* Philips */
#define LV240000 0x20 /* Sanyo */ #define LV240000 0x20 /* Sanyo */
#define IPOD_REMOTE_TUNER 0x40 /* Apple */
/* CONFIG_CODEC */ /* CONFIG_CODEC */
#define MAS3587F 3587 #define MAS3587F 3587

View file

@ -20,6 +20,9 @@
#ifndef __IAP_H__ #ifndef __IAP_H__
#define __IAP_H__ #define __IAP_H__
#define RX_BUFLEN 260
#define TX_BUFLEN 128
extern int iap_getc(unsigned char x); extern int iap_getc(unsigned char x);
extern void iap_write_pkt(unsigned char data, int len); extern void iap_write_pkt(unsigned char data, int len);
extern void iap_setup(int ratenum); 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_periodic(void);
extern void iap_handlepkt(void); extern void iap_handlepkt(void);
extern void iap_track_changed(void *ignored); 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 #endif

View file

@ -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_ */

View file

@ -44,11 +44,25 @@ enum
RADIO_PRESENT = 0, RADIO_PRESENT = 0,
RADIO_TUNED, RADIO_TUNED,
RADIO_STEREO, RADIO_STEREO,
/* RADIO_EVENT is an event that requests a screen update */
RADIO_EVENT,
/* Put new general-purpose readback values above this line */ /* Put new general-purpose readback values above this line */
__RADIO_GET_STANDARD_LAST __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 **/ /** Tuner regions **/
/* Basic region information */ /* Basic region information */
@ -114,6 +128,11 @@ extern int (*tuner_get)(int setting);
#include "si4700.h" #include "si4700.h"
#endif #endif
/* Apple remote tuner */
#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
#include "ipod_remote_tuner.h"
#endif
#endif /* SIMULATOR */ #endif /* SIMULATOR */
/* Additional messages that get enumerated after tuner driver headers */ /* Additional messages that get enumerated after tuner driver headers */

View file

@ -104,11 +104,17 @@ void audio_input_mux(int source, unsigned flags)
if (!recording) if (!recording)
audiohw_set_recvol(0x17, 0x17, AUDIO_GAIN_LINEIN); audiohw_set_recvol(0x17, 0x17, AUDIO_GAIN_LINEIN);
#endif #endif
if (source == last_source && recording == last_recording) if (source == last_source && recording == last_recording)
break; break;
last_recording = recording; 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) if (recording)
{ {
audiohw_set_monitor(false); /* disable bypass mode */ audiohw_set_monitor(false); /* disable bypass mode */
@ -119,6 +125,7 @@ void audio_input_mux(int source, unsigned flags)
audiohw_disable_recording(); audiohw_disable_recording();
audiohw_set_monitor(true); /* enable bypass mode */ audiohw_set_monitor(true); /* enable bypass mode */
} }
#endif
break; break;
#endif #endif
} /* end switch */ } /* end switch */

View file

@ -89,9 +89,23 @@ const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS] =
}; };
#endif /* (CONFIG_TUNER & SI4700) */ #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 #ifdef CONFIG_TUNER_MULTI
int (*tuner_set)(int setting, int value); int (*tuner_set)(int setting, int value);
int (*tuner_get)(int setting); int (*tuner_get)(int setting);
#define TUNER_TYPE_CASE(type, set, get, ...) \ #define TUNER_TYPE_CASE(type, set, get, ...) \
case type: \ case type: \
tuner_set = set; \ tuner_set = set; \
@ -139,5 +153,4 @@ void tuner_init(void)
#endif #endif
} }
} }
#endif /* SIMULATOR */ #endif /* SIMULATOR */