251 lines
9.3 KiB
C
251 lines
9.3 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* 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.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
#ifndef _IAP_CORE_H
|
||
|
#define _IAP_CORE_H
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
#include "timefuncs.h"
|
||
|
#include "metadata.h"
|
||
|
#include "playlist.h"
|
||
|
#include "iap.h"
|
||
|
|
||
|
#define LOGF_ENABLE
|
||
|
/* #undef LOGF_ENABLE */
|
||
|
#ifdef LOGF_ENABLE
|
||
|
#include "logf.h"
|
||
|
#endif
|
||
|
|
||
|
/* The Model ID of the iPod we emulate. Currently a 160GB classic */
|
||
|
#define IAP_IPOD_MODEL (0x00130200U)
|
||
|
|
||
|
/* The firmware version we emulate. Currently 2.0.3 */
|
||
|
#define IAP_IPOD_FIRMWARE_MAJOR (2)
|
||
|
#define IAP_IPOD_FIRMWARE_MINOR (0)
|
||
|
#define IAP_IPOD_FIRMWARE_REV (3)
|
||
|
|
||
|
/* Status code for IAP ack messages */
|
||
|
#define IAP_ACK_OK (0x00) /* Success */
|
||
|
#define IAP_ACK_UNKNOWN_DB (0x01) /* Unknown Database Category */
|
||
|
#define IAP_ACK_CMD_FAILED (0x02) /* Command failed */
|
||
|
#define IAP_ACK_NO_RESOURCE (0x03) /* Out of resources */
|
||
|
#define IAP_ACK_BAD_PARAM (0x04) /* Bad parameter */
|
||
|
#define IAP_ACK_UNKNOWN_ID (0x05) /* Unknown ID */
|
||
|
#define IAP_ACK_PENDING (0x06) /* Command pending */
|
||
|
#define IAP_ACK_NO_AUTHEN (0x07) /* Not authenticated */
|
||
|
#define IAP_ACK_BAD_AUTHEN (0x08) /* Bad authentication version */
|
||
|
/* 0x09 reserved */
|
||
|
#define IAP_ACK_CERT_INVAL (0x0A) /* Certificate invalid */
|
||
|
#define IAP_ACK_CERT_PERM (0x0B) /* Certificate permissions invalid */
|
||
|
/* 0x0C-0x10 reserved */
|
||
|
#define IAP_ACK_RES_INVAL (0x11) /* Invalid accessory resistor value */
|
||
|
|
||
|
/* Add a button to the remote button bitfield. Also set iap_repeatbtn=1
|
||
|
* to ensure a button press is at least delivered once.
|
||
|
*/
|
||
|
#define REMOTE_BUTTON(x) do { \
|
||
|
iap_remotebtn |= (x); \
|
||
|
iap_timeoutbtn = 3; \
|
||
|
iap_repeatbtn = 2; \
|
||
|
} while(0)
|
||
|
|
||
|
/* States of the extended command support */
|
||
|
enum interface_state {
|
||
|
IST_STANDARD, /* General state, support lingo 0x00 commands */
|
||
|
IST_EXTENDED, /* Extended Interface lingo (0x04) negotiated */
|
||
|
};
|
||
|
|
||
|
/* States of the authentication state machine */
|
||
|
enum authen_state {
|
||
|
AUST_NONE, /* Initial state, no message sent */
|
||
|
AUST_INIT, /* Remote side has requested authentication */
|
||
|
AUST_CERTREQ, /* Remote certificate requested */
|
||
|
AUST_CERTBEG, /* Certificate is being received */
|
||
|
AUST_CERTDONE, /* Certificate received */
|
||
|
AUST_CHASENT, /* Challenge sent */
|
||
|
AUST_CHADONE, /* Challenge response received */
|
||
|
AUST_AUTH, /* Authentication complete */
|
||
|
};
|
||
|
|
||
|
/* State of authentication */
|
||
|
struct auth_t {
|
||
|
enum authen_state state; /* Current state of authentication */
|
||
|
unsigned char max_section; /* The maximum number of certificate sections */
|
||
|
unsigned char next_section; /* The next expected section number */
|
||
|
};
|
||
|
|
||
|
/* State of GetAccessoryInfo */
|
||
|
enum accinfo_state {
|
||
|
ACCST_NONE, /* Initial state, no message sent */
|
||
|
ACCST_INIT, /* Send out initial GetAccessoryInfo */
|
||
|
ACCST_SENT, /* Wait for initial RetAccessoryInfo */
|
||
|
ACCST_DATA, /* Query device information, according to capabilities */
|
||
|
};
|
||
|
|
||
|
/* A struct describing an attached device and it's current
|
||
|
* state
|
||
|
*/
|
||
|
struct device_t {
|
||
|
struct auth_t auth; /* Authentication state */
|
||
|
enum accinfo_state accinfo; /* Accessory information state */
|
||
|
uint32_t lingoes; /* Negotiated lingoes */
|
||
|
uint32_t notifications; /* Requested notifications. These are just the
|
||
|
* notifications explicitly requested by the
|
||
|
* device
|
||
|
*/
|
||
|
uint32_t changed_notifications; /* Tracks notifications that changed since the last
|
||
|
* call to SetRemoteEventNotification or GetRemoteEventStatus
|
||
|
*/
|
||
|
bool do_notify; /* Notifications enabled */
|
||
|
bool do_power_notify; /* Whether to send power change notifications.
|
||
|
* These are sent automatically to all devices
|
||
|
* that used IdentifyDeviceLingoes to identify
|
||
|
* themselves, independent of other notifications
|
||
|
*/
|
||
|
|
||
|
uint32_t trackpos_ms; /* These fields are to save the current state */
|
||
|
uint32_t track_index; /* of various fields so we can send a notification */
|
||
|
uint32_t chapter_index; /* if they change */
|
||
|
unsigned char play_status;
|
||
|
bool mute;
|
||
|
unsigned char volume;
|
||
|
unsigned char power_state;
|
||
|
unsigned char battery_level;
|
||
|
uint32_t equalizer_index;
|
||
|
unsigned char shuffle;
|
||
|
unsigned char repeat;
|
||
|
struct tm datetime;
|
||
|
unsigned char alarm_state;
|
||
|
unsigned char alarm_hour;
|
||
|
unsigned char alarm_minute;
|
||
|
unsigned char backlight;
|
||
|
bool hold;
|
||
|
unsigned char soundcheck;
|
||
|
unsigned char audiobook;
|
||
|
uint16_t trackpos_s;
|
||
|
uint32_t capabilities; /* Capabilities of the device, as returned by type 0
|
||
|
* of GetAccessoryInfo
|
||
|
*/
|
||
|
uint32_t capabilities_queried; /* Capabilities already queried */
|
||
|
};
|
||
|
|
||
|
extern struct device_t device;
|
||
|
#define DEVICE_AUTHENTICATED (device.auth.state == AUST_AUTH)
|
||
|
#define DEVICE_AUTH_RUNNING ((device.auth.state != AUST_NONE) && (device.auth.state != AUST_AUTH))
|
||
|
#define DEVICE_LINGO_SUPPORTED(x) (device.lingoes & BIT_N((x)&0x1f))
|
||
|
|
||
|
extern unsigned long iap_remotebtn;
|
||
|
extern unsigned int iap_timeoutbtn;
|
||
|
extern int iap_repeatbtn;
|
||
|
|
||
|
extern unsigned char* iap_txpayload;
|
||
|
extern unsigned char* iap_txnext;
|
||
|
|
||
|
/* These are a number of helper macros to manage the dynamic TX buffer content
|
||
|
* These macros DO NOT CHECK for buffer overflow. iap_send_tx() will, but
|
||
|
* it might be too late at that point. See the current size of TX_BUFLEN
|
||
|
*/
|
||
|
|
||
|
/* Initialize the TX buffer with a lingo and command ID. This will reset the
|
||
|
* data pointer, effectively invalidating unsent information in the TX buffer.
|
||
|
* There are two versions of this, one for 1 byte command IDs (all Lingoes except
|
||
|
* 0x04) and one for two byte command IDs (Lingo 0x04)
|
||
|
*/
|
||
|
#define IAP_TX_INIT(lingo, command) do { \
|
||
|
iap_txnext = iap_txpayload; \
|
||
|
IAP_TX_PUT((lingo)); \
|
||
|
IAP_TX_PUT((command)); \
|
||
|
} while (0)
|
||
|
|
||
|
#define IAP_TX_INIT4(lingo, command) do { \
|
||
|
iap_txnext = iap_txpayload; \
|
||
|
IAP_TX_PUT((lingo)); \
|
||
|
IAP_TX_PUT_U16((command)); \
|
||
|
} while (0)
|
||
|
|
||
|
/* Put an unsigned char into the TX buffer */
|
||
|
#define IAP_TX_PUT(data) *(iap_txnext++) = (data)
|
||
|
|
||
|
/* Put a 16bit unsigned quantity into the TX buffer */
|
||
|
#define IAP_TX_PUT_U16(data) do { \
|
||
|
put_u16(iap_txnext, (data)); \
|
||
|
iap_txnext += 2; \
|
||
|
} while (0)
|
||
|
|
||
|
/* Put a 32bit unsigned quantity into the TX buffer */
|
||
|
#define IAP_TX_PUT_U32(data) do { \
|
||
|
put_u32(iap_txnext, (data)); \
|
||
|
iap_txnext += 4; \
|
||
|
} while (0)
|
||
|
|
||
|
/* Put an arbitrary amount of data (identified by a char pointer and
|
||
|
* a length) into the TX buffer
|
||
|
*/
|
||
|
#define IAP_TX_PUT_DATA(data, len) do { \
|
||
|
memcpy(iap_txnext, (unsigned char *)(data), (len)); \
|
||
|
iap_txnext += (len); \
|
||
|
} while(0)
|
||
|
|
||
|
/* Put a NULL terminated string into the TX buffer, including the
|
||
|
* NULL byte
|
||
|
*/
|
||
|
#define IAP_TX_PUT_STRING(str) IAP_TX_PUT_DATA((str), strlen((str))+1)
|
||
|
|
||
|
/* Put a NULL terminated string into the TX buffer, taking care not to
|
||
|
* overflow the buffer. If the string does not fit into the TX buffer
|
||
|
* it will be truncated, but always NULL terminated.
|
||
|
*
|
||
|
* This function is expensive compared to the other IAP_TX_PUT_*
|
||
|
* functions
|
||
|
*/
|
||
|
#define IAP_TX_PUT_STRLCPY(str) iap_tx_strlcpy(str)
|
||
|
|
||
|
extern unsigned char lingo_versions[32][2];
|
||
|
#define LINGO_SUPPORTED(x) (LINGO_MAJOR((x)&0x1f) > 0)
|
||
|
#define LINGO_MAJOR(x) (lingo_versions[(x)&0x1f][0])
|
||
|
#define LINGO_MINOR(x) (lingo_versions[(x)&0x1f][1])
|
||
|
|
||
|
void put_u16(unsigned char *buf, const uint16_t data);
|
||
|
void put_u32(unsigned char *buf, const uint32_t data);
|
||
|
uint32_t get_u32(const unsigned char *buf);
|
||
|
uint16_t get_u16(const unsigned char *buf);
|
||
|
void iap_tx_strlcpy(const unsigned char *str);
|
||
|
|
||
|
void iap_reset_auth(struct auth_t* auth);
|
||
|
void iap_reset_device(struct device_t* device);
|
||
|
|
||
|
void iap_shuffle_state(bool state);
|
||
|
void iap_repeat_state(unsigned char state);
|
||
|
void iap_repeat_next(void);
|
||
|
void iap_fill_power_state(void);
|
||
|
|
||
|
void iap_send_tx(void);
|
||
|
|
||
|
extern enum interface_state interface_state;
|
||
|
void iap_interface_state_change(const enum interface_state new);
|
||
|
|
||
|
extern bool iap_btnrepeat;
|
||
|
extern bool iap_btnshuffle;
|
||
|
|
||
|
uint32_t iap_get_trackpos(void);
|
||
|
uint32_t iap_get_trackindex(void);
|
||
|
void iap_get_trackinfo(const unsigned int track, struct mp3entry* id3);
|
||
|
|
||
|
#endif
|