/*
 * Copyright (c) 2002, Peter Bentley
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *   Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 *   Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 *   The name Peter Bentley may not be used to endorse or promote
 * products derived from this software without specific prior
 * written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *
 * ** netmd_cmd.c - send actual netmd commands to the unit
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "omd.h"

CVSID( "$Id: netmd_cmd.c,v 1.21 2003/04/08 19:14:47 pete Exp $" );



int compare_const( const uint8 *actual, const uint8 *expected, int size )
{
    int i;

    for( i = 0; i < size; i++, actual++, expected++ )
    {
	if( *actual != *expected )
	{
	    slog( LOG_WARNING,
		     "Byte arrays differ at offset %d, got %02x expected %02x",
		     i, *actual, *expected );
	    return -1;
	}
    }
    return 0;
}


/*****************************************************************************
 * Allocate buffer
 */
netmd_cmd_buf_t *netmd_alloc_buffer( unsigned int size )
{
    netmd_cmd_buf_t *cmdbuf = malloc( sizeof( netmd_cmd_buf_t ));

    if( cmdbuf )
    {
	cmdbuf->buffer = malloc( size );
	if( cmdbuf->buffer == NULL )
	{
	    slog( LOG_ERROR, "netmd_alloc_buffer: Unable to allocate %d bytes", size );
	    cmdbuf->size = 0;
	    cmdbuf->used = 0;
	}
	else
	{
	    cmdbuf->size = size;
	    cmdbuf->used = 0;
	}
    }
    return cmdbuf;
}




/*****************************************************************************
 * Free buffer
 */
int netmd_free_buffer( netmd_cmd_buf_t *cmdbuf )
{
    if( cmdbuf )
    {
	if( cmdbuf->buffer )
	{
	    free( cmdbuf->buffer );
	}
	free( cmdbuf );
    }
    return 0;
}


/*****************************************************************************
 * Helper functions for netmd_vbuild_command
 */
static int hexvalue( uint8 byte )
{
    if( ( byte >= '0' ) && ( byte <= '9' ))
    {
	return( byte - '0' );
    }
    if( ( byte >= 'a' ) && ( byte <= 'f' ))
    {
	return( (  byte - 'a' ) + 10 );
    }
    return -1;
}

static int addbyte( netmd_cmd_buf_t *cmdbuf, uint8 byte )
{
    if( cmdbuf->used >= cmdbuf->size )
    {
	slog( LOG_ERROR, "command buffer overflow" );
	return -1;
    }
    cmdbuf->buffer[cmdbuf->used++] = byte;
    return 0;
}

int netmd_vbuild_command( netmd_cmd_buf_t *cmdbuf, const char *fmt, va_list args )
{
    int		i, val1, val2, datalen;
    uint8	byteval, *dataptr;
    uint16	wordval;
    uint32	dwordval;

    slog( LOG_DETAIL, "netmd_vbuild_command: '%s'", fmt );

    /* Reset buffer pointers */
    cmdbuf->used = 0;
    for( ; *fmt; fmt++ ) {
	switch( *fmt )
	{
	    /* Ignore whitespace */
	    case ' ':
	    case '\t':
		break;

	    /* Insert a word value */
	    case 'W':
		wordval = va_arg( args, int );
		// slog( LOG_DETAIL, "Word value: 0x%04x", wordval );
		if( ( addbyte( cmdbuf, (wordval >> 8 ) & 0xff ) < 0 ) ||
		    ( addbyte( cmdbuf, (wordval & 0xff )) < 0 ))
		{
		    return -1;
		}
		break;

	    /* Insert a byte value */
	    case 'B':
		byteval = va_arg( args, int );
		// slog( LOG_DETAIL, "Byte value: 0x%02x", byteval );
		if( addbyte( cmdbuf, byteval ) < 0 )
		{
		    return -1;
		}
		break;

	    /* Insert a longword */
	    case 'L':
		dwordval = va_arg( args, int );
		// slog( LOG_DETAIL, "Long value: 0x%08x", dwordval );
		if( ( addbyte( cmdbuf, (dwordval >> 24 ) & 0xff ) < 0 ) ||
		    ( addbyte( cmdbuf, (dwordval >> 16 ) & 0xff ) < 0 ) ||
		    ( addbyte( cmdbuf, (dwordval >> 8 ) & 0xff ) < 0 ) ||
		    ( addbyte( cmdbuf, (dwordval & 0xff )) < 0 ))
		{
		    return -1;
		}
		break;

	    /* Insert byte data */
	    case 'D':
		dataptr = va_arg( args, uint8* );
		datalen = va_arg( args, int );
		for( i = 0; i < datalen; i++, dataptr++ )
		{
		    if( addbyte( cmdbuf, *dataptr ) < 0 )
		    {
			return -1;
		    }
		}
		break;
		
	    default:
		val1 = hexvalue( *fmt );
		if( val1 >= 0 )
		{
		    fmt++;
		    /* XXX check for end of string */
		    val2 = hexvalue( *fmt );
		    if( val2 >= 0 )
		    {
			byteval = ( val1 << 4 ) | val2;
			//slog( LOG_DETAIL, "Build: Literal byte: 0x%02x", byteval);
			if( addbyte( cmdbuf, byteval ) < 0 )
			{
			    return -1;
			}
			/* Success! */
			break;
		    }
		}
		slog( LOG_ERROR, "Invalid cmd format string! Got '%c'",
			 *fmt );
		return -1;
	}
    }
    return 0;
}


