Initial commit of DVD emulator sources.
This commit is contained in:
commit
93cc758fa9
|
@ -0,0 +1,237 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include "DVDEmu.h"
|
||||
#include "Serial.h"
|
||||
#include "Video.h"
|
||||
#include "Victor_XV-D701.h"
|
||||
#include "Toshiba_SD-B100.h"
|
||||
|
||||
#define VERBOSE_DEBUG 1
|
||||
|
||||
int getDirectoryCount( const char * const directory )
|
||||
{
|
||||
int file_count = 0;
|
||||
DIR * dirp;
|
||||
struct dirent * entry;
|
||||
|
||||
dirp = opendir(directory);
|
||||
|
||||
while ((entry = readdir(dirp)) != NULL)
|
||||
{
|
||||
if (entry->d_type == DT_REG)
|
||||
{
|
||||
file_count++;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
|
||||
return file_count;
|
||||
}
|
||||
|
||||
int getDvdType( const char * const directory )
|
||||
{
|
||||
char file[256];
|
||||
FILE *fp;
|
||||
int type = DVD_TYPE_UNKNOWN;
|
||||
|
||||
sprintf(file, "%sdisc.cfg", directory);
|
||||
|
||||
fp = fopen( file, "r" );
|
||||
|
||||
if( fp )
|
||||
{
|
||||
char value[128];
|
||||
fscanf(fp, "%s", value);
|
||||
|
||||
if( strcmp( value, "XV-D701-VCD" ) == 0 )
|
||||
{
|
||||
type = DVD_TYPE_VICTOR_XV_D701_VCD;
|
||||
}
|
||||
else if( strcmp( value, "XV-D701-DVD" ) == 0 )
|
||||
{
|
||||
type = DVD_TYPE_VICTOR_XV_D701_DVD;
|
||||
}
|
||||
else if( strcmp( value, "SD-B100-DVD" ) == 0 )
|
||||
{
|
||||
type = DVD_TYPE_TOSHIBA_SD_B100_DVD;
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void verbose_printf( const char * const fmt, ... )
|
||||
{
|
||||
if( VERBOSE_DEBUG )
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
void exec_shell( const char * shellcmd )
|
||||
{
|
||||
if( fork() == 0 )
|
||||
{
|
||||
execl("/bin/bash","/bin/bash","-c",shellcmd,NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
int status;
|
||||
wait(&status);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHex( const char * prepend, const unsigned char * const data, int length )
|
||||
{
|
||||
if( prepend )
|
||||
{
|
||||
printf( "%s", prepend );
|
||||
}
|
||||
|
||||
for( int x = 0; x < length; x++ )
|
||||
{
|
||||
printf( "%02X ", data[x] );
|
||||
}
|
||||
|
||||
printf( " (Length: %d bytes)\n", length );
|
||||
}
|
||||
|
||||
void PrintInstructions( char * name )
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, "%s port folder\n", name );
|
||||
fprintf( stderr, "\tport - a com port, such as COM1.\n" );
|
||||
fprintf( stderr, "\tfolder - a folder of M4Vs, such as /home/root/dvd/. Must end in \"/\".\n" );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
/* Ensure command is good */
|
||||
if( argc < 2 )
|
||||
{
|
||||
fprintf( stderr, "Missing serial port argument!\n" );
|
||||
PrintInstructions( argv[0] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( argc < 3 )
|
||||
{
|
||||
fprintf( stderr, "Missing m4v folder!\n" );
|
||||
PrintInstructions( argv[0] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Attempt to ascertain the type */
|
||||
int dvd_type = getDvdType( argv[2] );
|
||||
|
||||
if( dvd_type == DVD_TYPE_UNKNOWN )
|
||||
{
|
||||
fprintf( stderr, "Couldn't get DVD information. Be sure your DVD directory has a disc.cfg specifying the type!\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set up DVD player serial */
|
||||
int serial;
|
||||
if( dvd_type == DVD_TYPE_VICTOR_XV_D701_VCD || dvd_type == DVD_TYPE_VICTOR_XV_D701_DVD )
|
||||
{
|
||||
serial = OpenSerial( argv[1], 9600, PARITY_EVEN );
|
||||
}
|
||||
else if( dvd_type == DVD_TYPE_TOSHIBA_SD_B100_DVD )
|
||||
{
|
||||
serial = OpenSerial( argv[1], 9600, PARITY_NONE );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Unknown DVD type, can't init!\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( serial < 0 )
|
||||
{
|
||||
fprintf( stderr, "Failed to open serial port '%s'!\n", argv[1] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ensure we start fresh */
|
||||
PurgeComm( serial );
|
||||
|
||||
/* Set up defaults, making sure not to include the type file */
|
||||
VideoInit( argv[2], getDirectoryCount(argv[2]) - 1 );
|
||||
|
||||
/* Set up DVD emulator */
|
||||
if( dvd_type == DVD_TYPE_VICTOR_XV_D701_VCD )
|
||||
{
|
||||
VictorInit( VICTOR_VCD );
|
||||
}
|
||||
else if( dvd_type == DVD_TYPE_VICTOR_XV_D701_DVD )
|
||||
{
|
||||
VictorInit( VICTOR_DVD );
|
||||
}
|
||||
else if( dvd_type == DVD_TYPE_TOSHIBA_SD_B100_DVD )
|
||||
{
|
||||
ToshibaInit();
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
/* Since JLIP has a start of message and a checksum, as well as the
|
||||
* start of message can't be a mistake given that the top bit is set
|
||||
* and no command or argument uses the top bit, we can simply feed
|
||||
* every byte we get into a small buffer and attempt to handle it as
|
||||
* a packet.
|
||||
*/
|
||||
unsigned char byte;
|
||||
|
||||
if( ReadSerial( serial, &byte, 1 ) == 1 )
|
||||
{
|
||||
/* Got one */
|
||||
int response;
|
||||
if( dvd_type == DVD_TYPE_VICTOR_XV_D701_VCD || dvd_type == DVD_TYPE_VICTOR_XV_D701_DVD )
|
||||
{
|
||||
response = VictorReceiveByte( byte );
|
||||
}
|
||||
else if( dvd_type == DVD_TYPE_TOSHIBA_SD_B100_DVD )
|
||||
{
|
||||
response = ToshibaReceiveByte( byte );
|
||||
}
|
||||
|
||||
if( response )
|
||||
{
|
||||
int length = 0;
|
||||
unsigned char *packet = NULL;
|
||||
|
||||
if( dvd_type == DVD_TYPE_VICTOR_XV_D701_VCD || dvd_type == DVD_TYPE_VICTOR_XV_D701_DVD )
|
||||
{
|
||||
packet = VictorGetResponse( &length );
|
||||
}
|
||||
else if( dvd_type == DVD_TYPE_TOSHIBA_SD_B100_DVD )
|
||||
{
|
||||
packet = ToshibaGetResponse( &length );
|
||||
}
|
||||
|
||||
if( packet && length > 0 )
|
||||
{
|
||||
WriteSerial( serial, packet, length );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Close the connection */
|
||||
CloseSerial( serial );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef __DVDEMU_H
|
||||
#define __DVDEMU_H
|
||||
|
||||
// The disc type is unknown
|
||||
#define DVD_TYPE_UNKNOWN 0
|
||||
// The DVD player is emulating a Victor XV-D701 VCD
|
||||
#define DVD_TYPE_VICTOR_XV_D701_VCD 1
|
||||
// The DVD player is emulating a Victor XV-D701 DVD
|
||||
#define DVD_TYPE_VICTOR_XV_D701_DVD 2
|
||||
// The DVD player is emulating a Toshiba SD-B100
|
||||
#define DVD_TYPE_TOSHIBA_SD_B100_DVD 3
|
||||
|
||||
void verbose_printf( const char * const fmt, ... );
|
||||
void exec_shell( const char * shellcmd );
|
||||
void PrintHex( const char * prepend, const unsigned char * const data, int length );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
all: dvdemu
|
||||
|
||||
dvdemu: DVDEmu.cpp
|
||||
g++ -Wall -Werror -D_BSD_SOURCE -o dvdemu DVDEmu.cpp Serial.cpp Video.cpp Toshiba_SD-B100.cpp Victor_XV-D701.cpp -ggdb
|
||||
|
||||
.DUMMY: clean
|
||||
clean:
|
||||
rm -rf dvdemu
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dirent.h>
|
||||
#include "Serial.h"
|
||||
|
||||
/* Blocking wait time */
|
||||
#define WAIT_TIME 1000
|
||||
|
||||
int OpenSerial( const char *port, int baud, int parity )
|
||||
{
|
||||
int rate;
|
||||
|
||||
switch( baud )
|
||||
{
|
||||
case 9600:
|
||||
rate = B9600;
|
||||
break;
|
||||
case 19200:
|
||||
rate = B19200;
|
||||
break;
|
||||
case 38400:
|
||||
rate = B38400;
|
||||
break;
|
||||
case 57600:
|
||||
rate = B57600;
|
||||
break;
|
||||
case 115200:
|
||||
rate = B115200;
|
||||
break;
|
||||
default:
|
||||
/* Invalid baud rate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct termios tio;
|
||||
int hSerial = 0;
|
||||
|
||||
memset(&tio,0,sizeof(tio));
|
||||
tio.c_iflag=0;
|
||||
tio.c_oflag=0;
|
||||
tio.c_cflag=CS8|CREAD|PARENB|CLOCAL;
|
||||
tio.c_lflag=0;
|
||||
tio.c_cc[VMIN]=1;
|
||||
tio.c_cc[VTIME]=0;
|
||||
|
||||
hSerial=open(port, O_RDWR | O_NONBLOCK);
|
||||
if( hSerial < 0 ) { return -1; }
|
||||
cfsetospeed(&tio,rate);
|
||||
cfsetispeed(&tio,rate);
|
||||
|
||||
tcsetattr(hSerial,TCSANOW,&tio);
|
||||
|
||||
return hSerial;
|
||||
}
|
||||
|
||||
uint64_t getMS()
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL );
|
||||
|
||||
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
bool ReadFile ( int file, void *buf, uint32_t length, uint32_t *readAmt, void *reserved )
|
||||
{
|
||||
uint64_t start = getMS();
|
||||
|
||||
*readAmt = 0;
|
||||
|
||||
while( true )
|
||||
{
|
||||
int amount = read( file, buf, length );
|
||||
|
||||
if( amount < 0 )
|
||||
{
|
||||
if( errno == EAGAIN || errno == EWOULDBLOCK )
|
||||
{
|
||||
if( (getMS() - start) > WAIT_TIME )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try again */
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
*readAmt = amount;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteFile( int file, const void * const buf, uint32_t length, uint32_t *writeAmt, void *reserved )
|
||||
{
|
||||
uint64_t start = getMS();
|
||||
|
||||
while( true )
|
||||
{
|
||||
int amount = write( file, buf, length );
|
||||
|
||||
if( amount < 0 )
|
||||
{
|
||||
if( errno == EAGAIN || errno == EWOULDBLOCK )
|
||||
{
|
||||
if( (getMS() - start) > 1000 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try again */
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
*writeAmt = amount;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetFilePointer ( int file, int off, void *reserved, int origin )
|
||||
{
|
||||
lseek( file, off, origin );
|
||||
}
|
||||
|
||||
void CloseHandle( int file )
|
||||
{
|
||||
close( file );
|
||||
}
|
||||
|
||||
bool PurgeComm( int serial )
|
||||
{
|
||||
/* Clear out old data */
|
||||
do
|
||||
{
|
||||
unsigned char data[1024];
|
||||
uint32_t length;
|
||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||
|
||||
if( length <= 0 ) { break; }
|
||||
} while( 1 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t ReadSerial( int serial, unsigned char *data, unsigned int length )
|
||||
{
|
||||
uint32_t readLength = 0;
|
||||
ReadFile( serial, data, length, &readLength, 0 );
|
||||
return readLength;
|
||||
}
|
||||
|
||||
uint32_t WriteSerial( int serial, const unsigned char *data, unsigned int length )
|
||||
{
|
||||
uint32_t wroteLength = 0;
|
||||
WriteFile( serial, data, length, &wroteLength, 0 );
|
||||
return wroteLength;
|
||||
}
|
||||
|
||||
void CloseSerial( int serial )
|
||||
{
|
||||
CloseHandle( serial );
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __SERIAL_H
|
||||
#define __SERIAL_H
|
||||
|
||||
#define PARITY_EVEN 1
|
||||
#define PARITY_ODD 2
|
||||
#define PARITY_NONE 3
|
||||
|
||||
int OpenSerial( const char *port, int baud, int parity );
|
||||
uint32_t ReadSerial( int serial, unsigned char *data, unsigned int length );
|
||||
uint32_t WriteSerial( int serial, const unsigned char *data, unsigned int length );
|
||||
void CloseSerial( int serial );
|
||||
bool PurgeComm( int serial );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "Toshiba_SD-B100.h"
|
||||
|
||||
void ToshibaInit()
|
||||
{
|
||||
/* TODO: Init packet buffer and response buffer */
|
||||
}
|
||||
|
||||
int ToshibaReceiveByte( unsigned char byte )
|
||||
{
|
||||
/* TODO: Read and parse bytes, return 1 if there is a response to send */
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char *ToshibaGetResponse( int *length )
|
||||
{
|
||||
/* TODO: Return response buffer */
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __TOSHIBA_SD_B100
|
||||
#define __TOSHIBA_SD_B100
|
||||
|
||||
void ToshibaInit();
|
||||
int ToshibaReceiveByte( unsigned char byte );
|
||||
unsigned char *ToshibaGetResponse( int *length );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,457 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include "Victor_XV-D701.h"
|
||||
#include "Video.h"
|
||||
#include "DVDEmu.h"
|
||||
|
||||
#define PACKET_DEBUG 0
|
||||
|
||||
/* Wait for serial */
|
||||
#define WAIT() usleep( 100000 )
|
||||
#define LONGWAIT() usleep( 1000000 )
|
||||
|
||||
/* Requests */
|
||||
#define PREAMBLE_LOC 0
|
||||
#define JLIP_ID_LOC 2
|
||||
#define COMMAND_LOC 3
|
||||
#define PARAMS_LOC 6
|
||||
|
||||
/* Responses */
|
||||
#define PREAMBLE_LOC 0
|
||||
#define JLIP_ID_LOC 2
|
||||
#define STATUS_LOC 3
|
||||
#define DATA_LOC 4
|
||||
|
||||
/* Generic */
|
||||
#define PACKET_LEN 11
|
||||
#define CRC_LOC 10
|
||||
|
||||
unsigned char NO_RESPONSE[6] = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// The command sent was unknown
|
||||
#define STATUS_UNKNOWN_CMD 0x01
|
||||
// The command sent was executed properly
|
||||
#define STATUS_OK 0x03
|
||||
// The command sent was unable to be executed
|
||||
#define STATUS_ERROR 0x05
|
||||
|
||||
typedef unsigned char * packet_t;
|
||||
|
||||
/* Emulator state */
|
||||
struct victor_state
|
||||
{
|
||||
unsigned int jlip_id;
|
||||
unsigned int dvd_type;
|
||||
unsigned char packet[PACKET_LEN];
|
||||
packet_t response;
|
||||
} victor_state;
|
||||
|
||||
unsigned char CalcChecksum( const packet_t packet )
|
||||
{
|
||||
unsigned char sum = 0x80;
|
||||
|
||||
for( uint32_t i = 0; i < (PACKET_LEN - 1); i++ )
|
||||
{
|
||||
sum = sum - (packet[i] & 0x7F);
|
||||
}
|
||||
|
||||
return sum & 0x7F;
|
||||
}
|
||||
|
||||
packet_t CreatePacket( unsigned int id, unsigned int status, unsigned char response[6] )
|
||||
{
|
||||
packet_t data = (packet_t)malloc(PACKET_LEN);
|
||||
|
||||
/* Create preamble */
|
||||
data[PREAMBLE_LOC + 0] = 0xFC;
|
||||
data[PREAMBLE_LOC + 1] = 0xFF;
|
||||
|
||||
/* Stuff in ID */
|
||||
data[JLIP_ID_LOC] = id & 0xFF;
|
||||
|
||||
/* Stuff in response status */
|
||||
data[STATUS_LOC] = status & 0xFF;
|
||||
|
||||
/* Stuff in data */
|
||||
memcpy(&data[DATA_LOC], response, 6);
|
||||
|
||||
/* Calculate CRC */
|
||||
data[CRC_LOC] = CalcChecksum(data);
|
||||
|
||||
/* Send it */
|
||||
return data;
|
||||
}
|
||||
|
||||
int IsPacketGood( packet_t packet )
|
||||
{
|
||||
if( packet[PREAMBLE_LOC + 0] != 0xFF ||
|
||||
packet[PREAMBLE_LOC + 1] != 0xFF )
|
||||
{
|
||||
/* Not a response packet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( CalcChecksum( packet ) != packet[CRC_LOC] )
|
||||
{
|
||||
/* Invalid CRC */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DestroyPacket( packet_t packet )
|
||||
{
|
||||
free( packet );
|
||||
}
|
||||
|
||||
packet_t HandleMediaPacket( packet_t packet )
|
||||
{
|
||||
switch( packet[COMMAND_LOC + 1] )
|
||||
{
|
||||
case 0x41:
|
||||
{
|
||||
/* TODO: Drive commands */
|
||||
break;
|
||||
}
|
||||
case 0x43:
|
||||
{
|
||||
/* Play commands */
|
||||
if( packet[COMMAND_LOC + 2] == 0x6D )
|
||||
{
|
||||
/* Pause */
|
||||
verbose_printf( "Device pause request.\n" );
|
||||
|
||||
Pause();
|
||||
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 2] == 0x75 )
|
||||
{
|
||||
/* Play */
|
||||
verbose_printf( "Device play request.\n" );
|
||||
|
||||
Play();
|
||||
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x44:
|
||||
{
|
||||
/* Stop commands */
|
||||
if( packet[COMMAND_LOC + 2] == 0x60 )
|
||||
{
|
||||
/* Stop */
|
||||
verbose_printf( "Device stop request.\n" );
|
||||
|
||||
Stop();
|
||||
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x4C:
|
||||
{
|
||||
/* TODO; Disk parameter commands */
|
||||
break;
|
||||
}
|
||||
case 0x4E:
|
||||
{
|
||||
/* TODO: Disk status commands */
|
||||
break;
|
||||
}
|
||||
case 0x50:
|
||||
{
|
||||
/* Seek commands */
|
||||
if( packet[COMMAND_LOC + 2] == 0x20 )
|
||||
{
|
||||
/* Seek to chapter */
|
||||
unsigned int chapter = ((packet[PARAMS_LOC + 0] % 10) * 100) + ((packet[PARAMS_LOC + 1] % 10) * 10) + (packet[PARAMS_LOC + 2] % 10);
|
||||
|
||||
if( victor_state.dvd_type == VICTOR_VCD )
|
||||
{
|
||||
/* VCD can only go to 99, so it sicks the data in the first two spots */
|
||||
chapter /= 10;
|
||||
}
|
||||
|
||||
verbose_printf( "Device seek to chapter %d request.\n", chapter );
|
||||
|
||||
if( SeekToChapter( chapter ) )
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
||||
}
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 2] == 0x61 )
|
||||
{
|
||||
/* Previous chapter */
|
||||
verbose_printf( "Device seek to previous chapter request.\n" );
|
||||
|
||||
unsigned int chapter = GetChapter() - 1;
|
||||
|
||||
/* Special case where if we're idle and seek backwards, we start at chapter 1 */
|
||||
if( !IsPlaying() && chapter == 0 )
|
||||
{
|
||||
chapter = 1;
|
||||
}
|
||||
|
||||
if( SeekToChapter( chapter ) )
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
||||
}
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 2] == 0x73 )
|
||||
{
|
||||
/* Next chapter */
|
||||
verbose_printf( "Device seek to next chapter request.\n" );
|
||||
|
||||
unsigned int chapter = GetChapter() + 1;
|
||||
|
||||
if( SeekToChapter( chapter ) )
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
packet_t HandlePowerPacket( packet_t packet )
|
||||
{
|
||||
switch( packet[COMMAND_LOC + 1] )
|
||||
{
|
||||
case 0x40:
|
||||
{
|
||||
/* Power commands */
|
||||
if( packet[COMMAND_LOC + 2] == 0x60 )
|
||||
{
|
||||
/* Turn off device */
|
||||
verbose_printf( "Device turn off request.\n" );
|
||||
|
||||
int status = IsPowered() ? STATUS_OK : STATUS_ERROR;
|
||||
if( status == STATUS_OK )
|
||||
{
|
||||
PowerOff();
|
||||
}
|
||||
return CreatePacket( victor_state.jlip_id, status, NO_RESPONSE );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 2] == 0x70 )
|
||||
{
|
||||
/* Turn on device */
|
||||
verbose_printf( "Device turn on request.\n" );
|
||||
|
||||
int status = IsPowered() ? STATUS_ERROR : STATUS_OK;
|
||||
if( status == STATUS_OK )
|
||||
{
|
||||
PowerOn();
|
||||
}
|
||||
return CreatePacket( victor_state.jlip_id, status, NO_RESPONSE );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x4E:
|
||||
{
|
||||
/* Power status commands */
|
||||
if( packet[COMMAND_LOC + 2] == 0x20 )
|
||||
{
|
||||
/* Status */
|
||||
verbose_printf( "Device power status request.\n" );
|
||||
|
||||
unsigned char response[6] = { 0, 0x20, 0, 0, 0, 0 };
|
||||
response[0] = IsPowered() ? 0x01 : 0x0;
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unknown, let the master handler send back a failure */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
packet_t HandleDevicePacket( packet_t packet )
|
||||
{
|
||||
if( packet[COMMAND_LOC + 1] == 0x41 )
|
||||
{
|
||||
/* Change JLIP ID packet */
|
||||
unsigned int new_id = packet[COMMAND_LOC + 2];
|
||||
|
||||
if( new_id >= 1 && new_id <= 63 )
|
||||
{
|
||||
/* Switch IDs */
|
||||
verbose_printf( "Change JLIP ID to %d\n", new_id );
|
||||
|
||||
packet_t response = CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
||||
victor_state.jlip_id = new_id; /* TODO: Save this to file */
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Out of range */
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
||||
}
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x45 &&
|
||||
packet[COMMAND_LOC + 2] == 0x00 )
|
||||
{
|
||||
/* Get machine code */
|
||||
verbose_printf( "Machine code request.\n");
|
||||
|
||||
unsigned char response[6] = { 0x00, 0x01, 0x03, 0x00, 0x03, 0x01 };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x48 &&
|
||||
packet[COMMAND_LOC + 2] == 0x20 )
|
||||
{
|
||||
/* Get max baud rate */
|
||||
verbose_printf( "Baud rate request.\n");
|
||||
|
||||
/* Hardcode to 9600 baud for now */
|
||||
unsigned char response[6] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x49 &&
|
||||
packet[COMMAND_LOC + 2] == 0x00 )
|
||||
{
|
||||
/* Get machine code */
|
||||
verbose_printf( "Device code request.\n");
|
||||
|
||||
unsigned char response[6] = { 0x03, 0x0C, 0x7F, 0x7F, 0x7F, 0x7F };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x4C &&
|
||||
packet[COMMAND_LOC + 2] == 0x00 )
|
||||
{
|
||||
/* Get first 6 bytes of name */
|
||||
verbose_printf( "Device name first half request.\n" );
|
||||
|
||||
unsigned char response[6] = { 'D', 'V', 'D', ' ', 'P', 'L' };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x4D &&
|
||||
packet[COMMAND_LOC + 2] == 0x00 )
|
||||
{
|
||||
/* Get first 6 bytes of name */
|
||||
verbose_printf( "Device name last half request.\n" );
|
||||
|
||||
unsigned char response[6] = { 'A', 'Y', 'E', 'R', 0x7F, 0x7F };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
else if( packet[COMMAND_LOC + 1] == 0x4E &&
|
||||
packet[COMMAND_LOC + 2] == 0x20 )
|
||||
{
|
||||
/* Get machine code */
|
||||
verbose_printf( "NOP request.\n");
|
||||
|
||||
unsigned char response[6] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
packet_t HandlePacket( packet_t packet )
|
||||
{
|
||||
/* Check that the checksum is good first */
|
||||
if( !IsPacketGood( packet ) )
|
||||
{
|
||||
/* In the case its invalid or unrecognized, we do nothing */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check to see that it's the right ID */
|
||||
if( packet[JLIP_ID_LOC] != victor_state.jlip_id )
|
||||
{
|
||||
/* If it isn't destined for us, we do nothing */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( PACKET_DEBUG ) { PrintHex( "= Incoming: ", packet, PACKET_LEN ); }
|
||||
|
||||
/* Parse the packet out */
|
||||
packet_t response = NULL;
|
||||
switch( packet[COMMAND_LOC] )
|
||||
{
|
||||
case 0x0C:
|
||||
response = HandleMediaPacket( packet );
|
||||
break;
|
||||
case 0x3E:
|
||||
response = HandlePowerPacket( packet );
|
||||
break;
|
||||
case 0x7C:
|
||||
response = HandleDevicePacket( packet );
|
||||
break;
|
||||
}
|
||||
|
||||
if( !response )
|
||||
{
|
||||
/* Unknown */
|
||||
PrintHex( "Unknown packet ", packet, PACKET_LEN );
|
||||
response = CreatePacket( victor_state.jlip_id, STATUS_UNKNOWN_CMD, NO_RESPONSE );
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void VictorInit( int type )
|
||||
{
|
||||
victor_state.jlip_id = 33; /* TODO: Read this from file */
|
||||
victor_state.dvd_type = type;
|
||||
memset( victor_state.packet, 0, PACKET_LEN );
|
||||
victor_state.response = NULL;
|
||||
|
||||
verbose_printf( "Emulating Victor XV-D701 %s.\n", victor_state.dvd_type == VICTOR_VCD ? "VCD" : "DVD" );
|
||||
}
|
||||
|
||||
int VictorReceiveByte( unsigned char byte )
|
||||
{
|
||||
memmove( &victor_state.packet[0], &victor_state.packet[1], PACKET_LEN - 1 );
|
||||
victor_state.packet[PACKET_LEN - 1] = byte;
|
||||
|
||||
if( victor_state.response )
|
||||
{
|
||||
/* Destroy previous packet */
|
||||
DestroyPacket( victor_state.response );
|
||||
}
|
||||
|
||||
victor_state.response = HandlePacket( victor_state.packet );
|
||||
if( victor_state.response )
|
||||
{
|
||||
if( PACKET_DEBUG ) { PrintHex( "= Outgoing: ", victor_state.response, PACKET_LEN ); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char *VictorGetResponse( int *length )
|
||||
{
|
||||
if( victor_state.response )
|
||||
{
|
||||
*length = PACKET_LEN;
|
||||
return (unsigned char *)victor_state.response;
|
||||
}
|
||||
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __Victor_XV_D701_H
|
||||
#define __Victor_XV_D701_H
|
||||
|
||||
#define VICTOR_DVD 0
|
||||
#define VICTOR_VCD 1
|
||||
|
||||
void VictorInit( int type );
|
||||
int VictorReceiveByte( unsigned char byte );
|
||||
unsigned char *VictorGetResponse( int *length );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,178 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "Video.h"
|
||||
#include "DVDEmu.h"
|
||||
|
||||
/* Emulator state */
|
||||
struct video_state
|
||||
{
|
||||
unsigned int power_state;
|
||||
unsigned int playing_state;
|
||||
unsigned int paused_state;
|
||||
unsigned int chapter;
|
||||
unsigned int max_chapter;
|
||||
char * dvd_path;
|
||||
} video_state;
|
||||
|
||||
void VideoInit( char * path, int chapters )
|
||||
{
|
||||
video_state.power_state = 0;
|
||||
video_state.playing_state = 0;
|
||||
video_state.paused_state = 0;
|
||||
video_state.chapter = 1;
|
||||
video_state.dvd_path = path;
|
||||
video_state.max_chapter = chapters;
|
||||
|
||||
verbose_printf( "Disc has %d chapters.\n", video_state.max_chapter );
|
||||
}
|
||||
|
||||
void PowerOn()
|
||||
{
|
||||
if( !video_state.power_state )
|
||||
{
|
||||
verbose_printf( "* Exit standby mode.\n" );
|
||||
|
||||
video_state.power_state = 1;
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void PowerOff()
|
||||
{
|
||||
if( video_state.power_state )
|
||||
{
|
||||
Stop();
|
||||
|
||||
verbose_printf( "* Enter standby mode.\n" );
|
||||
|
||||
video_state.power_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Play()
|
||||
{
|
||||
if( !video_state.power_state )
|
||||
{
|
||||
PowerOn();
|
||||
}
|
||||
|
||||
if( !video_state.playing_state )
|
||||
{
|
||||
verbose_printf( "* Enter playing state.\n" );
|
||||
|
||||
SeekToChapter(video_state.chapter);
|
||||
|
||||
video_state.playing_state = 1;
|
||||
video_state.paused_state = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unpause only if we need to */
|
||||
Unpause();
|
||||
}
|
||||
}
|
||||
|
||||
void Pause()
|
||||
{
|
||||
if( video_state.power_state &&
|
||||
video_state.playing_state &&
|
||||
!video_state.paused_state )
|
||||
{
|
||||
verbose_printf( "* Enter paused state.\n" );
|
||||
|
||||
exec_shell("./control.sh pause &");
|
||||
|
||||
video_state.playing_state = 1;
|
||||
video_state.paused_state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Unpause()
|
||||
{
|
||||
if( video_state.power_state &&
|
||||
video_state.playing_state &&
|
||||
video_state.paused_state )
|
||||
{
|
||||
verbose_printf( "* Exit paused state.\n" );
|
||||
|
||||
exec_shell("./control.sh pause &");
|
||||
|
||||
video_state.playing_state = 1;
|
||||
video_state.paused_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int SeekToChapter( unsigned int chapter )
|
||||
{
|
||||
if( video_state.power_state )
|
||||
{
|
||||
/* Is the chapter in bounds? */
|
||||
if( chapter >= 1 && chapter <= video_state.max_chapter )
|
||||
{
|
||||
verbose_printf( "* Seek to chapter %d.\n", chapter );
|
||||
|
||||
/* Kill player */
|
||||
exec_shell("./control.sh stop 2>/dev/null &");
|
||||
sleep( 1 );
|
||||
exec_shell("killall dbus-daemon");
|
||||
|
||||
/* Seek */
|
||||
char syscall[256];
|
||||
sprintf(syscall, "omxplayer -b --no-osd %s%02d.m4v >/dev/null &", video_state.dvd_path, chapter);
|
||||
exec_shell(syscall);
|
||||
video_state.playing_state = 1;
|
||||
sleep( 1 );
|
||||
|
||||
/* Repause if needed */
|
||||
if( video_state.paused_state ) { exec_shell("./control.sh pause 2>/dev/null &"); }
|
||||
|
||||
video_state.chapter = chapter;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nothing seek-wise while off */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if( video_state.power_state &&
|
||||
( video_state.playing_state ||
|
||||
video_state.paused_state ) )
|
||||
{
|
||||
/* If we are paused, exit the pause state */
|
||||
Unpause();
|
||||
|
||||
verbose_printf( "* Enter idle state.\n" );
|
||||
|
||||
exec_shell("./control.sh stop &");
|
||||
sleep(1);
|
||||
exec_shell("killall dbus-daemon");
|
||||
|
||||
video_state.playing_state = 0;
|
||||
video_state.paused_state = 0;
|
||||
video_state.chapter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int GetChapter()
|
||||
{
|
||||
return video_state.chapter;
|
||||
}
|
||||
|
||||
int IsPowered()
|
||||
{
|
||||
return video_state.power_state;
|
||||
}
|
||||
|
||||
int IsPlaying()
|
||||
{
|
||||
return video_state.playing_state;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef __VIDEO_H
|
||||
#define __VIDEO_H
|
||||
|
||||
void VideoInit( char * path, int chapters );
|
||||
|
||||
void PowerOn();
|
||||
void PowerOff();
|
||||
void Play();
|
||||
void Pause();
|
||||
void Unpause();
|
||||
int SeekToChapter( unsigned int chapter );
|
||||
void Stop();
|
||||
|
||||
int GetChapter();
|
||||
int IsPowered();
|
||||
int IsPlaying();
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue