iAP: lingo 1 (microphone)

Change-Id: I65da2064951972368a2880d271280e5b5ae878fe
This commit is contained in:
Cástor Muñoz 2014-12-06 23:28:31 +01:00
parent b2d650f511
commit be5fc0ff7f
7 changed files with 271 additions and 1 deletions

View file

@ -64,6 +64,9 @@ scrobbler.c
#ifdef IPOD_ACCESSORY_PROTOCOL #ifdef IPOD_ACCESSORY_PROTOCOL
iap/iap-core.c iap/iap-core.c
iap/iap-lingo0.c iap/iap-lingo0.c
#ifdef HAVE_LINE_REC
iap/iap-lingo1.c
#endif
iap/iap-lingo2.c iap/iap-lingo2.c
iap/iap-lingo3.c iap/iap-lingo3.c
iap/iap-lingo4.c iap/iap-lingo4.c

View file

@ -34,6 +34,9 @@
#if CONFIG_TUNER #if CONFIG_TUNER
#include "radio.h" #include "radio.h"
#endif #endif
#if defined(IPOD_ACCESSORY_PROTOCOL) && defined(HAVE_LINE_REC)
#include "iap.h"
#endif
/* Some audio sources may require a boosted CPU */ /* Some audio sources may require a boosted CPU */
#ifdef HAVE_ADJUSTABLE_CPU_FREQ #ifdef HAVE_ADJUSTABLE_CPU_FREQ
@ -95,6 +98,13 @@ void audio_set_input_source(int source, unsigned flags)
radio_start(); radio_start();
#endif #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 */ /* set hardware inputs */
audio_input_mux(source, flags); audio_input_mux(source, flags);
} /* audio_set_source */ } /* audio_set_source */

View file

@ -154,7 +154,11 @@ unsigned char* iap_txnext;
*/ */
unsigned char lingo_versions[32][2] = { unsigned char lingo_versions[32][2] = {
{1, 9}, /* General lingo, 0x00 */ {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, 2}, /* Simple remote lingo, 0x02 */
{1, 5}, /* Display remote lingo, 0x03 */ {1, 5}, /* Display remote lingo, 0x03 */
{1, 12}, /* Extended Interface lingo, 0x04 */ {1, 12}, /* Extended Interface lingo, 0x04 */
@ -1262,6 +1266,9 @@ void iap_handlepkt(void)
unsigned char mode = *(iap_rxstart+2); unsigned char mode = *(iap_rxstart+2);
switch (mode) { switch (mode) {
case 0: iap_handlepkt_mode0(length, iap_rxstart+2); break; 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 2: iap_handlepkt_mode2(length, iap_rxstart+2); break;
case 3: iap_handlepkt_mode3(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; case 4: iap_handlepkt_mode4(length, iap_rxstart+2); break;

View file

@ -18,6 +18,9 @@
****************************************************************************/ ****************************************************************************/
void iap_handlepkt_mode0(const unsigned int len, const unsigned char *buf); 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_mode2(const unsigned int len, const unsigned char *buf);
void iap_handlepkt_mode3(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); void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf);

218
apps/iap/iap-lingo1.c Normal file
View file

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

View file

@ -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) void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
{ {
static bool poweron_pressed = false;
unsigned int cmd = buf[1]; unsigned int cmd = buf[1];
/* We expect at least three bytes in the buffer, one for the /* 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 /* Power off
* Not quite sure how to react to this, but stopping playback * Not quite sure how to react to this, but stopping playback
* is a good start. * 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); 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; break;
} }
/* ACK (0x01) /* ACK (0x01)

View file

@ -34,5 +34,8 @@ extern void iap_periodic(void);
extern void iap_handlepkt(void); extern void iap_handlepkt(void);
extern void iap_send_pkt(const unsigned char * data, int len); extern void iap_send_pkt(const unsigned char * data, int len);
const unsigned char *iap_get_serbuf(void); const unsigned char *iap_get_serbuf(void);
#ifdef HAVE_LINE_REC
extern bool iap_record(bool onoff);
#endif
#endif #endif