/*****************************************************************************
 * Build up a command buffer from arguments
 */

int netmd_build_command( netmd_cmd_buf_t *cmdbuf, const char *fmt, ... )
{
    va_list args;

    va_start( args, fmt );

    return netmd_vbuild_command( cmdbuf, fmt, args );
}
/*****************************************************************************
 * Function to decode a netmd reply
 */
int netmd_vdecode_response( netmd_cmd_buf_t *cmdbuf, const char *fmt,
			    va_list args )
{
    int		val1, val2, stringmode, outbufsize;
    uint8	byteval, *data, *outbuf;
    uint16	wordexp, wordval, length;
    uint32	longexp, longval;

    slog( LOG_DETAIL, "netmd_vdecode_response: '%s'", fmt );

    data = cmdbuf->buffer;
    for( ; *fmt; fmt++ ) {

	if( (data - cmdbuf->buffer) > cmdbuf->used )
	{
	    slog( LOG_ERROR, "Data underflow in decode" );
	    return -1;
	}

	stringmode = 0;
	switch( *fmt )
	{
	    /* Ignore whitespace */
	    case ' ':
	    case '\t':
		break;

	    /* Decode a word which should be constant */
	    case 'W':
		wordexp = va_arg( args, int );
		// slog( LOG_DETAIL, "Expect word value: 0x%04x", wordexp );

		wordval = (data[0] << 8) | data[1];
		data += 2;
		if( wordexp != wordval )
		{
		    slog( LOG_ERROR, "Word mistach: expected %04x got %04x",
			     wordexp, wordval );
		    return -1;
		}
		break;
		
	    /* Decode a byte which should be constant */
	    case 'B':
		byteval = va_arg( args, int );
		// slog( LOG_DETAIL, "Expect byte value: 0x%02x", byteval );

		if( byteval != *data )
		{
		    slog( LOG_ERROR, "Byte mistach: expected %02x got %02x",
			     byteval, *data );
		    return -1;
		}
		data++;
		break;



	    /* Decode a long word which should be constant */
	    case 'L':
		longexp = va_arg( args, int );
		// slog( LOG_DETAIL, "Expect long value: 0x%08x", longexp );

		longval = (data[0] << 24) | (data[1] << 16) |
		    (data[2] << 8) | data[3];
		data += 4;
		if( longexp != longval )
		{
		    slog( LOG_ERROR, "Long mistach: expected %04x got %04x",
			     longexp, longval );
		    return -1;
		}
		break;
		
	    /* Wildcard byte */
	    case '?':
		// slog( LOG_DETAIL, "Wildcard: %02x", *data );
		data++;
		break;
		
	    case 'S':
	    /* 'Decode' string data into a buffer... should be a 2 byte
	       count followed by that many bytes of data... same as the
	       D case except we terminate the string
	    */
		stringmode = 1;
		/* FALL THROUGH */
		
	    /* 'Decode' binary data into a buffer... should be a 2 byte
	       count followed by that many bytes of data */
	    case 'D':
		outbuf = va_arg( args, uint8 * );
		outbufsize = va_arg( args, int );
		length = (data[0] << 8) | data[1];
		data += 2;
		if( stringmode )
		{
		    outbufsize--;
		}
		if( length > outbufsize )
		{
		    slog( LOG_ERROR,
			     "Decode: received data too big for buffer %d vs %d",
			     length, outbufsize);
		    return -1;
		}
		// slog( LOG_DETAIL, "Decode data blob, %04x bytes", length );
		/* XXX Never seen one longer than 255, but you never know...*/
		if( length > 255 )
		{
		    slog( LOG_ERROR,
			     "Data blob too long, claims to be %d bytes",
			     length );
		    return -1;
		}
		if( length > cmdbuf->used - (data - cmdbuf->buffer))
		{
		    slog( LOG_ERROR,
			     "Data blob underflow - Length %x, only %x left ",
			     length, cmdbuf->used - (data - cmdbuf->buffer) );
		    return -1;
		}
		memcpy( outbuf, data, length );
		/* If it's really a string, terminate it */
		if( stringmode )
		{
		    outbuf[length] = '\0';
		}
		data += length;
		break;


	    default:
		val1 = hexvalue( *fmt );
		if( val1 >= 0 )
		{
		    fmt++;
		    /* XXX check for end of string */
		    val2 = hexvalue( *fmt );
		    if( val2 >= 0 )
		    {
			byteval = ( val1 << 4 ) | val2;
			// slog( LOG_DETAIL, "Decode: Literal byte: 0x%02x", byteval);
			if( byteval != *data )
			{
			    slog(LOG_WARNING,
				    "Data mismatch in decode got 0x%02x not 0x%02x", *data, byteval );
			}
			data++;
			
			/* Success! */
			break;
		    }
		}
		slog( LOG_ERROR, "Invalid cmd format string! Got '%c'",
			 *fmt );
		return -1;
	}
    }
    return 0;
}

/*****************************************************************************
 * Function to decode a netmd reply
 */
int netmd_decode_response( netmd_cmd_buf_t *cmdbuf, const char *fmt, ... )
{
    va_list args;

    va_start( args, fmt );

    return netmd_vdecode_response( cmdbuf, fmt, args );
}

/*****************************************************************************
 * Private function to poll for replies from unit
 *
 * Unit sends a 4 byte code either 00 00 00 00 (no reply? seems to occur
 * during long operations like disk initialise) or 01 81 nn 00 where
 * nn is the number of bytes to transfer back
 *
 * XXX Currently polls a fixed number of times, should be changed to
 * calculate a timeout
 *
 * Return:-
 *	0	No reply
 *      <0	Error
 *	>0	Length waiting to be transferred in
 */
static int netmd_poll_for_reply( omd_unit_t *unit, int retries )
{
    int 	i;
    uint8	reply[4];
    uint32	delay = 10000;		/* 20ms initial delay */
    uint32	maxdelay = 250000;	/* 250ms initial delay */
    
    for( i = 0; i < retries; i++ )
    {
	if( usb_control_msg( unit->handle,
			     USB_ENDPOINT_IN | USB_TYPE_VENDOR |
			     USB_RECIP_INTERFACE,
			     0x01, 0x00, 0x00,  /* Vendor request, val, idx */
			     reply, 4, 		/* Data buffer and length */
			     5000 ) < 0 )       /* Timeout in ms */
	{
	    slog( LOG_ERROR, "Reply poll failed: %s", usb_strerror );
	    return -1;
	}
	if( (reply[0] == 0x00) && (reply[1] == 0x00) &&
	    (reply[2] == 0x00) && (reply[3] == 0x00))
	{
	    slog( LOG_DETAIL, "Poll: No reply yet, pass %d", i + 1 );
	    /* Exponential backoff... XXX experimental! */
	    usleep( delay );
	    delay *= 2;
	    if( delay > maxdelay )
	    {
		delay = maxdelay;
	    }
	}
	else if( (reply[0] == 0x01) && (reply[1] == 0x81) &&
		 (reply[3] == 0x00))
	{
	    return reply[2];
	}
	else
	{
	    slog( LOG_ERROR, "Unexpected poll reply!" );
	    return -1;
	}
    }
    /* Timed out or no reply */
    return 0;
}


/*****************************************************************************
 * Send a command to the unit
 */
