rockbox/apps/iap/iap-lingo2.c

305 lines
9.2 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* 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 0x02, Simple Remote Lingo
*
* TODO:
* - Fix cmd 0x00 handling, there has to be a more elegant way of doing
* this
*/
#include "iap-core.h"
#include "iap-lingo.h"
#include "system.h"
#include "button.h"
#include "audio.h"
#include "settings.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)
static void cmd_ack(const unsigned char cmd, const unsigned char status)
{
IAP_TX_INIT(0x02, 0x01);
IAP_TX_PUT(status);
IAP_TX_PUT(cmd);
iap_send_tx();
}
#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)
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
* lingo, one for the command, and one for the first button
* state bits.
*/
CHECKLEN(3);
/* Lingo 0x02 must have been negotiated */
if (!DEVICE_LINGO_SUPPORTED(0x02)) {
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
return;
}
switch (cmd)
{
/* ContextButtonStatus (0x00)
*
* Transmit button events from the device to the iPod
*
* Packet format (offset in buf[]: Description)
* 0x00: Lingo ID: Simple Remote Lingo, always 0x02
* 0x01: Command, always 0x00
* 0x02: Button states 0:7
* 0x03: Button states 8:15 (optional)
* 0x04: Button states 16:23 (optional)
* 0x05: Button states 24:31 (optional)
*
* Returns: (none)
*/
case 0x00:
{
iap_remotebtn = BUTTON_NONE;
iap_timeoutbtn = 0;
if(buf[2] != 0)
{
if(buf[2] & 1)
REMOTE_BUTTON(BUTTON_RC_PLAY);
if(buf[2] & 2)
REMOTE_BUTTON(BUTTON_RC_VOL_UP);
if(buf[2] & 4)
REMOTE_BUTTON(BUTTON_RC_VOL_DOWN);
if(buf[2] & 8)
REMOTE_BUTTON(BUTTON_RC_RIGHT);
if(buf[2] & 16)
REMOTE_BUTTON(BUTTON_RC_LEFT);
}
else if(len >= 4 && buf[3] != 0)
{
if(buf[3] & 1) /* play */
{
if (audio_status() != AUDIO_STATUS_PLAY)
REMOTE_BUTTON(BUTTON_RC_PLAY);
}
if(buf[3] & 2) /* pause */
{
if (audio_status() == AUDIO_STATUS_PLAY)
REMOTE_BUTTON(BUTTON_RC_PLAY);
}
if(buf[3] & 128) /* Shuffle */
{
if (!iap_btnshuffle)
{
iap_shuffle_state(!global_settings.playlist_shuffle);
iap_btnshuffle = true;
}
}
}
else if(len >= 5 && buf[4] != 0)
{
if(buf[4] & 1) /* repeat */
{
if (!iap_btnrepeat)
{
iap_repeat_next();
iap_btnrepeat = true;
}
}
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.
*/
if (buf[4] & 0x04)
{
if (audio_status() == AUDIO_STATUS_PLAY)
REMOTE_BUTTON(BUTTON_RC_PLAY);
}
if(buf[4] & 16) /* ffwd */
REMOTE_BUTTON(BUTTON_RC_RIGHT);
if(buf[4] & 32) /* frwd */
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)
*
* Sent from the iPod to the device
*/
/* ImageButtonStatus (0x02)
*
* Transmit image button events from the device to the iPod
*
* Packet format (offset in buf[]: Description)
* 0x00: Lingo ID: Simple Remote Lingo, always 0x02
* 0x01: Command, always 0x02
* 0x02: Button states 0:7
* 0x03: Button states 8:15 (optional)
* 0x04: Button states 16:23 (optional)
* 0x05: Button states 24:31 (optional)
*
* This command requires authentication
*
* Returns on success:
* IAP_ACK_OK
*
* Returns on failure:
* IAP_ACK_*
*/
case 0x02:
{
if (!DEVICE_AUTHENTICATED) {
cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
break;
}
cmd_ack(cmd, IAP_ACK_CMD_FAILED);
break;
}
/* VideoButtonStatus (0x03)
*
* Transmit video button events from the device to the iPod
*
* Packet format (offset in buf[]: Description)
* 0x00: Lingo ID: Simple Remote Lingo, always 0x02
* 0x01: Command, always 0x03
* 0x02: Button states 0:7
* 0x03: Button states 8:15 (optional)
* 0x04: Button states 16:23 (optional)
* 0x05: Button states 24:31 (optional)
*
* This command requires authentication
*
* Returns on success:
* IAP_ACK_OK
*
* Returns on failure:
* IAP_ACK_*
*/
case 0x03:
{
if (!DEVICE_AUTHENTICATED) {
cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
break;
}
cmd_ack(cmd, IAP_ACK_CMD_FAILED);
break;
}
/* AudioButtonStatus (0x04)
*
* Transmit audio button events from the device to the iPod
*
* Packet format (offset in buf[]: Description)
* 0x00: Lingo ID: Simple Remote Lingo, always 0x02
* 0x01: Command, always 0x04
* 0x02: Button states 0:7
* 0x03: Button states 8:15 (optional)
* 0x04: Button states 16:23 (optional)
* 0x05: Button states 24:31 (optional)
*
* This command requires authentication
*
* Returns on success:
* IAP_ACK_OK
*
* Returns on failure:
* IAP_ACK_*
*/
case 0x04:
{
unsigned char repeatbuf[6];
if (!DEVICE_AUTHENTICATED) {
cmd_ack(cmd, IAP_ACK_NO_AUTHEN);
break;
}
/* This is basically the same command as ContextButtonStatus (0x00),
* with the difference that it requires authentication and that
* it returns an ACK packet to the device.
* So just route it through the handler again, with 0x00 as the
* command
*/
memcpy(repeatbuf, buf, 6);
repeatbuf[1] = 0x00;
iap_handlepkt_mode2((len<6)?len:6, repeatbuf);
cmd_ok(cmd);
break;
}
/* The default response is IAP_ACK_BAD_PARAM */
default:
{
#ifdef LOGF_ENABLE
logf("iap: Unsupported Mode02 Command");
#else
cmd_ack(cmd, IAP_ACK_BAD_PARAM);
#endif
break;
}
}
}