diff options
| author | Michael Giacomelli <giac2000@hotmail.com> | 2008-12-25 01:46:16 +0000 |
|---|---|---|
| committer | Michael Giacomelli <giac2000@hotmail.com> | 2008-12-25 01:46:16 +0000 |
| commit | 70e9c7aed361787a404c4856211ddf53127fca9a (patch) | |
| tree | 3072967cf3ed2779acf939e150694ef437cbad2d /apps/iap.c | |
| parent | f921f74873a1439cf4e25b87192b31c079863924 (diff) | |
| download | rockbox-70e9c7aed361787a404c4856211ddf53127fca9a.zip rockbox-70e9c7aed361787a404c4856211ddf53127fca9a.tar.gz rockbox-70e9c7aed361787a404c4856211ddf53127fca9a.tar.bz2 rockbox-70e9c7aed361787a404c4856211ddf53127fca9a.tar.xz | |
Commit FS#8624 by Linus Nielsen, Ryan Press, Craig Elliott, and Kenderes Tamas. Adds preliminary support for numerous accessories that use the ipod serial port on the dock connector. See IpodAccessories for a list of tested devices.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19585 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/iap.c')
| -rw-r--r-- | apps/iap.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/apps/iap.c b/apps/iap.c new file mode 100644 index 0000000..a15fd81 --- /dev/null +++ b/apps/iap.c @@ -0,0 +1,730 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: iap.c 17400 2008-05-07 14:30:29Z xxxxxx $ + * + * 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. + * + ****************************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <memory.h> +#include <string.h> + +#include "iap.h" +#include "button.h" +#include "config.h" +#include "cpu.h" +#include "system.h" +#include "kernel.h" +#include "serial.h" + +#include "playlist.h" +#include "playback.h" +#include "audio.h" +#include "settings.h" +#include "metadata.h" +#include "gwps.h" + +#include "button.h" +#include "action.h" + +#define RX_BUFLEN 260 +#define TX_BUFLEN 128 + +static volatile int iap_pollspeed = 0; +static volatile bool iap_remotetick = true; +static bool iap_setupflag = false, iap_updateflag = false; +static int iap_changedctr = 0; + +static unsigned long iap_remotebtn = 0; +static int iap_repeatbtn = 0; +static bool iap_btnrepeat = false, iap_btnshuffle = false; + +unsigned char serbuf[RX_BUFLEN]; +static int serbuf_i = 0; + +static unsigned char response[TX_BUFLEN]; +static int responselen; + +static void iap_task(void) +{ + static int count = 0; + + count += iap_pollspeed; + if (count < (500/10)) return; + + /* exec every 500ms if pollspeed == 1 */ + count = 0; + queue_post(&button_queue, SYS_IAP_PERIODIC, 0); +} + +void iap_setup(int ratenum) +{ + iap_bitrate_set(ratenum); + iap_pollspeed = 0; + iap_remotetick = true; + iap_updateflag = false; + iap_changedctr = 0; + iap_setupflag = true; + iap_remotebtn = BUTTON_NONE; + tick_add_task(iap_task); +} + +void iap_bitrate_set(int ratenum) +{ + switch(ratenum) + { + case 0: + serial_bitrate(0); + break; + case 1: + serial_bitrate(9600); + break; + case 2: + serial_bitrate(19200); + break; + case 3: + serial_bitrate(38400); + break; + case 4: + serial_bitrate(57600); + break; + } +} + +/* Message format: + 0xff + 0x55 + length + mode + command (2 bytes) + parameters (0-n bytes) + checksum (length+mode+parameters+checksum == 0) +*/ + +void iap_send_pkt(unsigned char * data, int len) +{ + int i, chksum; + + if(len > TX_BUFLEN-4) len = TX_BUFLEN-4; + responselen = len + 4; + + response[0] = 0xFF; + response[1] = 0x55; + + chksum = response[2] = len; + for(i = 0; i < len; i ++) + { + chksum += data[i]; + response[i+3] = data[i]; + } + + response[i+3] = 0x100 - (chksum & 0xFF); + + for(i = 0; i < responselen; i ++) + { + while (!tx_rdy()) ; + tx_writec(response[i]); + } +} + +int iap_getc(unsigned char x) +{ + static unsigned char last_x = 0; + static bool newpkt = true; + static unsigned char chksum = 0; + + /* Restart if the sync word is seen */ + if(x == 0x55 && last_x == 0xff/* && newpkt*/) + { + serbuf[0] = 0; + serbuf_i = 0; + chksum = 0; + newpkt = false; + } + else + { + if(serbuf_i >= RX_BUFLEN) + serbuf_i = 0; + + serbuf[serbuf_i++] = x; + chksum += x; + } + last_x = x; + + /* Broadcast to queue if we have a complete message */ + if(serbuf_i && (serbuf_i == serbuf[0]+2)) + { + serbuf_i = 0; + newpkt = true; + if(chksum == 0) + queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0); + } + return newpkt; +} + +void iap_track_changed(void) +{ + iap_changedctr = 1; +} + +void iap_periodic(void) +{ + if(!iap_setupflag) return; + if(!iap_pollspeed) return; + + unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00}; + unsigned long time_elapsed = audio_current_track()->elapsed; + + time_elapsed += wps_state.ff_rewind_count; + + 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? + iap_updateflag = true; + } + + data[4] = time_elapsed >> 24; + data[5] = time_elapsed >> 16; + data[6] = time_elapsed >> 8; + data[7] = time_elapsed; + 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) + { + queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0); + return; + } + + /* Handle Mode 0 */ + if (serbuf[1] == 0x00) + { + switch (serbuf[2]) + { + /* get model info */ + case 0x0D: + { + unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10, + 'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* No idea ??? */ + case 0x0F: + { + unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* FM transmitter sends this: FF 55 06 00 01 05 00 02 01 F1 (mode switch) */ + case 0x01: + { + if(serbuf[3] == 0x05) + { + sleep(HZ/3); + unsigned char data[] = {0x05, 0x02}; + iap_send_pkt(data, sizeof(data)); + } + 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 + iap_send_pkt(data, sizeof(data)); + break; + } + } + } + /* Handle Mode 2 */ + else if (serbuf[1] == 0x02) + { + if(serbuf[2] != 0) return; + iap_remotebtn = BUTTON_NONE; + iap_remotetick = false; + + if(serbuf[0] >= 3 && serbuf[3] != 0) + { + if(serbuf[3] & 1) + iap_remotebtn |= BUTTON_RC_PLAY; + if(serbuf[3] & 2) + iap_remotebtn |= BUTTON_RC_VOL_UP; + if(serbuf[3] & 4) + iap_remotebtn |= BUTTON_RC_VOL_DOWN; + if(serbuf[3] & 8) + iap_remotebtn |= BUTTON_RC_RIGHT; + if(serbuf[3] & 16) + iap_remotebtn |= BUTTON_RC_LEFT; + } + else if(serbuf[0] >= 4 && serbuf[4] != 0) + { + if(serbuf[4] & 1) /* play */ + { + if (audio_status() != AUDIO_STATUS_PLAY) + { + iap_remotebtn |= BUTTON_RC_PLAY; + iap_repeatbtn = 2; + iap_remotetick = false; + iap_changedctr = 1; + } + } + if(serbuf[4] & 2) /* pause */ + { + if (audio_status() == AUDIO_STATUS_PLAY) + { + iap_remotebtn |= BUTTON_RC_PLAY; + iap_repeatbtn = 2; + iap_remotetick = false; + iap_changedctr = 1; + } + } + if((serbuf[4] & 128) && !iap_btnshuffle) /* shuffle */ + { + iap_btnshuffle = true; + if(!global_settings.playlist_shuffle) + { + global_settings.playlist_shuffle = 1; + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + playlist_randomise(NULL, current_tick, true); + } + else if(global_settings.playlist_shuffle) + { + global_settings.playlist_shuffle = 0; + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + playlist_sort(NULL, true); + } + } + else + iap_btnshuffle = false; + } + else if(serbuf[0] >= 5 && serbuf[5] != 0) + { + if((serbuf[5] & 1) && !iap_btnrepeat) /* repeat */ + { + int oldmode = global_settings.repeat_mode; + iap_btnrepeat = true; + + if (oldmode == REPEAT_ONE) + global_settings.repeat_mode = REPEAT_OFF; + else if (oldmode == REPEAT_ALL) + global_settings.repeat_mode = REPEAT_ONE; + else if (oldmode == REPEAT_OFF) + global_settings.repeat_mode = REPEAT_ALL; + + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + audio_flush_and_reload_tracks(); + } + else + iap_btnrepeat = false; + + if(serbuf[5] & 16) /* ffwd */ + { + iap_remotebtn |= BUTTON_RC_RIGHT; + } + if(serbuf[5] & 32) /* frwd */ + { + iap_remotebtn |= BUTTON_RC_LEFT; + } + } + } + /* Handle Mode 3 */ + else if (serbuf[1] == 0x03) + { + switch(serbuf[2]) + { + /* some kind of status packet? */ + case 0x01: + { + unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00}; + iap_send_pkt(data, sizeof(data)); + break; + } + } + } + /* Handle Mode 4 */ + else if (serbuf[1] == 0x04) + { + switch (((unsigned long)serbuf[2] << 8) | serbuf[3]) + { + /* Get data updated??? flag */ + case 0x0009: + { + unsigned char data[] = {0x04, 0x00, 0x0A, 0x00}; + data[3] = iap_updateflag ? 0 : 1; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Set data updated??? flag */ + case 0x000B: + { + iap_updateflag = serbuf[4] ? 0 : 1; + /* respond with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x0B}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get iPod size? */ + case 0x0012: + { + unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get count of given types */ + case 0x0018: + { + unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00}; + unsigned long num = 0; + switch(serbuf[4]) /* type number */ + { + case 0x01: /* total number of playlists */ + num = 1; + break; + case 0x05: /* total number of songs */ + num = 1; + } + data[3] = num >> 24; + data[4] = num >> 16; + data[5] = num >> 8; + data[6] = num; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get time and status */ + case 0x001C: + { + unsigned char data[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + struct mp3entry *id3 = audio_current_track(); + unsigned long time_total = id3->length; + unsigned long time_elapsed = id3->elapsed; + int status = audio_status(); + data[3] = time_total >> 24; + data[4] = time_total >> 16; + data[5] = time_total >> 8; + data[6] = time_total; + data[7] = time_elapsed >> 24; + data[8] = time_elapsed >> 16; + data[9] = time_elapsed >> 8; + data[10] = time_elapsed; + if (status == AUDIO_STATUS_PLAY) + data[11] = 0x01; /* play */ + else if (status & AUDIO_STATUS_PAUSE) + data[11] = 0x02; /* pause */ + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get current pos in playlist */ + case 0x001E: + { + unsigned char data[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00}; + long playlist_pos = playlist_next(0); + playlist_pos -= playlist_get_first_index(NULL); + if(playlist_pos < 0) + playlist_pos += playlist_amount(); + data[3] = playlist_pos >> 24; + data[4] = playlist_pos >> 16; + data[5] = playlist_pos >> 8; + data[6] = playlist_pos; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get title of a song number */ + case 0x0020: + /* Get artist of a song number */ + case 0x0022: + /* Get album of a song number */ + case 0x0024: + { + unsigned char data[70] = {0x04, 0x00, 0xFF}; + struct mp3entry id3; + int fd; + long tracknum = (signed long)serbuf[4] << 24 | + (signed long)serbuf[5] << 16 | + (signed long)serbuf[6] << 8 | serbuf[7]; + data[2] = serbuf[3] + 1; + memcpy(&id3, audio_current_track(), sizeof(id3)); + tracknum += playlist_get_first_index(NULL); + if(tracknum >= playlist_amount()) + tracknum -= playlist_amount(); + + /* If the tracknumber is not the current one, + read id3 from disk */ + if(playlist_next(0) != tracknum) + { + struct playlist_track_info info; + playlist_get_track_info(NULL, tracknum, &info); + fd = open(info.filename, O_RDONLY); + memset(&id3, 0, sizeof(struct mp3entry)); + get_metadata(&id3, fd, info.filename); + close(fd); + } + + /* Return the requested track data */ + switch(serbuf[3]) + { + case 0x20: + strncpy((char *)&data[3], id3.title, 64); + iap_send_pkt(data, 4+strlen(id3.title)); + break; + case 0x22: + strncpy((char *)&data[3], id3.artist, 64); + iap_send_pkt(data, 4+strlen(id3.artist)); + break; + case 0x24: + strncpy((char *)&data[3], id3.album, 64); + iap_send_pkt(data, 4+strlen(id3.album)); + break; + } + break; + } + /* Set polling mode */ + case 0x0026: + { + iap_pollspeed = serbuf[4] ? 1 : 0; + /*responsed with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x26}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* AiR playback control */ + case 0x0029: + { + /* respond with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x29}; + iap_send_pkt(data, sizeof(data)); + switch(serbuf[4]) + { + case 0x01: /* play/pause */ + iap_remotebtn = BUTTON_RC_PLAY; + iap_repeatbtn = 2; + iap_remotetick = false; + iap_changedctr = 1; + break; + case 0x02: /* stop */ + iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT; + iap_repeatbtn = 2; + iap_remotetick = false; + iap_changedctr = 1; + break; + case 0x03: /* skip++ */ + iap_remotebtn = BUTTON_RC_RIGHT; + iap_repeatbtn = 2; + iap_remotetick = false; + break; + case 0x04: /* skip-- */ + iap_remotebtn = BUTTON_RC_LEFT; + iap_repeatbtn = 2; + iap_remotetick = false; + break; + case 0x05: /* ffwd */ + iap_remotebtn = BUTTON_RC_RIGHT; + iap_remotetick = false; + if(iap_pollspeed) iap_pollspeed = 5; + break; + case 0x06: /* frwd */ + iap_remotebtn = BUTTON_RC_LEFT; + iap_remotetick = false; + if(iap_pollspeed) iap_pollspeed = 5; + break; + case 0x07: /* end ffwd/frwd */ + iap_remotebtn = BUTTON_NONE; + iap_remotetick = false; + if(iap_pollspeed) iap_pollspeed = 1; + break; + } + break; + } + /* Get shuffle mode */ + case 0x002C: + { + unsigned char data[] = {0x04, 0x00, 0x2D, 0x00}; + data[3] = global_settings.playlist_shuffle ? 1 : 0; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Set shuffle mode */ + case 0x002E: + { + if(serbuf[4] && !global_settings.playlist_shuffle) + { + global_settings.playlist_shuffle = 1; + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + playlist_randomise(NULL, current_tick, true); + } + else if(!serbuf[4] && global_settings.playlist_shuffle) + { + global_settings.playlist_shuffle = 0; + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + playlist_sort(NULL, true); + } + + + /* respond with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x2E}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get repeat mode */ + case 0x002F: + { + unsigned char data[] = {0x04, 0x00, 0x30, 0x00}; + if(global_settings.repeat_mode == REPEAT_OFF) + data[3] = 0; + else if(global_settings.repeat_mode == REPEAT_ONE) + data[3] = 1; + else + data[3] = 2; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Set repeat mode */ + case 0x0031: + { + int oldmode = global_settings.repeat_mode; + if (serbuf[4] == 0) + global_settings.repeat_mode = REPEAT_OFF; + else if (serbuf[4] == 1) + global_settings.repeat_mode = REPEAT_ONE; + else if (serbuf[4] == 2) + global_settings.repeat_mode = REPEAT_ALL; + + if (oldmode != global_settings.repeat_mode) + { + settings_save(); + settings_apply(false); + if (audio_status() & AUDIO_STATUS_PLAY) + audio_flush_and_reload_tracks(); + } + + /* respond with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x31}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get Max Screen Size for Picture Upload??? */ + case 0x0033: + { + unsigned char data[] = {0x04, 0x00, 0x34, 0x01, 0x36, 0x00, 0xA8, 0x01}; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Get number songs in current playlist */ + case 0x0035: + { + unsigned char data[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00}; + unsigned long playlist_amt = playlist_amount(); + data[3] = playlist_amt >> 24; + data[4] = playlist_amt >> 16; + data[5] = playlist_amt >> 8; + data[6] = playlist_amt; + iap_send_pkt(data, sizeof(data)); + break; + } + /* Jump to track number in current playlist */ + case 0x0037: + { + long tracknum = (signed long)serbuf[4] << 24 | + (signed long)serbuf[5] << 16 | + (signed long)serbuf[6] << 8 | serbuf[7]; + if (!wps_state.paused) + audio_pause(); + audio_skip(tracknum - playlist_next(0)); + if (!wps_state.paused) + audio_resume(); + + /* respond with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00}; + data[4] = serbuf[2]; + data[5] = serbuf[3]; + iap_send_pkt(data, sizeof(data)); + break; + } + default: + { + /* default response is with cmd ok packet */ + unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00}; + data[4] = serbuf[2]; + data[5] = serbuf[3]; + iap_send_pkt(data, sizeof(data)); + break; + } + } + } + serbuf[0] = 0; +} + +int remote_control_rx(void) +{ + int btn = iap_remotebtn; + if(iap_repeatbtn) + { + iap_repeatbtn--; + if(!iap_repeatbtn) + { + iap_remotebtn = BUTTON_NONE; + iap_remotetick = true; + } + } + else + iap_remotetick = true; + + return btn; +} |