int netmd_send_command( omd_unit_t *unit, netmd_cmd_buf_t *sndbuf,
			netmd_cmd_buf_t *rcvbuf )
{
    int	replylen;
    
    if( ( sndbuf == NULL ) || ( sndbuf->buffer == NULL ) ||
	( rcvbuf == NULL ) || ( rcvbuf->buffer == NULL ) ||
	( unit == NULL ))
    {
	slog( LOG_ERROR, "netmd_send_command: Invalid buffers" );
	return -1;
    }

    /* Send command */
    slog( LOG_DETAIL, "Sending..." );
    if( usb_control_msg( unit->handle,
			 USB_ENDPOINT_OUT | USB_TYPE_VENDOR |
			 USB_RECIP_INTERFACE,
			 0x80, 0x00, 0x00,  /* Vendor request, value, index */
			 sndbuf->buffer, sndbuf->used, /* Data */
			 5000 ) < 0 )       /* Timeout in ms */
    {
	slog( LOG_ERROR, "Send type 0x80 failed: %s", usb_strerror() );
	return -1;
    }

    /* Get the reply size when the unit is ready */
    slog( LOG_DETAIL, "Polling..." );
    replylen = netmd_poll_for_reply( unit, 12 );
    if( replylen <= 0 )
    {
	slog( LOG_ERROR, "No reply from unit within timeout" );
	return -1;
    }
    if( replylen > rcvbuf->size )
    {
	slog( LOG_ERROR, "Reply too big for command buffer: %d vs %d",
		 replylen, rcvbuf->size );
	return -1;
    }

    /* Zero out reply buffer */
    rcvbuf->used = 0;
    /* Receive reply */
    slog( LOG_DETAIL, "Receiving..." );
    if( usb_control_msg( unit->handle,
			 USB_ENDPOINT_IN | USB_TYPE_VENDOR |
			 USB_RECIP_INTERFACE,
			 0x81, 0x00, 0x00,  /* Vendor request, value, index */
			 rcvbuf->buffer, replylen, /* Data */
			 5000 ) < 0 )       /* Timeout in ms */
    {
	slog( LOG_ERROR, "receive type 0x81 failed: %s", usb_strerror );
	return -1;
    }
    rcvbuf->used = replylen;

    /*
     * Open MG seems to poll the unit again after receving the reply
     * and always receives 0... libnetmd doesn't seem to do this.
     * We do it here for completeness.
     */
    slog( LOG_DETAIL, "Polling..." );
    if( netmd_poll_for_reply( unit, 1 ) != 0 )
    {
	slog( LOG_WARNING, "Unit poll didn't return 0" );
    }


    /* Quick sanity checks */
    if( rcvbuf->buffer[0] == 0x08 )
    {
	slog( LOG_WARNING, "*** NetMD Error? Reply started with 0x08" );
    }
    else if( rcvbuf->buffer[0] == 0x0a )
    {
	slog( LOG_DEBUG,"*** NetMD Warning: 0x0a response" );
    }
    else if( rcvbuf->buffer[0] != 0x09 )
    {
	slog( LOG_WARNING,"*** NetMD Error? Reply starts with unknown val" );
    }
    
    return 0;
}

/*****************************************************************************
 * Send a simple (ish) command to the unit and get a reply
 *
 * Return:
 * Pointer to command buffer with raw reply. Caller must free this
 * with netmd_free_buffer()
 */
netmd_cmd_buf_t *netmd_simple_command( omd_unit_t *unit, int max,
					      const char *fmt, ... )
{
    netmd_cmd_buf_t	*cmdbuf;
    va_list		args;

    if( ( cmdbuf = netmd_alloc_buffer( max )) == NULL )
    {
	slog( LOG_ERROR, "netmd_simple_command: Unable to alloc buffer");
	return NULL;
    }

    /* Build the command buffer to send */
    va_start( args, fmt );

    if( netmd_vbuild_command( cmdbuf, fmt, args ) < 0 )
    {
	slog( LOG_ERROR, "netmd_simple_command: Unable to build command");
	netmd_free_buffer( cmdbuf );
	return NULL;
    }

    /* Debug */
    slog_hex( LOG_TRACE, "cmd-> ", cmdbuf->buffer, cmdbuf->used );

    /*
     * actually send the command
     */
    if( netmd_send_command( unit, cmdbuf, cmdbuf ) < 0 )
    {
	slog( LOG_ERROR,"netmd_simple_command: netmd_send_command failed");
	netmd_free_buffer( cmdbuf );
	return NULL;
    }

    /* Debug */
    slog_hex( LOG_TRACE, "<-rsp ", cmdbuf->buffer, cmdbuf->used );

    return cmdbuf;
}


