/*
* Copyright 2000-2001, Brian Beattie <beattie@aracnet.com>, et. al.
*
*	This software was created with the help of proprietary
*      information belonging to StarDot Technologies
*
* Permission to copy and use and modify this file is granted
* provided the above notices are retained, intact.
*
* Revision History:
*	See CVS log for later revisions
*
*	2001/03/10 - Port to gphoto2				  	  Dan
*
*	2000/09/06 - Modified by Gregg Berg <gberg@covconn.net>		  GDB
*		* Rewrite mesa_read_row() to use mesa_image_arg		  GDB
*		* Add checksum to mesa_read_row()			  GDB
*		* Add return with number of bytes to mesa_read_image()	  GDB
*									  GDB
* $Id$
*/
#include "config.h"

#include "mesalib.h"

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <string.h>

#include <gphoto2/gphoto2.h>
#include "libgphoto2/gphoto2-endian.h"

#define GP_MODULE "dimera"
#define debuglog(e) GP_DEBUG( "%s", (e))

#ifdef CONVERT_PIXELS
static const uint16_t	pixelTable[256] = {
	0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
	0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
	0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017,
	0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F,
	0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027,
	0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F,
	0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037,
	0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F,
	0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047,

	0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04E, 0x04F, 0x050,
	0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058,
	0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F, 0x060,
	0x061, 0x062, 0x063, 0x064, 0x066, 0x067, 0x068, 0x069,
	0x06A, 0x06C, 0x06D, 0x06E, 0x06F, 0x070, 0x072, 0x073,
	0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B,
	0x07C, 0x07E, 0x07F, 0x081, 0x082, 0x084, 0x085, 0x087,

	0x088, 0x08A, 0x08B, 0x08C, 0x08E, 0x08F, 0x090, 0x092,
	0x093, 0x094, 0x096, 0x098, 0x09A, 0x09C, 0x09E, 0x0A0,
	0x0A2, 0x0A3, 0x0A5, 0x0A6, 0x0A8, 0x0A9, 0x0AB, 0x0AC,
	0x0AE, 0x0B0, 0x0B2, 0x0B4, 0x0B6, 0x0B8, 0x0BA, 0x0BC,

	0x0BE, 0x0C0, 0x0C2, 0x0C4, 0x0C6, 0x0C8, 0x0CA, 0x0CD,
	0x0CF, 0x0D2, 0x0D5, 0x0D8, 0x0DB, 0x0DE, 0x0E0, 0x0E2,
	0x0E5, 0x0E7, 0x0EA, 0x0EC, 0x0EE, 0x0F1, 0x0F3, 0x0F6,
	0x0FC, 0x102, 0x106, 0x10A, 0x10E, 0x111, 0x114, 0x117,

	0x11A, 0x11D, 0x120, 0x123, 0x126, 0x12C, 0x132, 0x138,
	0x13E, 0x142, 0x146, 0x14A, 0x150, 0x156, 0x15A, 0x15E,
	0x162, 0x166, 0x16A, 0x16E, 0x17A, 0x180, 0x186, 0x18C,
	0x192, 0x19E, 0x1AA, 0x1B0, 0x1B6, 0x1BA, 0x1BE, 0x1C2,

	0x1C8, 0x1CE, 0x1DA, 0x1E6, 0x1F2, 0x1FE, 0x204, 0x20A,
	0x216, 0x222, 0x22E, 0x23A, 0x240, 0x246, 0x24C, 0x252,

	0x25E, 0x26A, 0x276, 0x282, 0x28E, 0x2A6, 0x2B2, 0x2BE,
	0x2D6, 0x2EE, 0x2FA, 0x312, 0x336, 0x34E, 0x35e, 0x366
};
#endif

/*
 * return the difference in tenths of seconds, between t1 and t2.
 */
static long
timediff( struct timeval *t1, struct timeval *t2 )
{
	long	t;

	t = (t1->tv_sec - t2->tv_sec) * 10;
	t += (t1->tv_usec - t2->tv_usec) / 100000;
	return t;
}

/* flush input.  Wait for timeout deci-seconds to pass without input */

