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 "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 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;
}
/* FM transmitter sends this: FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
/* 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};
iap_send_pkt(data, sizeof(data));
unsigned char data2[] = {0x00, 0x02, 0x00, 0x05};
iap_send_pkt(data2, sizeof(data2));
break;
}
case 0x01:
{
if(serbuf[3] == 0x05)
/* 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;
}
/* 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));
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;
}
/* 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;
}

View file

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

View file

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

View file

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

View file

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

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
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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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