/*****************************************************************************
 * Command to get unit status - Buffer must be at least 7 bytes long
 */

int netmd_unit_status( omd_unit_t *unit, uint8 *buffer, int bufsize )
{
    int	rc = -1,
	p1 = 0x8800,
	p2 = 0x0030,
	p3 = 0x8804,
	p4 = 0x00,
	p5 = 0x0000;

    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001809 80010230 WWWB ff 00 L",
			      p1, p2, p3, p4, p5 );

    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf,
		"091809 80010230 WWWB 10 00 00 ? 00 00 D",
		p1, p2, p3, p4, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_unit_status_cmd: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}


/*****************************************************************************
 * Common code from both playback status commands
 */
static int netmd_playback_status( omd_unit_t *unit, int p1, int p5,
				 uint8 *buffer, int bufsize )
{
    int rc = -1,
	p2 = 0x0030,
	p3 = 0x8805,
	p4 = 0x0030,
	p6 = 0x00,
	p7 = 0x0000;

    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001809 80010330 WWWWWB ff 00 L",
			      p1,p2, p3, p4, p5, p6, p7 );

    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf,
		"091809 80010330 WWWWWB 10 00 00 ? 00 00 D",
		p1, p2, p3, p4, p5, p6, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_unit_status_cmd: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}



/*****************************************************************************
 * Command to get playback status A - Buffer must be at least 12 bytes long
 */

int netmd_playback_status_a( omd_unit_t *unit, uint8 *buffer, int bufsize )
{

    return netmd_playback_status( unit, 0x8801, 0x8807, buffer, bufsize );
}

/*****************************************************************************
 * Command to get playback status B - Buffer must be at least 6 bytes long
 */
int netmd_playback_status_b( omd_unit_t *unit, uint8 *buffer, int bufsize )
{

    return netmd_playback_status( unit, 0x8802, 0x8806, buffer, bufsize );
}


/*****************************************************************************
 * Command to get playback position - Buffer must be at least 11 bytes long
 */
int netmd_get_playback_position( omd_unit_t *unit, uint8 *buffer, int bufsize )
{
    int rc = -1,
	p1 = 0x8802,
	p2 = 0x0030,
	p3 = 0x8805,
	p4 = 0x0030,
	p5 = 0x0003,
	p6 = 0x0030,
	p7 = 0x0002,
	p8 = 0x00,
	p9 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001809 80010430 WWWWWWWB ff 00 L",
			      p1, p2, p3, p4, p5, p6, p7, p8, p9 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf,
		/* We might get 0a as a return if blank disc */
		"? 1809 80010430 WWWWWWWB ? 00 00 ? 00 00 D",
		p1, p2, p3, p4, p5, p6, p7, p8, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_unit_status_cmd: decode failed" );
	    rc = -1;
	}

	/* See if we got a valid response */
	if( cmdbuf->buffer[0] == 0x0a )
	{
	    slog( LOG_DEBUG, "Cannot determine playback position - fudging it");
	    memset( buffer, '\0', bufsize );
	}


	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

/*****************************************************************************
 * Common code for commands with constant send & reply strings
 */

int netmd_const_command( omd_unit_t *unit, const char *send,
			 const char *expect )
{
    int rc;
    netmd_cmd_buf_t *cmdbuf = netmd_simple_command( unit, 64, send );

    if( cmdbuf )
    {
	if( ( rc = netmd_decode_response( cmdbuf, expect )) < 0 )
	{
	    slog( LOG_ERROR, "Unexpected response, expected %s",
		     expect );
	}
	netmd_free_buffer( cmdbuf );
	return rc;
    }
    /* Error occurred during send */
    return -1;
}

/*****************************************************************************
 * Set playback track
 */