void
mesa_flush( GPPort *port, int timeout )
{
	uint8_t	b[256];
	struct timeval	start, now;

	gettimeofday( &start, NULL );

	/* Clear any pending input bytes */
	gp_port_flush(port, 0);

	/* Wait for silence */
	do {
		if ( gp_port_read( port, (char *)b, sizeof( b ) ) > 0 )
			/* reset timer */
			gettimeofday( &start, NULL );
		gettimeofday( &now, NULL );
	} while ( timeout > timediff( &now, &start ) );
	return;
}

/* Read exactly this number of bytes from the port within the given timeouts */
unsigned int
mesa_read( GPPort *port, uint8_t *b, int s, int timeout2, int timeout1 )
{
	int		n = 0;
	int		r, t;
	struct timeval	start, now;

	t = timeout1 ? timeout1 : timeout2;	/* set first byte timeout */
	gettimeofday( &start, NULL );

	do
	{
		/* limit reads to 1k segment */
		r = gp_port_read( port, (char *)&b[n], s>1024?1024:s );
		if ( r > 0 )
		{
			n += r;
			s -= r;
			gettimeofday( &start, NULL );
			t = timeout2;
		}
		gettimeofday( &now, NULL );
	} while ( s > 0 && t > timediff( &now, &start ) );
	return n;
}

/* Send a command to the camera and read the acknowledgement byte */
int
mesa_send_command( GPPort *port, uint8_t *cmd, int n, int ackTimeout )
{
	uint8_t	c;

	CHECK (gp_port_write( port, (char *)cmd, n ));

	if ( mesa_read( port, &c, 1, ackTimeout, 0 ) != 1 )
	{
		debuglog("mesa_send_command: timeout");
		return GP_ERROR_TIMEOUT;
	}
	if ( c != CMD_ACK )
	{
		debuglog("mesa_send_command: error response");
		return GP_ERROR_CORRUPTED_DATA;
	}
	return GP_OK;
}

/* Open the serial port and configure it */
int
mesa_port_open( GPPort *port )
{
	GPPortSettings settings;

	debuglog("mesa_port_open()");
	gp_port_set_timeout(port, 5000);

	gp_port_get_settings(port, &settings);

	settings.serial.speed   = 115200;
	settings.serial.bits    = 8;
	settings.serial.parity  = 0;
	settings.serial.stopbits= 1;

	return gp_port_set_settings(port, settings);
}

/* Close the serial port (now done by gphoto2 itself) */
int
mesa_port_close( GPPort *port )
{
	return GP_OK;
}

/* reset camera */
int
mesa_reset( GPPort *port )
{
	return gp_port_send_break( port, 1 );
}

/* set camera serial port speed, then our local port speed */
int
mesa_set_speed( GPPort *port, int speed )
{
	uint8_t	b[2];
	gp_port_settings settings;

	if (speed == 0)
		speed = 115200;		/* use default speed */

	GP_DEBUG(
	 "mesa_set_speed: speed %d", speed);

	b[0] = SET_SPEED;

	switch( speed )
	{
	case 9600:
		b[1] = 1;
		break;
	case 14400:
		b[1] = 2;
		break;
	case 19200:
		b[1] = 3;
		break;
	case 38400:
		b[1] = 4;
		break;
	case 57600:
		b[1] = 5;
		break;
	case 76800:
		b[1] = 6;
		break;
	case 115200:
		b[1] = 7;
		break;
	case 230400:
		b[1] = 8;
		break;
	case 460800:
		b[1] = 9;
		break;
	default:
		return GP_ERROR_BAD_PARAMETERS;
	}
	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	gp_port_get_settings(port, &settings);
	settings.serial.speed = speed;
	return gp_port_set_settings(port, settings);
}

/* get camera version number */
int
mesa_version( GPPort *port, char *version_string)
{
	uint8_t		b;
	uint8_t		r[3];
	int		i;
	char		*v = version_string;

	b = MESA_VERSION;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	if ( ( i = mesa_read( port, r, sizeof( r ), 10, 0 ) ) != sizeof( r ) )
	{
		return GP_ERROR_TIMEOUT;
	}

	GP_DEBUG(
	 "mesa_version: %02x:%02x:%02x\n", r[0], r[1], r[2] );
	sprintf(v, "%2x.%02x%c", r[1], r[0], r[2]);
	/* highest byte must be < MESA_VERSION_SZ */
	return GP_OK;
}

/* test camera serial port transmit */
int
mesa_transmit_test( GPPort *port )
{
	uint8_t		b;
	uint8_t		r[256];
	unsigned int	i;

	b = XMIT_TEST;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	if ( mesa_read( port, r, sizeof( r ), 10, 0 ) != sizeof( r ) )
	{
		return GP_ERROR_TIMEOUT;
	}

	for ( i = 0; i < 256; i++ )
	{
		if ( r[i] != i )
			return GP_ERROR_CORRUPTED_DATA;
	}
	return GP_OK;
}

/* test camera ram */
int
mesa_ram_test( GPPort *port )
{
	uint8_t	b;
	uint8_t	r;

	b = RAM_TEST;

	CHECK (mesa_send_command( port, &b, 1, 100 ));

	if ( mesa_read( port, &r, 1, 10, 0 ) != 1 )
	{
		return GP_ERROR_TIMEOUT;
	}

	return (int)r;
}

/*
 * Read image row from camera.
 * ********************************************************************   GDB
 * *   The following contradicted by traces from my Win98 twain driver*	  GDB
 * ********************************************************************   GDB
 * The image is stored in the camera as 504 row (0 - 503), the first 8 and the
 * last 2 are blank.
 * The rows are 18 dummy pixels, 2 blanks, 646 pixels, 13 bytes garbage,
 * 2 bytes blanks
 *
 * start  - offset into row;
 * send   - bytes to send;
 * skip   - bytes to skip;
 * repeat - # repeats of send skip
 * 640 bytes start = 28, send = 2, skip = 0, repeat = 320
 * 320 bytes start = 28, send = 2, skip = 2, repeat = 160
 *
 * return number of bytes read, or < 0 if an error was detected.
 *									  GDB
 * ********************************************************************   GDB
 *									  GDB
 * 640 bytes start = 30, send = 4, skip = 0, repeat = 160		  GDB
 * 320 bytes start = 30, send = 4, skip = 0, repeat = 80		  GDB
 *									  GDB
 * return value	>0 number of bytes read					  GDB
 *		 GP_ERROR_TIMEOUT read time-out
 *		 GP_ERROR_BAD_PARAMETERS bad values
 *		 GP_ERROR command failure
 *		 GP_ERROR_CORRUPTED_DATA checksum failure
 */

int									/*GDB*/
mesa_read_row( GPPort *port, uint8_t *r, struct mesa_image_arg *s )	/*GDB*/
{
	uint8_t		b[9];
	unsigned int	bytes, i;					/*GDB*/
	uint8_t		checksum = 0;					/*GDB*/

	if ( (bytes = s->send * s->repeat) > 680 )			/*GDB*/
	{
		return GP_ERROR_BAD_PARAMETERS;
	}

	b[0] = SEND_RAM;
	htole16a(&b[1], s->row);
	htole16a(&b[3], s->start);
	b[5] = s->send;
	b[6] = s->skip;
	htole16a(&b[7], s->repeat);

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

/*	return ( mesa_read( port, r, n, 10, 0 ));			  GDB*/
	if ( mesa_read( port, r, bytes, 10, 0 ) != bytes )		/*GDB*/
	{								/*GDB*/
		return GP_ERROR_TIMEOUT;
	}								/*GDB*/
									/*GDB*/
	if ( mesa_read( port, b, 1, 10, 0 ) != 1) 			/*GDB*/
	{								/*GDB*/
		return GP_ERROR_TIMEOUT;
	}								/*GDB*/
									/*GDB*/
	for (i = 0; i < bytes; i++ )					/*GDB*/
	{								/*GDB*/
		checksum += r[i];					/*GDB*/
	}								/*GDB*/
									/*GDB*/
	if ( checksum != b[0] )						/*GDB*/
	{								/*GDB*/
		return GP_ERROR_CORRUPTED_DATA;
	}								/*GDB*/
									/*GDB*/
	return bytes;							/*GDB*/
}

/*
 * Snaps a full resolution image, with no flash, or shutter click
 *
 * exposure is in units of 1/50000 seconds
 */
int
mesa_snap_image( GPPort *port, uint16_t exposure )
{
	uint8_t		b[3];
	int		timeout;

	if ( exposure )
		timeout = (exposure/50000) + 10;
	else
		timeout = 10;

	b[0] = SNAP_IMAGE;
	htole16a(&b[1], exposure);
	return mesa_send_command( port, b, sizeof( b ), timeout );
}

/* return black levels, overwrites image memory */
int
mesa_black_levels( GPPort *port, uint8_t r[2] )
{
	uint8_t	b;

	b = SND_BLACK;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	return mesa_read( port, r, 2, 10, 0 );
}

