You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1615 lines
38 KiB

#if defined( _WIN32 )
#include "stdafx.h"
#include <windows.h>
#else
#include <stdio.h>
#include <wchar.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>
#endif
#ifdef _WIN32
#define WAIT() Sleep( 100 )
#define LONGWAIT() Sleep( 1000 )
#else
typedef unsigned char BYTE;
typedef const char * LPCTSTR;
typedef wchar_t _TCHAR;
typedef int HANDLE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef int32_t LONG;
#define PURGE_RXABORT 0
#define PURGE_RXCLEAR 0
#define PURGE_TXABORT 0
#define PURGE_TXCLEAR 0
/* Blocking wait time */
#define WAIT_TIME 1000
/* Wait for serial */
#define WAIT() usleep( 100000 )
#define LONGWAIT() usleep( 1000000 )
int _tstoi( _TCHAR *text )
{
int len = wcslen( text );
char *newtext = (char *)malloc( len * 4 );
wcstombs( newtext, text , len * 4 );
int out = atoi( newtext );
free( newtext );
return out;
}
#endif
/* 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
/* Status stuff */
// This is invalid response packet data or otherwise unrecognized
#define STATUS_BAD_PACKET -1
// This response packet was recognized but failed the CRC check
#define STATUS_BAD_CRC -2
// 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
/* Device status stuff */
// Could not read device status for some reason
#define DEVICE_STATUS_UNKNOWN 0
// The device is currently off
#define DEVICE_STATUS_OFF 1
// The device is currently on
#define DEVICE_STATUS_ON 2
typedef unsigned char * packet_t;
/* Drive status */
typedef enum {
DRIVE_IDLE,
DRIVE_EJECTED,
DRIVE_INSERTING,
DRIVE_READING,
DRIVE_PLAYING,
DRIVE_PAUSED,
DRIVE_OFF,
DRIVE_UNKNOWN_STATUS
} drive_status_t;
/* Disk type */
typedef enum {
DISK_NONE,
DISK_VIDEO_CD,
DISK_AUDIO_CD,
DISK_VIDEO_DVD,
DISK_UNRECOGNIZED_CD,
DISK_UNRECOGNIZED_DVD,
DISK_UNKNOWN_STATUS
} disk_type_t;
/* Disk status */
typedef struct
{
drive_status_t drive_status;
disk_type_t disk_type;
} disk_status_t;
unsigned char NO_PARAMS[4] = { 0, 0, 0, 0 };
void PrintHex(const unsigned char * const data, int length )
{
printf( "Length: %d bytes\n", length );
for( int x = 0; x < length; x++ )
{
printf( "%02X ", data[x] );
}
printf( "\n" );
}
unsigned int GetHex(const _TCHAR *str)
{
int i = 0;
unsigned int value = 0;
while( str[i] != 0 )
{
/* Make room for this character */
value <<= 4;
switch( str[i] )
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value |= str[i] - '0';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value |= (str[i] - 'A') + 10;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value |= (str[i] - 'a') + 10;
break;
}
i++;
}
return value;
}
void PrintStatus( int status )
{
switch( status )
{
case STATUS_BAD_PACKET:
printf( "The device is not communicating.\n" );
return;
case STATUS_BAD_CRC:
printf( "The device returned a bad CRC.\n" );
return;
case STATUS_UNKNOWN_CMD:
printf( "The device did not recognize the command.\n" );
return;
case STATUS_OK:
printf( "The device successfully executed the command.\n" );
return;
case STATUS_ERROR:
printf( "The device could not execute the command.\n" );
return;
default:
printf( "Unknown status!\n" );
return;
}
}
void PrintDriveStatus( drive_status_t status )
{
switch( status )
{
case DRIVE_IDLE:
printf( "The drive is idle.\n" );
return;
case DRIVE_EJECTED:
printf( "The drive is ejected.\n" );
return;
case DRIVE_INSERTING:
printf( "The drive is inserting a disk.\n" );
return;
case DRIVE_READING:
printf( "The drive is reading a disk.\n" );
return;
case DRIVE_PLAYING:
printf( "The drive is playing a disk.\n" );
return;
case DRIVE_PAUSED:
printf( "The drive is paused.\n" );
return;
case DRIVE_OFF:
printf( "The drive is off.\n" );
return;
case DRIVE_UNKNOWN_STATUS:
printf( "The drive status is unknown.\n" );
return;
}
}
void PrintDiskType( disk_type_t type )
{
switch( type )
{
case DISK_NONE:
printf( "There is no disk in the drive.\n" );
return;
case DISK_VIDEO_CD:
printf( "The disk is a video CD.\n" );
return;
case DISK_AUDIO_CD:
printf( "The disk is an audio CD.\n" );
return;
case DISK_VIDEO_DVD:
printf( "The disk is a video DVD.\n" );
return;
case DISK_UNRECOGNIZED_CD:
printf( "The disk is an unrecognized CD.\n" );
return;
case DISK_UNRECOGNIZED_DVD:
printf( "The disk is an unrecognized DVD.\n" );
return;
case DISK_UNKNOWN_STATUS:
printf( "The disk type is unknown.\n" );
return;
}
}
HANDLE OpenSerial( const _TCHAR *port, int baud )
{
#ifdef _WIN32
HANDLE hSerial = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
int rate;
switch( baud )
{
case 9600:
rate = CBR_9600;
break;
case 19200:
rate = CBR_19200;
break;
case 38400:
rate = CBR_38400;
break;
case 57600:
rate = CBR_57600;
break;
case 115200:
rate = CBR_115200;
break;
default:
rate = CBR_9600;
break;
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
dcbSerialParams.BaudRate = rate;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = EVENPARITY;
SetCommState(hSerial, &dcbSerialParams);
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 100;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.ReadTotalTimeoutMultiplier = 20;
SetCommTimeouts(hSerial, &timeouts);
#else
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:
rate = B9600;
break;
}
struct termios tio;
HANDLE hSerial = 0;
memset(&tio,0,sizeof(tio));
tio.c_iflag=0;
tio.c_oflag=0;
tio.c_cflag=CS8|CREAD|PARENB|CLOCAL; // 8n1, see termios.h for more information
tio.c_lflag=0;
tio.c_cc[VMIN]=1;
tio.c_cc[VTIME]=0;
/* Convert to char */
int portlen = wcslen( port );
char *portname = (char *)malloc( portlen * 4 );
wcstombs( portname, port, portlen * 4 );
hSerial=open(portname, O_RDWR | O_NONBLOCK);
if( hSerial < 0 ) { return -1; }
cfsetospeed(&tio,rate);
cfsetispeed(&tio,rate);
tcsetattr(hSerial,TCSANOW,&tio);
free( portname );
#endif
return hSerial;
}
#ifndef _WIN32
uint64_t getMS()
{
struct timeval tv;
gettimeofday(&tv, NULL );
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
bool ReadFile ( HANDLE file, void *buf, DWORD length, DWORD *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( HANDLE file, const void * const buf, DWORD length, DWORD *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 ( HANDLE file, int off, void *reserved, int origin )
{
lseek( file, off, origin );
}
void CloseHandle( HANDLE file )
{
close( file );
}
bool PurgeComm( HANDLE serial, DWORD dwFlags )
{
/* Clear out old data */
do
{
unsigned char data[1024];
DWORD length;
ReadFile( serial, data, sizeof( data ), &length, 0 );
if( length <= 0 ) { break; }
} while( 1 );
return true;
}
#endif
DWORD ReadSerial( HANDLE serial, unsigned char *data, unsigned int length )
{
DWORD readLength = 0;
ReadFile( serial, data, length, &readLength, 0 );
return readLength;
}
DWORD WriteSerial( HANDLE serial, const unsigned char *data, unsigned int length )
{
DWORD wroteLength = 0;
WriteFile( serial, data, length, &wroteLength, 0 );
return wroteLength;
}
void CloseSerial( HANDLE serial )
{
CloseHandle( serial );
}
unsigned char CalcChecksum( const packet_t packet )
{
unsigned char sum = 0x80;
for( DWORD i = 0; i < (PACKET_LEN - 1); i++ )
{
sum = sum - (packet[i] & 0x7F);
}
return sum & 0x7F;
}
packet_t CreateEmptyPacket()
{
return (packet_t)malloc(PACKET_LEN);
}
packet_t CreatePacket( unsigned int id, unsigned int command, unsigned char params[4] )
{
packet_t data = (packet_t)malloc(PACKET_LEN);
/* Create preamble */
data[PREAMBLE_LOC + 0] = 0xFF;
data[PREAMBLE_LOC + 1] = 0xFF;
/* Stuff in ID */
data[JLIP_ID_LOC] = id & 0xFF;
/* Stuff in command */
data[COMMAND_LOC + 0] = (command >> 16) & 0xFF;
data[COMMAND_LOC + 1] = (command >> 8) & 0xFF;
data[COMMAND_LOC + 2] = command & 0xFF;
/* Stuff in params */
memcpy(&data[PARAMS_LOC], params, 4);
/* Calculate CRC */
data[CRC_LOC] = CalcChecksum(data);
/* Send it */
return data;
}
int GetPacketStatus( packet_t packet )
{
if( packet[PREAMBLE_LOC + 0] != 0xFC ||
packet[PREAMBLE_LOC + 1] != 0xFF )
{
/* Not a response packet */
return STATUS_BAD_PACKET;
}
if( CalcChecksum( packet ) != packet[CRC_LOC] )
{
/* Invalid CRC */
return STATUS_BAD_CRC;
}
return packet[STATUS_LOC];
}
void DestroyPacket( packet_t packet )
{
free( packet );
}
/**
* Send a NOP to the device. Useful for probing for devices.
* Returns STATUS_OK if the device is present and talking.
*
* @retval STATUS_OK The device responded successfully
* @retval STATUS_UNKNOWN_CMD The device did not recognize the command
* @retval STATUS_BAD_PACKET The device is not present
* @retval STATUS_BAD_CRC The device had an error in communicating
*/
int NoOp( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x7C4E20, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Ask the device for its machine code. Returns a 6-byte malloc'd byte
* array. If there is an error reading the device, returns NULL.
*/
unsigned char * MachineCode( HANDLE serial, unsigned int id )
{
unsigned char * result = (unsigned char *)malloc(6);
packet_t request = CreatePacket( id, 0x7C4500, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
memcpy( result, &response[DATA_LOC], 6 );
}
else
{
free( result );
result = 0;
}
DestroyPacket( request );
DestroyPacket( response );
return result;
}
/**
* Ask the device for its device code. Returns a 6-byte malloc'd byte
* array. If there is an error reading the device, returns NULL. Empty
* bytes in the array will be filled with 0x7F.
*/
unsigned char * DeviceCode( HANDLE serial, unsigned int id )
{
unsigned char * result = (unsigned char *)malloc(6);
packet_t request = CreatePacket( id, 0x7C4900, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
memcpy( result, &response[DATA_LOC], 6 );
}
else
{
free( result );
result = 0;
}
DestroyPacket( request );
DestroyPacket( response );
return result;
}
/**
* Queries the device to see the maximum baud rate it can
* operate. Assume that it can always operate at 9600 baud.
*
* @retval 9600 The device can operate at 9600 baud.
* @retval 19200 The device can operate at 9600 or 19200 baud.
* @retval 38400 The device can operate at 9600, 19200 or 38400 baud.
* @retval 115200 The device can operate at 9600, 19200, 38400 or 115200 baud.
* @retval 0 The device didn't respond properly or the baud rate was unrecognized. Assume 9600.
*/
unsigned int MaxBaudRate( HANDLE serial, unsigned int id )
{
unsigned int rate = 0;
packet_t request = CreatePacket( id, 0x7C4820, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
switch( response[DATA_LOC] )
{
case 0x20:
rate = 9600;
break;
case 0x21:
rate = 19200;
break;
case 0x22:
rate = 38400;
break;
case 0x23:
rate = 115200;
break;
default:
/* Unknown */
rate = 0;
break;
}
}
else
{
/* Unknown */
rate = 0;
}
DestroyPacket( request );
DestroyPacket( response );
return rate;
}
/**
* Ask the device for its internl name. Returns a malloc'd string
* containing the device name as read from the device. If the device
* could not be read, returns NULL.
*/
char *DeviceName( HANDLE serial, unsigned int id )
{
/* Maximum characters is 12 I think */
char *name = (char *)malloc(13);
name[0] = 0;
packet_t request1 = CreatePacket( id, 0x7C4C00, NO_PARAMS );
packet_t response1 = CreateEmptyPacket();
packet_t request2 = CreatePacket( id, 0x7C4D00, NO_PARAMS );
packet_t response2 = CreateEmptyPacket();
WriteSerial( serial, request1, PACKET_LEN );
WAIT();
ReadSerial( serial, response1, PACKET_LEN );
WAIT();
WriteSerial( serial, request2, PACKET_LEN );
WAIT();
ReadSerial( serial, response2, PACKET_LEN );
if( GetPacketStatus( response1 ) == STATUS_OK &&
GetPacketStatus( response2 ) == STATUS_OK )
{
packet_t responses[2] = { response1, response2 };
for( int y = 0; y < 2; y++ )
{
for( int x = 0; x < 6; x++ )
{
if( responses[y][DATA_LOC + x] != 0x7F )
{
char str[2] = { responses[y][DATA_LOC + x], 0x00 };
strcat( name, str );
}
}
}
}
else
{
/* Unknown */
free( name );
name = 0;
}
DestroyPacket( request1 );
DestroyPacket( request2 );
DestroyPacket( response1 );
DestroyPacket( response2 );
return name;
}
/**
* Ask the device for the current length of the disk. Returns a
* malloc'd 3-integer array representing the minutes, seconds and fractions
* of a second of content on the currently inserted disk. If the details
* couldn't be fetched, returns NULL.
*/
unsigned int *DiskLengthTime( HANDLE serial, unsigned int id )
{
unsigned int *length = (unsigned int *)malloc((sizeof(unsigned int)) * 3);
length[0] = 0;
length[1] = 0;
length[2] = 0;
packet_t request = CreatePacket( id, 0x0C4C22, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
length[0] = response[DATA_LOC + 0] * 10 + response[DATA_LOC + 1];
length[1] = response[DATA_LOC + 2] * 10 + response[DATA_LOC + 3];
length[2] = response[DATA_LOC + 4] * 10 + response[DATA_LOC + 5];
}
else
{
/* Unknown */
free( length );
length = 0;
}
DestroyPacket( request );
DestroyPacket( response );
return length;
}
/**
* Ask the device for the number of chapters of the disk. Returns an
* integer representing the number of tracks. If the device couldn't be
* queried, returns 0.
*/
unsigned int DiskLengthChapters( HANDLE serial, unsigned int id )
{
unsigned int length;
packet_t request = CreatePacket( id, 0x0C4C23, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
length = response[DATA_LOC + 2] * 10 + response[DATA_LOC + 3];
}
else
{
/* Unknown */
length = 0;
}
DestroyPacket( request );
DestroyPacket( response );
return length;
}
/**
* Ask the device for current disk status. Returns a structure
* representing the status of the disk drive and the type of media
* detected.
*/
disk_status_t DiskStatus( HANDLE serial, unsigned int id )
{
disk_status_t status;
packet_t request = CreatePacket( id, 0x0C4E20, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) == STATUS_OK )
{
/* Get drive status */
switch( response[DATA_LOC + 0] )
{
case 0x01:
status.drive_status = DRIVE_EJECTED;
break;
case 0x02:
status.drive_status = DRIVE_INSERTING;
break;
case 0x10:
status.drive_status = DRIVE_READING;
break;
case 0x20:
status.drive_status = DRIVE_IDLE;
break;
case 0x40:
status.drive_status = DRIVE_PLAYING;
break;
case 0x50:
status.drive_status = DRIVE_PAUSED;
break;
case 0x6F:
status.drive_status = DRIVE_OFF;
break;
default:
status.drive_status = DRIVE_UNKNOWN_STATUS;
break;
}
/* Get disk type */
unsigned int type = (response[DATA_LOC + 2] << 8) | response[DATA_LOC + 3];
switch( type )
{
case 0x4040:
status.disk_type = DISK_NONE;
break;
case 0x4144:
status.disk_type = DISK_VIDEO_CD;
break;
case 0x4440:
status.disk_type = DISK_AUDIO_CD;
break;
case 0x4A40:
status.disk_type = DISK_VIDEO_DVD;
break;
case 0x4840:
status.disk_type = DISK_UNRECOGNIZED_DVD;
break;
case 0x414E:
case 0x4140:
status.disk_type = DISK_UNRECOGNIZED_CD;
break;
default:
status.disk_type = DISK_UNKNOWN_STATUS;
break;
}
}
else
{
/* Unknown */
status.drive_status = DRIVE_UNKNOWN_STATUS;
status.disk_type = DISK_UNKNOWN_STATUS;
}
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Set the device to a new JLIP ID.
*
* @retval STATUS_BAD_PACKET The device isn't communicating
* @retval STATUS_OK The device changed to the new ID
* @retval STATUS_ERROR The ID was out of range
*/
int SetID( HANDLE serial, unsigned int id, unsigned int newid )
{
int status;
packet_t request = CreatePacket( id, 0x7C4100 | (newid & 0x3F), NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Turn the device on.
*
* @retval STATUS_BAD_PACKET The device isn't communicating
* @retval STATUS_OK The device was turned on
* @retval STATUS_ERROR The device was already on
*/
int TurnOn( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x3E4070, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Turn the device off.
*
* @retval STATUS_BAD_PACKET The device isn't communicating
* @retval STATUS_OK The device was turned off
* @retval STATUS_ERROR The device was already off
*/
int TurnOff( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x3E4060, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Queries the device for status and returns whether the device
* is on or off. If the device can't be read, returns unknown.
*
* @retval DEVICE_STATUS_UNKNOWN Could not probe the device for status
* @retval DEVICE_STATUS_OFF The device is off or in standby
* @retval DEVICE_STATUS_ON The device is on
*/
int PowerStatus( HANDLE serial, unsigned int id )
{
unsigned int status;
packet_t request = CreatePacket( id, 0x3E4E20, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
if( GetPacketStatus( response ) != STATUS_OK )
{
status = DEVICE_STATUS_UNKNOWN;
}
else
{
switch( response[DATA_LOC] )
{
case 0x00:
status = DEVICE_STATUS_OFF;
break;
case 0x01:
status = DEVICE_STATUS_ON;
break;
default:
status = DEVICE_STATUS_UNKNOWN;
break;
}
}
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Play the currently selected track or chapter on the device. If the
* device is off, turns the device on and starts playing at chapter/track
* 1. When the device is idle, starts playing at chapter/track 1. When
* the device is paused, resumes playing at the current location. When
* already playing, has no effect.
*
* @retval STATUS_OK The device executed the play command
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int Play( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C4375, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Pause the currently playing track/chapter. If the device is
* off, has no effect. If the device is idle, queues up track/chapter
* 1 and starts paused. If the device is playing, pauses the
* track/chapter at the current location. If the device is already
* paused, it stays paused.
*
* @retval STATUS_OK The device executed the pause command
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int Pause( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C436D, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Stop the device if it is currently playing. If the device is off
* or idle, this has no effect. If the device is playing or paused,
* its state is returned to idle.
*
* @retval STATUS_OK The device executed the stop command
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int Stop( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C4460, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Seek to the next track/chapter on the device. If the device is off
* or the track/chapter is out of bounds, this will return an error. If the
* device is idle, this will seek to track/chapter 2 and play. If the device
* is playing, this will seek to the next track/chapter and play. If the device
* is paused, this will seek to the next track/chapter and remain paused at the
* start of the track/chapter.
*
* @retval STATUS_OK The device executed the seek command
* @retval STATUS_ERROR The device was off or the track/chapter was out of bounds
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int NextChapter( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C5073, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Seek to the previous track/chapter on the device. If the device is off
* or the track/chapter is out of bounds, this will return an error. If the
* device is idle, this will seek to track/chapter 1 and play. If the device
* is playing, this will seek to the previous track/chapter and play. If the device
* is paused, this will seek to the previous track/chapter and remain paused at the
* start of the track/chapter.
*
* @retval STATUS_OK The device executed the seek command
* @retval STATUS_ERROR The device was off or the track/chapter was out of bounds
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int PrevChapter( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C5061, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Seek to the specified track/chapter on the device. If the device is off
* or the track/chapter is out of bounds, this will return an error. If the
* device is idle, this will seek to the track/chapter and play. If the device
* is playing, this will seek to the new track/chapter and play. If the device
* is paused, this will seek to the new track/chapter and remain paused at the
* start of the track/chapter.
*
* @retval STATUS_OK The device executed the seek command
* @retval STATUS_ERROR The device was off or the track/chapter was out of bounds
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int SeekToChapter( HANDLE serial, unsigned int id, unsigned int chapter )
{
int status;
unsigned char params[4] = { 0x00, (unsigned char)((chapter / 10) % 10), (unsigned char)(chapter % 10), 0x00 };
packet_t request = CreatePacket( id, 0x0C5020, params );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Eject the drive tray on the device. If the device is off or in the process of
* identifying a disk, this will return an error. If the device has already ejected,
* this will also return an error.
*
* @retval STATUS_OK The device ejected the drive tray
* @retval STATUS_ERROR The device was off or busy, or the device was already ejected
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int Eject( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C4161, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
/**
* Insert the drive tray on the device. If the device is off or in the process of
* inserting a disk, this will return an error. If the device tray is closed, this will
* instead perform an eject command. Therefore it is not safe to call this assuming the
* state will always be inserted after a successful execute. It is recommneded to probe
* the device status to determine the drive tray state.
*
* @retval STATUS_OK The device inserted the drive tray
* @retval STATUS_ERROR The device was off or busy
* @retval STATUS_BAD_PACKET The device isn't communicating
*/
int Insert( HANDLE serial, unsigned int id )
{
int status;
packet_t request = CreatePacket( id, 0x0C4171, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
status = GetPacketStatus( response );
DestroyPacket( request );
DestroyPacket( response );
return status;
}
void PrintInstructions( _TCHAR * name )
{
fwprintf( stderr, L"\n" );
fwprintf( stderr, L"%ls port id command\n", name );
fwprintf( stderr, L"\tport - a com port, such as COM1.\n" );
fwprintf( stderr, L"\tid - a JLIP ID, such as 33. Use 0 to probe.\n" );
fwprintf( stderr, L"\tcommand - a command to perform. Valid commands below.\n" );
fwprintf( stderr, L"\n" );
fwprintf( stderr, L"\tturnon\t\tturn the device on.\n");
fwprintf( stderr, L"\tturnoff\t\tturn the device off.\n");
fwprintf( stderr, L"\teject\t\teject the device tray.\n");
fwprintf( stderr, L"\tinsert\t\tinsert the device tray.\n");
fwprintf( stderr, L"\tstatus\t\tprint device status information.\n");
fwprintf( stderr, L"\tdiskstatus\t\tprint disk status information.\n");
fwprintf( stderr, L"\tsetid x\t\tset the device to listen on JLIP ID x.\n");
fwprintf( stderr, L"\tplay\t\tplay current chapter/track on device.\n");
fwprintf( stderr, L"\tpause\t\tpause playback on device.\n");
fwprintf( stderr, L"\tstop\t\tstop playing and return to idle state.\n");
fwprintf( stderr, L"\tnextchapter\tseek to next chapter.\n");
fwprintf( stderr, L"\tpreviouschapter\tseek to previous chapter.\n");
fwprintf( stderr, L"\tseekchapter x\tseek to chapter x (range 1-99).\n");
fwprintf( stderr, L"\n" );
fwprintf( stderr, L"\traw xxyyzz aa bb cc dd\tperform raw command xxyyzz with params aa, bb, cc, dd.\n");
fwprintf( stderr, L"\tsweep xx[yy[zz]]\tdo a raw command sweep with prefix xx[yy[zz]].\n");
}
#ifdef _WIN32
int _tmain(int argc, _TCHAR* argv[])
{
#else
int main(int argc, char *_targv[])
{
/* Convert to wide */
_TCHAR ** argv = (_TCHAR **)malloc( sizeof(_TCHAR *) * (argc + 1) );
argv[argc] = NULL;
for( int tmp = 0; tmp < argc; tmp++ )
{
int len = strlen(_targv[tmp]);
argv[tmp] = (_TCHAR *)malloc( sizeof(_TCHAR) * (len + 1) );
mbstowcs( argv[tmp], _targv[tmp], sizeof(_TCHAR) * (len + 1) );
}
#endif
/* Ensure command is good */
if( argc < 2 )
{
fwprintf( stderr, L"Missing serial port argument!\n" );
PrintInstructions( argv[0] );
return 1;
}
if( argc < 3 )
{
fwprintf( stderr, L"Missing JLIP ID argument!\n" );
PrintInstructions( argv[0] );
return 1;
}
if( argc < 4 )
{
fwprintf( stderr, L"Missing command argument!\n" );
PrintInstructions( argv[0] );
return 1;
}
/* Get serial device */
HANDLE serial = OpenSerial( argv[1], 9600 );
unsigned int id = _tstoi( argv[2] );
if( serial < 0 )
{
fwprintf( stderr, L"Failed to open serial port '%ls'!\n", argv[1] );
return 1;
}
/* Ensure we start fresh */
PurgeComm( serial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR );
if( id < 1 || id > 63 )
{
printf("Invalid JLIP ID, attempting to probe...\n");
for( unsigned int i = 1; i <= 63; i++ )
{
if( NoOp( serial, i ) == STATUS_OK )
{
/* This is it! */
printf("JLIP device found at address %d.\n", i);
id = i;
break;
}
}
}
if( id < 1 || id > 63 )
{
fwprintf( stderr, L"Unable to find a device!\n" );
}
else
{
if( wcscmp( argv[3], L"status" ) == 0 )
{
printf("Attempting to read device status...\n\n");
char * name = DeviceName( serial, id );
if( name )
{
printf("Device name is '%s'\n", name );
free( name );
}
else
{
printf("Couldn't read name!\n");
}
int mbr = MaxBaudRate( serial, id );
if( mbr > 0 )
{
printf("Device max baud rate is '%d'\n", mbr );
}
else
{
printf("Device max baud rate is UNKNOWN\n");
}
switch( PowerStatus( serial, id ) )
{
case DEVICE_STATUS_UNKNOWN:
printf("Device status is UNKNOWN\n");
break;
case DEVICE_STATUS_OFF:
printf("Device status is OFF\n");
break;
case DEVICE_STATUS_ON:
printf("Device status is ON\n");
break;
}
unsigned char *mc = MachineCode( serial, id );
if( mc )
{
printf("Device machine code is %02X %02X %02X %02X %02X %02X\n",
mc[0], mc[1], mc[2], mc[3], mc[4], mc[5] );
free( mc );
}
else
{
printf("Couldn't read machine code!\n");
}
unsigned char *dc = DeviceCode( serial, id );
if( dc )
{
printf("Device code is");
for( int i = 0; i < 6; i++ )
{
if( dc[i] != 0x7F )
{
printf(" %02X", dc[i] );
}
}
printf("\n");
free( dc );
}
else
{
printf("Couldn't read device code!\n");
}
}
else if( wcscmp( argv[3], L"diskstatus" ) == 0 )
{
printf("Attempting to read disk status...\n\n");
disk_status_t status = DiskStatus( serial, id );
PrintDriveStatus( status.drive_status );
if( status.drive_status != DRIVE_OFF )
{
PrintDiskType( status.disk_type );
if( status.drive_status != DRIVE_EJECTED &&
status.drive_status != DRIVE_INSERTING &&
status.drive_status != DRIVE_READING &&
status.drive_status != DRIVE_OFF &&
status.disk_type != DISK_NONE )
{
unsigned int chapters = DiskLengthChapters( serial, id );
if( chapters )
{
printf("Disk has %d chapers/tracks.\n", chapters );
}
else
{
printf("Couldn't read disk chapters!\n");
}
unsigned int *length = DiskLengthTime( serial, id );
if( length )
{
printf("Disk length is %d:%d.%d\n",
length[0], length[1], length[2] );
free( length );
}
else
{
printf("Couldn't read disk length!\n");
}
}
}
}
else if( wcscmp( argv[3], L"turnon" ) == 0 )
{
int status = TurnOn( serial, id );
if( status == STATUS_ERROR )
{
printf( "The device was already on.\n" );
}
else
{
PrintStatus( status );
}
}
else if( wcscmp( argv[3], L"turnoff" ) == 0 )
{
int status = TurnOff( serial, id );
if( status == STATUS_ERROR )
{
printf( "The device was already off.\n" );
}
else
{
PrintStatus( status );
}
}
else if( wcscmp( argv[3], L"eject" ) == 0 )
{
PrintStatus(Eject( serial, id ));
}
else if( wcscmp( argv[3], L"insert" ) == 0 )
{
PrintStatus(Insert( serial, id ));
}
else if( wcscmp( argv[3], L"play" ) == 0 )
{
PrintStatus(Play( serial, id ));
}
else if( wcscmp( argv[3], L"pause" ) == 0 )
{
PrintStatus(Pause( serial, id ));
}
else if( wcscmp( argv[3], L"stop" ) == 0 )
{
PrintStatus(Stop( serial, id ));
}
else if( wcscmp( argv[3], L"nextchapter" ) == 0 )
{
PrintStatus(NextChapter( serial, id ));
}
else if( wcscmp( argv[3], L"previouschapter" ) == 0 )
{
PrintStatus(PrevChapter( serial, id ));
}
else if( wcscmp( argv[3], L"setid" ) == 0 )
{
unsigned int newid = 33;
if( argc < 5 )
{
fwprintf( stderr, L"Unknown JLIP ID, assumming 33!\n" );
}
else
{
newid = _tstoi( argv[4] );
}
PrintStatus(SetID( serial, id, newid ));
}
else if( wcscmp( argv[3], L"seekchapter" ) == 0 )
{
unsigned int chapter = 1;
if( argc < 5 )
{
fwprintf( stderr, L"Unknown chapter, assuming chapter 1!\n" );
}
else
{
chapter = _tstoi( argv[4] );
}
PrintStatus(SeekToChapter( serial, id, chapter ));
}
else if( wcscmp( argv[3], L"raw" ) == 0 )
{
if( argc < 9 )
{
fwprintf( stderr, L"Invalid raw command format!\n" );
}
else
{
unsigned int command = GetHex(argv[4]);
unsigned char params[4] = { (unsigned char)(GetHex(argv[5]) & 0xFF),
(unsigned char)(GetHex(argv[6]) & 0xFF),
(unsigned char)(GetHex(argv[7]) & 0xFF),
(unsigned char)(GetHex(argv[8]) & 0xFF) };
packet_t request = CreatePacket( id, command, params );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
printf("Request RAW:\n");
PrintHex( request, PACKET_LEN );
printf("Response RAW:\n");
PrintHex( response, PACKET_LEN );
DestroyPacket( request );
DestroyPacket( response );
}
}
else if( wcscmp( argv[3], L"sweep" ) == 0 )
{
if( argc < 5 )
{
fwprintf( stderr, L"Invalid sweep command format!\n" );
}
else
{
unsigned int command = GetHex(argv[4]);
unsigned int commandlen = wcslen(argv[4]);
unsigned int sweeplen = (6 - commandlen);
if( sweeplen > 0 )
{
int sweepmax = 1 << (sweeplen * 4);
command <<= (sweeplen * 4);
for( int i = 0; i < sweepmax; i++ )
{
packet_t request = CreatePacket( id, command | i, NO_PARAMS );
packet_t response = CreateEmptyPacket();
WriteSerial( serial, request, PACKET_LEN );
WAIT();
ReadSerial( serial, response, PACKET_LEN );
printf("Request RAW:\n");
PrintHex( request, PACKET_LEN );
printf("Response RAW:\n");
PrintHex( response, PACKET_LEN );
printf("\n");
DestroyPacket( request );
DestroyPacket( response );
}
}
else
{
printf( "No range to sweep!\n" );
}
}
}
else
{
fwprintf( stderr, L"Unrecognized command!\n" );
PrintInstructions( argv[0] );
}
}
/* Close the connection */
CloseSerial( serial );
return 0;
}