int netmd_set_playback_track( omd_unit_t *unit, uint16 track )
{
    int rc = -1,
	p1 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 32, "001850 ff010000 WW",
			      p1, track );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(cmdbuf, "091850 00010000 WW",
				  p1, track ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_set_playback_track: command failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

/*****************************************************************************
 * Set track and position - position is 4 byte BCD
 */
int netmd_set_playback_position( omd_unit_t *unit, uint16 track, uint32 pos )
{
    int rc = -1,
	p1 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 32, "001850 ff000000 WWL",
			      p1, track, pos );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(cmdbuf, "091850 00000000 WWL",
				  p1, track, pos ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_set_playback_position: command failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}



/*****************************************************************************
 * Start playback
 */

int netmd_start_playback( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"0018c3 ff750000 00",
				"0918c3 00750000 00" );
}

/*****************************************************************************
 * Pause playback
 */

int netmd_pause_playback( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"0018c3 ff7d0000 00",
				"0918c3 007d0000 00" );
}

/*****************************************************************************
 * Start fast forward
 */

int netmd_start_fforward( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"0018c3 ff390000 00",
				"0918c3 00390000 00" );
}

/*****************************************************************************
 * Start rewind
 */

int netmd_start_rewind( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"0018c3 ff490000 00",
				"0918c3 00490000 00" );
}

/*****************************************************************************
 * Stop playback
 */

int netmd_stop_playback( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"0018c5 ff000000 00",
				"0918c5 00000000 00" );
}

/*****************************************************************************
 * Disc initialise
 */

int netmd_disc_init( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"001840 ff0000",
				"091840 000000" );
}



int netmd_toc_sync( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"001808 10180200 00",
				"091808 10180200 00" );
}
int netmd_toc_cache( omd_unit_t *unit )
{
    return netmd_const_command( unit,
				"001808 10180203 00",
				"091808 10180203 00" );
}


/*****************************************************************************
 * Get disc flags
 */

int netmd_disc_flags( omd_unit_t *unit, uint8 *flagbuf )
{
    int rc = -1,
	p1 = 0x0001000b;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001806 01101000 ff 00 L", p1 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf,
		"091806 01101000 10 00 L ?", p1 ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_disc_flags: decode failed" );
	    rc = -1;
	}
	*flagbuf = cmdbuf->buffer[13];
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}


/*****************************************************************************
 * Get track count
 */