/*
 * snap a view finder image, optionally return image in buffer.
 *
 * if hi_res is true, image is 128x96 packed 4 bits per pixel (6144 bytes).
 * else               image is 64x48 packed 4 bits per pixel (1536 bytes).
 *
 * the image can be full scale, 0, zoom 2x 1, zoom 4x, 2, and for
 * low res images, 8x, 3.
 *
 * row and column may be offset 0-197 and 0-223 for zoom.
 *
 * exposure is * 1/50000 seconds.
 *
 * download specifies how, of if the image is downloaded.
 * 0-47		returns one 32 byte (64 pixel) row.
 * 128-223	returns on 64 byte row.
 * 249		returns all odd rows of a hi-res image 1536 bytes.
 * 250		returns all even rows of a hi-res image 1536 bytes.
 * 251		returns complete hi res image 6144 bytes.
 * 252		returns nothing.
 * 253		returns all odd rows of low res image 768 bytes.
 * 254		returns all even rows of low res image 768 bytes.
 * 255		returns entire image of 1536 bytes.
 *
 */
int
mesa_snap_view( GPPort *port, uint8_t *r, unsigned int hi_res,
	unsigned int zoom, unsigned int row, unsigned int col, uint16_t exposure,
	uint8_t download )
{
	unsigned int	timeout, i;
	unsigned int	bytes = 0;
	uint8_t		b[7], cksum;

	if ( download <= 47 )
		bytes = 32;
	else if ( download >= 48 && download <= 127 )
		return GP_ERROR_BAD_PARAMETERS;
	else if ( download >= 128 && download <= 223 )
		bytes = 64;
	else if ( download >= 224 && download <= 248 )
		return GP_ERROR_BAD_PARAMETERS;
	else if ( download == 249 || download == 250 )
		bytes = 1536;
	else if ( download == 251 )
		bytes = 6144;
	else if ( download == 252 )
		bytes = 0;
	else if ( download == 253 || download == 254 )
		bytes = 768;
	else /* if ( download == 255 ) */
		bytes = 1536;

	if ( bytes != 0 && r == NULL )
	{
		return GP_ERROR_BAD_PARAMETERS;
	}

	if ( exposure )
		timeout = (exposure/50000) + 10;
	else
		timeout = 10;

	b[0] = SNAP_VIEW;
	b[1] = (zoom&3)+(hi_res?0x80:0);
	b[2] = row;
	b[3] = col;
	htole16a(&b[4], exposure);
	b[6] = download;

	CHECK (mesa_send_command( port, b, sizeof( b ), timeout ));

	if ( bytes != 0 )
	{
		if ( mesa_read( port, r, bytes, 10, 0 ) != bytes )
		{
			return GP_ERROR_TIMEOUT;
		}

		if ( mesa_read( port, b, 1, 10, 0 ) != 1 )
		{
			return GP_ERROR_TIMEOUT;
		}

		for ( i = 0, cksum = 0; i < bytes; i++ )
		{
			cksum += r[i];
		}
		if ( cksum != b[0] )
		{
			return GP_ERROR_CORRUPTED_DATA;
		}
	}

	return bytes;
}

/*
 * tell camera to insert extra stop bits between bytes.
 * slows down transmission without using a slower speed.
 */
int
mesa_set_stopbits( GPPort *port, unsigned int bits )
{
	uint8_t	b[2];

	b[0] = XTRA_STP_BIT;
	b[1] = bits;

	return mesa_send_command( port, b, sizeof( b ), 10 );
}

