diff --git a/apps/SOURCES b/apps/SOURCES index 0965c498b6..c2d591a18b 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -64,6 +64,9 @@ scrobbler.c #ifdef IPOD_ACCESSORY_PROTOCOL iap/iap-core.c iap/iap-lingo0.c +#ifdef HAVE_LINE_REC +iap/iap-lingo1.c +#endif iap/iap-lingo2.c iap/iap-lingo3.c iap/iap-lingo4.c diff --git a/apps/audio_path.c b/apps/audio_path.c index c4a4d1b277..6709d4421d 100644 --- a/apps/audio_path.c +++ b/apps/audio_path.c @@ -34,6 +34,9 @@ #if CONFIG_TUNER #include "radio.h" #endif +#if defined(IPOD_ACCESSORY_PROTOCOL) && defined(HAVE_LINE_REC) +#include "iap.h" +#endif /* Some audio sources may require a boosted CPU */ #ifdef HAVE_ADJUSTABLE_CPU_FREQ @@ -95,6 +98,13 @@ void audio_set_input_source(int source, unsigned flags) radio_start(); #endif +#if defined(IPOD_ACCESSORY_PROTOCOL) && defined(HAVE_LINE_REC) + static bool last_rec_onoff = false; + bool onoff = (source == AUDIO_SRC_LINEIN) ? true : false; + if (last_rec_onoff != onoff) + last_rec_onoff = iap_record(onoff); +#endif + /* set hardware inputs */ audio_input_mux(source, flags); } /* audio_set_source */ diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c index 9e5771ab50..e41660392c 100644 --- a/apps/iap/iap-core.c +++ b/apps/iap/iap-core.c @@ -154,7 +154,11 @@ unsigned char* iap_txnext; */ unsigned char lingo_versions[32][2] = { {1, 9}, /* General lingo, 0x00 */ - {0, 0}, /* Microphone lingo, 0x01, unsupported */ +#ifdef HAVE_LINE_REC + {1, 1}, /* Microphone lingo, 0x01 */ +#else + {0, 0}, /* Microphone lingo, 0x01, disabled */ +#endif {1, 2}, /* Simple remote lingo, 0x02 */ {1, 5}, /* Display remote lingo, 0x03 */ {1, 12}, /* Extended Interface lingo, 0x04 */ @@ -1262,6 +1266,9 @@ void iap_handlepkt(void) unsigned char mode = *(iap_rxstart+2); switch (mode) { case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break; +#ifdef HAVE_LINE_REC + case 1: iap_handlepkt_mode1(length, iap_rxstart+2); break; +#endif case 2: iap_handlepkt_mode2(length, iap_rxstart+2); break; case 3: iap_handlepkt_mode3(length, iap_rxstart+2); break; case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break; diff --git a/apps/iap/iap-lingo.h b/apps/iap/iap-lingo.h index 0c0a9e633d..888490c26a 100644 --- a/apps/iap/iap-lingo.h +++ b/apps/iap/iap-lingo.h @@ -18,6 +18,9 @@ ****************************************************************************/ void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf); +#ifdef HAVE_LINE_REC +void iap_handlepkt_mode1(const unsigned int len, const unsigned char *buf); +#endif void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf); void iap_handlepkt_mode3(const unsigned int len, const unsigned char *buf); void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf); diff --git a/apps/iap/iap-lingo1.c b/apps/iap/iap-lingo1.c new file mode 100644 index 0000000000..5702500f23 --- /dev/null +++ b/apps/iap/iap-lingo1.c @@ -0,0 +1,218 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr & Nick Robinson + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* Lingo 0x01: Microphone Lingo + */ + +#include "iap-core.h" +#include "iap-lingo.h" + +/* + * This macro is meant to be used inside an IAP mode message handler. + * It is passed the expected minimum length of the message buffer. + * If the buffer does not have the required lenght an ACK + * packet with a Bad Parameter error is generated. + */ +#define CHECKLEN(x) do { \ + if (len < (x)) { \ + cmd_ack(cmd, IAP_ACK_BAD_PARAM); \ + return; \ + }} while(0) + +/* Check for authenticated state, and return an ACK Not + * Authenticated on failure. + */ +#define CHECKAUTH do { \ + if (!DEVICE_AUTHENTICATED) { \ + cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \ + return; \ + }} while(0) + +static void cmd_ack(const unsigned char cmd, const unsigned char status) +{ + IAP_TX_INIT(0x03, 0x00); + IAP_TX_PUT(status); + IAP_TX_PUT(cmd); + + iap_send_tx(); +} + +/* returns record status */ +bool iap_record(bool onoff) +{ + if (!DEVICE_LINGO_SUPPORTED(0x01)) + return false; + + /* iPodModeChange */ + IAP_TX_INIT(0x01, 0x06); + IAP_TX_PUT(onoff ? 0x00 : 0x01); + iap_send_tx(); + + return onoff; +} + +void iap_handlepkt_mode1(const unsigned int len, const unsigned char *buf) +{ + unsigned int cmd = buf[1]; + + /* Lingo 0x04 commands are at least 4 bytes in length */ + CHECKLEN(4); + + /* Lingo 0x01 must have been negotiated */ + if (!DEVICE_LINGO_SUPPORTED(0x01)) { + cmd_ack(cmd, IAP_ACK_BAD_PARAM); + return; + } + + /* Authentication required for all commands */ + CHECKAUTH; + + switch (cmd) + { + /* BeginRecord (0x00) Deprecated + * + * Sent from the iPod to the device + */ + + /* EndRecord (0x01) Deprecated + * + * Sent from the iPod to the device + */ + + /* BeginPlayback (0x02) Deprecated + * + * Sent from the iPod to the device + */ + + /* EndPlayback (0x03) Deprecated + * + * Sent from the iPod to the device + */ + + /* ACK (0x04) + * + * The device sends an ACK response when a command + * that does not return any data has completed. + * + * Packet format (offset in buf[]: Description) + * 0x00: Lingo ID: Microphone Lingo, always 0x01 + * 0x01: Command, always 0x04 + * 0x02: The command result status + * 0x03: The ID of the command for which the + * response is being sent + * + * Returns: (none) + */ + case 0x04: +#ifdef LOGF_ENABLE + if (buf[2] != 0x00) + logf("iap: Mode1 Command ACK error: " + "0x%02x 0x%02x", buf[2], buf[3]); +#endif + break; + + /* GetDevAck (0x05) + * + * Sent from the iPod to the device + */ + + /* iPodModeChange (0x06) + * + * Sent from the iPod to the device + */ + + /* GetDevCaps (0x07) + * + * Sent from the iPod to the device + */ + + /* RetDevCaps (0x08) + * + * The microphone device returns the payload + * indicating which capabilities it supports. + * + * Packet format (offset in buf[]: Description) + * 0x00: Lingo ID: Microphone Lingo, always 0x01 + * 0x01: Command, always 0x08 + * 0x02: Device capabilities (bits 31:24) + * 0x03: Device capabilities (bits 23:16) + * 0x04: Device capabilities (bits 15:8) + * 0x05: Device capabilities (bits 7:0) + * + * Returns: + * SetDevCtrl, sets stereo line input when supported + */ + case 0x08: + CHECKLEN(6); + + if ((buf[5] & 3) == 3) { + /* SetDevCtrl, set stereo line-in */ + IAP_TX_INIT(0x01, 0x0B); + IAP_TX_PUT(0x01); + IAP_TX_PUT(0x01); + + iap_send_tx(); + } + + /* TODO?: configure recording level/limiter controls + when supported by the device */ + + break; + + /* GetDevCtrl (0x09) + * + * Sent from the iPod to the device + */ + + /* RetDevCaps (0x0A) + * + * The device returns the current control state + * for the specified control type. + * + * Packet format (offset in buf[]: Description) + * 0x00: Lingo ID: Microphone Lingo, always 0x01 + * 0x01: Command, always 0x0A + * 0x02: The control type + * 0x03: The control data + */ + case 0x0A: + switch (buf[2]) + { + case 0x01: /* stereo/mono line-in control */ + case 0x02: /* recording level control */ + case 0x03: /* recording level limiter control */ + break; + } + break; + + /* SetDevCtrl (0x0B) + * + * Sent from the iPod to the device + */ + + /* The default response is IAP_ACK_BAD_PARAM */ + default: + { +#ifdef LOGF_ENABLE + logf("iap: Unsupported Mode1 Command"); +#endif + break; + } + } +} diff --git a/apps/iap/iap-lingo2.c b/apps/iap/iap-lingo2.c index 4fbf730192..d4c2f18c2f 100644 --- a/apps/iap/iap-lingo2.c +++ b/apps/iap/iap-lingo2.c @@ -56,6 +56,7 @@ static void cmd_ack(const unsigned char cmd, const unsigned char status) void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf) { + static bool poweron_pressed = false; unsigned int cmd = buf[1]; /* We expect at least three bytes in the buffer, one for the @@ -136,6 +137,11 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf) } } + if (buf[4] & 2) /* power on */ + { + poweron_pressed = true; + } + /* Power off * Not quite sure how to react to this, but stopping playback * is a good start. @@ -152,6 +158,26 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf) REMOTE_BUTTON(BUTTON_RC_LEFT); } + /* power on released */ + if (poweron_pressed && len >= 5 && !(buf[4] & 2)) + { + poweron_pressed = false; +#ifdef HAVE_LINE_REC + /* Belkin TuneTalk microphone sends power-on press+release + * events once authentication sequence is finished, + * GetDevCaps command is ignored by the device when it is + * sent before power-on release event is received. + * XXX: It is unknown if other microphone devices are + * sending the power-on events. + */ + if (DEVICE_LINGO_SUPPORTED(0x01)) { + /* GetDevCaps */ + IAP_TX_INIT(0x01, 0x07); + iap_send_tx(); + } +#endif + } + break; } /* ACK (0x01) diff --git a/firmware/export/iap.h b/firmware/export/iap.h index 22f36083d1..2626f48355 100644 --- a/firmware/export/iap.h +++ b/firmware/export/iap.h @@ -34,5 +34,8 @@ extern void iap_periodic(void); extern void iap_handlepkt(void); extern void iap_send_pkt(const unsigned char * data, int len); const unsigned char *iap_get_serbuf(void); +#ifdef HAVE_LINE_REC +extern bool iap_record(bool onoff); +#endif #endif