int netmd_track_count( omd_unit_t *unit, uint8 *buffer, int bufsize )
{
    int rc = -1,
	p1 = 0x3000,
	p2 = 0x1000,
	p3 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001806 02101001 WW ff 00 L",
			      p1, p2, p3 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091806 02101001 WW 10 00 00 ? 00 00 D",
		p1, p2, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_count_info: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

/*****************************************************************************
 * Get disc capacity
 */

int netmd_disc_capacity( omd_unit_t *unit, uint8 *buffer, int bufsize )
{
    int rc = -1,
	p1 = 0x3080,
	p2 = 0x0300,
	p3 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001806 02101000 WW ff 00 L",
			      p1, p2, p3 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091806 02101000 WW 10 00 00 ? 00 00 D",
		p1, p2, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_disc_capacity: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

/*****************************************************************************
 * Get disc title
 */

int netmd_disc_get_title( omd_unit_t *unit, uint8 *buffer, int bufsize )
{
    int rc = -1,
	p1 = 0x0000,
	p2 = 0x3000,
	p3 = 0x0a00,
	p4 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 300, "001806 02201801 WWW ff 00 L",
			      p1, p2, p3, p4 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091806 02201801 WWW 10 00 00 ? 00 00 00 ? 00 0a S",
		p1, p2, p3, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_disc_get_title: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

/*****************************************************************************
 * Read a track title
 */
int netmd_track_get_title( omd_unit_t *unit, int track,uint8 *buffer,int bufsize)
{
    int rc = -1,
	p2 = 0x3000,
	p3 = 0x0a00,
	p4 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 300, "001806 02201802 WWW ff 00 L",
			      track, p2, p3, p4 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091806 02201802 WWW 10 00 00 ? 00 00 00 ? 00 0a S",
		track, p2, p3, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_get_title: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}


/*****************************************************************************
 * Set a track title
 */

int netmd_track_set_title( omd_unit_t *unit, int track, int oldlen,
			   const char *title )
{
    int rc = -1,
	p2 = 0x3000,
	p3 = 0x0a00,
	p4 = 0x5000,
	p6 = 0x0000;
    int newlen;
    netmd_cmd_buf_t *cmdbuf;

    newlen = strlen( title );
    if( oldlen < 0 )
    {
	/* seems to work anyway */
	oldlen = newlen;
    }
    
    cmdbuf = netmd_simple_command( unit, 300, "001807 02201802 WWWWWWW D",
				   track, p2, p3, p4, newlen, p6, oldlen,
				   title, newlen);


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091807 02201802 WWWWWWW",
		track, p2, p3, p4, newlen, p6, oldlen ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_set_title: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}


/*****************************************************************************
 * Set disc title
 */

int netmd_disc_set_title( omd_unit_t *unit, int oldlen, const char *title )
{
    int rc = -1,
	p1 = 0x0000,
	p2 = 0x3000,
	p3 = 0x0a00,
	p4 = 0x5000,
	p6 = 0x0000;
    int newlen;
    netmd_cmd_buf_t *cmdbuf;

    newlen = strlen( title );
    if( oldlen < 0 )
    {
	/* seems to work anyway */
	oldlen = newlen;
    }
    
    cmdbuf = netmd_simple_command( unit, 300, "001807 02201801 WWWWWWW D",
				   p1, p2, p3, p4, newlen, p6, oldlen,
				   title, newlen);


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091807 02201801 WWWWWWW",
		p1, p2, p3, p4, newlen, p6, oldlen ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_disc_set_title: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}



/*****************************************************************************
 * Move a track
 */

int netmd_track_move( omd_unit_t *unit, int src, int dst )
{
    int rc = -1,
	p1 = 0x1001,
	p3 = 0x20,
	p4 = 0x1001;

    netmd_cmd_buf_t *cmdbuf;

    
    cmdbuf = netmd_simple_command( unit, 64, "001843 ff000020 WWBWW",
				   p1, src, p3, p4, dst);


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091843 00000020 WWBWW", p1, src, p3, p4, dst ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_move: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}


/*****************************************************************************
 * Delete a track
 */

int netmd_track_delete( omd_unit_t *unit, int track )
{
    int rc = -1,
	p1 = 0x1001;

    netmd_cmd_buf_t *cmdbuf;

    
    cmdbuf = netmd_simple_command( unit, 64, "001840 ff010020 WW",
				   p1, track );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091840 00010020 WW", p1, track ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_delete: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}



/*****************************************************************************
 * Get track info (length or bitrate )
 */
int netmd_track_get_info( omd_unit_t *unit, int track, int p2, int p3,
			  uint8 *buffer, int bufsize)
{
    int rc = -1,
	p4 = 0x0000;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 300, "001806 02201001 WWW ff 00 L",
			      track, p2, p3, p4 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf, "091806 02201001 WWW 10 00 00 ? 00 00 D",
		track, p2, p3, buffer, bufsize ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_get_info: decode failed" );
	    rc = -1;
	}
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}



/*****************************************************************************
 * Read track length
 */
int netmd_track_get_length( omd_unit_t *unit,int track,uint8 *buffer,int bufsize)
{
    int p2 = 0x3000,
	p3 = 0x0100;
    
    return netmd_track_get_info( unit, track, p2, p3, buffer, bufsize );
}


/*****************************************************************************
 * Read track bitrate
 */
int netmd_track_get_bitrate( omd_unit_t *unit,int track, uint8 *buffer,
			     int bufsize)
{
    int p2 = 0x3080,
	p3 = 0x0700;
    
    return netmd_track_get_info( unit, track, p2, p3, buffer, bufsize );
}

/*****************************************************************************
 * Get track flags
 *
 *  Not very sure about this one, but 03 seems to mean the track is checked
 *  out, ie locked
 */
int netmd_track_get_flags( omd_unit_t *unit, int track, uint8 *flagbuf)
{
    int rc = -1,
	p1 = 0x00010008;
    
    netmd_cmd_buf_t *cmdbuf =
	netmd_simple_command( unit, 64, "001806 01201001 W ff 00 L",
			      track, p1 );


    if( cmdbuf )
    {
	rc = 0;
	if( netmd_decode_response(
		cmdbuf,
		"091806 01201001 W 10 00 L ?", track, p1 ) < 0 )
	{
	    slog( LOG_ERROR, "netmd_track_get_flags: decode failed" );
	    rc = -1;
	}
	*flagbuf = cmdbuf->buffer[15];
	netmd_free_buffer( cmdbuf );
    }
    /* Error occurred during send */
    return rc;
}