/* download viewfinder image ( see mesa_snap_view ) */
int
mesa_download_view( GPPort *port, uint8_t *r, uint8_t download )
{
	unsigned int	bytes, i;
	uint8_t		b[2], cksum;

	if ( download <= 47 )
		bytes = 32;
	else if ( download >= 48 && download <= 127 )
		return GP_ERROR_BAD_PARAMETERS;
	else if ( download >= 128 && download <= 223 )
		bytes = 64;
	else if ( download >= 224 && download <= 248 )
		return GP_ERROR_BAD_PARAMETERS;
	else if ( download == 249 )
		bytes = 1536;
	else if ( download == 250 || download == 251 )
		bytes = 768;
	else if ( download == 252 )
		bytes = 0;
	else if ( download == 253 )
		bytes = 6144;
	else /* if ( download == 254 || download == 255 ) */
		bytes = 1536;

	if ( bytes != 0 && r == NULL )
	{
		return GP_ERROR_BAD_PARAMETERS;
	}

	b[0] = SND_VIEW;
	b[1] = download;

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	if ( bytes != 0 )
	{
		if ( mesa_read( port, r, bytes, 10, 0 ) != bytes )
		{
			return GP_ERROR_TIMEOUT;
		}

		if ( mesa_read( port, b, 1, 10, 0 ) != 1 )
		{
			return GP_ERROR_TIMEOUT;
		}

		for ( i = 0, cksum = 0; i < bytes; i++ )
		{
			cksum += r[i];
		}
		if ( cksum != b[0] )
			return GP_ERROR_CORRUPTED_DATA;
	}

	return bytes;
}

/*
 * take a picture, with flash and shutter click
 */
int
mesa_snap_picture( GPPort *port, uint16_t exposure )
{
	unsigned int	timeout;
	uint8_t		b[3];

	if ( exposure )
		timeout = (exposure/50000) + 10;
	else
		timeout = 10;

	b[0] = SNAP_PICTURE;
	htole16a(&b[1], exposure);

	return mesa_send_command( port, b, sizeof( b ), timeout );
}

/* Get the camera chipset identification */
int
mesa_send_id( GPPort *port, struct mesa_id *id )
{
	uint8_t		b, r[4];

	b = SND_ID;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	if ( mesa_read( port, r, sizeof( r ), 10, 0 ) != sizeof( r ) )
	{
		return GP_ERROR_TIMEOUT;
	}

	id->man = r[0] + ((r[1]&15)<<8);
	id->ver = r[1]>>4;
	id->year = 1996 + r[2];
	id->week = r[3];

	return GP_OK;
}

/*
 * check for a camera, or a modem, send AT, if the response is 0x21,
 * it is a camera, if it is "AT" it's a modem.
 *
 * returns	 GP_OK = camera
 *		 GP_ERROR_MODEL_NOT_FOUND = modem
 *		 other = error
 */
int
mesa_modem_check( GPPort *port )
{
	uint8_t 	b[3];

	b[0] = 'A';
	b[1] = 'T';
	b[2] = '\r';

	CHECK (gp_port_write( port, (char *)b, sizeof( b ) ));

	/* Expect at least one byte */
	if ( mesa_read( port, b, 1, 5, 0 ) < 1 )
	{
		return GP_ERROR_TIMEOUT;
	}

	if ( b[0] == CMD_ACK )
		return GP_OK;

	/* Anything past this point results in an error */
	if ( mesa_read( port, b+1, sizeof(b) - 1, 2, 2 ) == sizeof(b) - 1 )
	{
		if ( b[0] == 'A' && b[1] == 'T' )
		{
			mesa_flush( port, 10 );
			return GP_ERROR_MODEL_NOT_FOUND;
		}
	}

	mesa_flush( port, 10 );
	return GP_ERROR;
}

/*
 * mesa_read_image	- returns all or part of an image in the supplied buffer
 * 	uint8_t		*r, buffer to return image in.
 *	struct mesa_image_arg	*i, download specifier.
 *
 *	return value	>0 number of bytes read
 *			GP_ERROR_TIMEOUT read time-out
 *			GP_ERROR command failure
 *			GP_ERROR_CORRUPTED_DATA checksum failure
 */

int
mesa_read_image( GPPort *port, uint8_t *r, struct mesa_image_arg *s )
{
	uint8_t		b[14];
	unsigned long	bytes, i;
	uint8_t		checksum = 0;

	bytes = s->row_cnt * (s->repeat * s->send);

	b[0]  = SND_IMAGE;
	htole16a(&b[1], s->row);
	htole16a(&b[3], s->start);
	b[5]  = s->send;
	b[6]  = s->skip;
	htole16a(&b[7], s->repeat);
	b[9]  = s->row_cnt;
	b[10] = s->inc1;
	b[11] = s->inc2;
	b[12] = s->inc3;
	b[13] = s->inc4;

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	if ( mesa_read( port, r, bytes, 10, 0 ) != bytes )
	{
		return GP_ERROR_TIMEOUT;
	}

	if ( mesa_read( port, b, 1, 10, 0 ) != 1 )
	{
		return GP_ERROR_TIMEOUT;
	}

	for ( i = 0; i < bytes; i++ )
	{
		checksum += r[ i ];
	}

	if ( checksum != b[0] )
	{
		return GP_ERROR_CORRUPTED_DATA;
	}

	return bytes;
}

