commit
93cc758fa9
11 changed files with 1152 additions and 0 deletions
@ -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