/*
 * test serial link, send six bytes
 *
 * returns	GP_OK - success
 *		GP_ERROR_TIMEOUT - no data received
 *		GP_ERROR - command failed (no or bad ack)
 *		GP_ERROR_CORRUPTED_DATA - returned bytes do not match
 */
int
mesa_recv_test( GPPort *port, uint8_t r[6] )
{
	uint8_t		b[7];
	int		i;

	b[0] = RCV_TEST;
	memcpy( &b[1], r, 6 );

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	if ( mesa_read( port, r, 6, 10, 0 ) != 6 )
	{
		return GP_ERROR_TIMEOUT;
	}

	for ( i = 0; i < 6; i++ )
		if ( r[i] != b[i+1] )
			return GP_ERROR_CORRUPTED_DATA;
	return GP_OK;
}

/* Return the number of images in the EEPROM */
int
mesa_get_image_count( GPPort *port )
{
	uint8_t		b;
	uint8_t		r[2];

	b = IMAGE_CNT;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	if ( mesa_read( port, r, sizeof( r ), 10, 0 ) != sizeof( r ) )
	{
		return GP_ERROR_TIMEOUT;
	}

	return le16atoh(r);
}

/* load image from eeprom, into ram */
int
mesa_load_image( GPPort *port, int image )
{
	uint8_t		b[3];

	b[0] = LD_IMAGE;
	htole16a(&b[1], image);

	return mesa_send_command( port, b, sizeof( b ), 1000 );
}

/*
 * Read EEPROM information
 * Bytes 10..16 of info correspond to the Device ID field, bytes 45..51
 */
int
mesa_eeprom_info( GPPort *port, int long_read, uint8_t info[MESA_EEPROM_SZ] )
{
	uint8_t		b;

	b = EEPROM_INFO;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	return mesa_read( port, info, long_read ? 49 : 33, 10, 0 );
}

/*
 *
 */

int32_t
mesa_read_thumbnail( GPPort *port, int picture, uint8_t *image )
{
	uint8_t		b[3], checksum = 0;
	uint32_t	bytes;
	unsigned int	standard_res, i;

	b[0] = SND_THUMB;
	htole16a(&b[1], picture);

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	if ( mesa_read( port, b, 3, 10, 0 ) != 3 )
	{
		return GP_ERROR_TIMEOUT;
	}

	checksum = b[0] + b[1] + b[2];

	standard_res = ((b[2] & 128) != 0);
	bytes = b[0] + (b[1]<<8) + ((b[2] & 127)<<16);

	if ( mesa_read( port, image, MESA_THUMB_SZ, 10, 0 ) != MESA_THUMB_SZ )
	{
		return GP_ERROR_TIMEOUT;
	}

	if ( mesa_read( port, b, 1, 10, 0 ) != 1 )
	{
		return GP_ERROR_TIMEOUT;
	}

	for ( i = 0; i < MESA_THUMB_SZ; i++ )
	{
		checksum += image[i];
	}

	if ( checksum != b[0] )
		return GP_ERROR_CORRUPTED_DATA;

	return (bytes + standard_res ? 0x1000000 : 0);
}

/* Return the camera's feature bits */
int
mesa_read_features( GPPort *port, struct mesa_feature *f )
{
	uint8_t		b;

	b = FEATURES;

	CHECK (mesa_send_command( port, &b, 1, 10 ));

	return ( mesa_read( port, (uint8_t *)f, sizeof( *f ), 10, 0 ) );
}

/* return percentage battery level */
int
mesa_battery_check( GPPort *port )
{
	struct mesa_feature	f;
	int			l, r, rc;

	if ( (rc = mesa_read_features( port, &f )) != sizeof( f ))
	{
		return rc;
	}

	if ( (f.feature_bits_hi & BAT_VALID) == 0 )
		return GP_ERROR_MODEL_NOT_FOUND;

	/* (df) Does BAT_DIGITAL need to be checked here? */

	if ( ( l =  f.battery_level - f.battery_zero ) < 0 )
		l = 0;

	r = f.battery_full - f.battery_zero;

	return ( (l*100)/r );
}

int32_t
mesa_read_image_info( GPPort *port, int i, struct mesa_image_info *info )
{
	uint8_t		b[3], r[3];
	int32_t		bytes;
	int32_t		standard_res;

	b[0] = SND_IMG_INFO;
	htole16a(&b[1], i);

	CHECK (mesa_send_command( port, b, sizeof( b ), 10 ));

	if ( mesa_read( port, r, sizeof( r ), 10, 0 ) != sizeof( r ) )
	{
		return GP_ERROR_TIMEOUT;
	}

	standard_res = ((r[2] & 128) != 0);
	bytes = r[0] + (r[1]<<8) + ((r[2] & 127)<<16);

	if ( info != NULL )
	{
		info->standard_res = standard_res;
		info->num_bytes = bytes;
	}

	return standard_res;
}

#ifdef CONVERT_PIXELS
/*
 * return and image with raw pixels expanded.
 */
uint16_t *
mesa_get_image( GPPort *port, int image )
{
	static uint8_t			buffer[640*480L];
	static struct mesa_image_info	info;
	static struct mesa_image_arg	ia;
	uint8_t				*b = buffer;
	uint16_t			*rbuffer;
	int				r, res;
	unsigned long			size, i;

	if ( image != RAM_IMAGE_NUM )
	{
		if ( mesa_load_image( port, image ) != GP_OK )
		{
			mesa_flush( port, 100 );
			return NULL;
		}

		if ( mesa_read_image_info( port, image, &info ) < 0 )
		{
			mesa_flush( port, 100 );
			return NULL;
		}
		if ( info.standard_res )
		{
			res = 1;
			size = 320*240;
		} else {
			res = 0;
			size = 640*480;
		}
	} else {
		res = 0;
		size = 640*480;
	}
	rbuffer = (uint16_t *)malloc( size*(sizeof (uint16_t)) );
	if ( rbuffer == NULL )
		return NULL;

	ia.start = 28;
	ia.send = 4;
	ia.skip = 0;
	ia.repeat = (res ? 80 : 160);
	ia.row_cnt = 40;
	ia.inc1 = 1;
	ia.inc2 = 128;
	ia.inc3 = ia.inc4 = 0;

	for ( ia.row = 4; ia.row < (res ? 244 : 484) ; ia.row += ia.row_cnt )
	{

		if ( (r = mesa_read_image( port, b, &ia )) < 0 )
		{
			free( rbuffer );
			return NULL;
		}
		b += r;
	}

	for ( i = 0; i < size; i++ )
	{
		*rbuffer++ = pixelTable[*b++];
	}

	return rbuffer;
}
#else
/*
 * return and raw image retransmit on errors
 */
uint8_t *
mesa_get_image( GPPort *port, int image )
{
	static struct mesa_image_info	info;
	static struct mesa_image_arg	ia;
	uint8_t				*rbuffer, *b;
	int				r, res, retry;
	unsigned long			size;

	if ( image != RAM_IMAGE_NUM )
	{
		if ( mesa_load_image( port, image ) < 0 )
		{
			mesa_flush( port, 100 );
			return NULL;
		}

		if ( mesa_read_image_info( port, image, &info ) < 0 )
		{
			mesa_flush( port, 100 );
			return NULL;
		}
		if ( info.standard_res )
		{
			res = 1;
			size = 320*240;
		} else {
			res = 0;
			size = 640*480;
		}
	} else {
		res = 0;
		size = 640*480;
	}
	rbuffer = (uint8_t *)malloc( size );
	if ( rbuffer == NULL )
		return NULL;
	b = rbuffer;

	ia.start = 28;
	ia.send = 4;
	ia.skip = 0;
	ia.repeat = (res ? 80 : 160);
	ia.row_cnt = 40;
	ia.inc1 = 1;
	ia.inc2 = 128;
	ia.inc3 = ia.inc4 = 0;

	for ( ia.row = 4; ia.row < (res ? 244 : 484) ; ia.row += ia.row_cnt )
	{

		for ( retry = 10;; )
		{
			if ( (r = mesa_read_image( port, b, &ia )) > 0)
				break;

				/* if checksum error count retries */
			if ( r == -2  && --retry > 0)
				continue;	/* not exceeded, try again */

				/* error, exit */
			free( rbuffer );
			return NULL;

		}
		b += r;
	}

	return rbuffer;
}
#endif
