text: /*****************************************************************************
   *  Copyright (C) 2002  Jason Surprise <thesurprises1@attbi.com>             *
   *                2003  Enno Bartels   <ennobartels@t-online.de>             *
   *		  2005  Marcus Meissner <marcus@jet.franken.de>              *
   *****************************************************************************
   *                                                                           *
   *  This program is free software; you can redistribute it and/or            *
   *  modify it under the terms of the GNU Library General Public              *
   *  License as published by the Free Software Foundation; either             *
   *  version 2 of the License, or (at your option) any later version.         *
   *                                                                           *
   *  This program is distributed in the hope that it will be useful,          *
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
   *  Library General Public License for more details.                         *
   *                                                                           *
   *  You should have received a copy of the GNU Library General Public        *
   *  License along with this program; see the file COPYING.LIB.  If not,      *
   *  write to the 
   *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   *  Boston, MA  02110-1301  USA
   *                                                                           *
   *****************************************************************************/
  
  
  /*
      Old Maintainer : Enno Bartels <ennobartels@t-online.de> in 2002/2003
      Original author : Jason Surprise <thesurprises1@attbi.com> in 2002.
      Checksum author: Roberto Ragusa <r.ragusa@libero.it> in 2002/2003.
  
      Ported to libgphoto2: Marcus Meissner <marcus@jet.franken.de> in 2005.
  
      Camera commands:
       Global comands
                             Bytes   | 0    1cmd 2    3    4    5    6    7    8    9    10   11   12   13   14   15
      -------------------------------+----------------------------------------------------------------------------------
       init camera              8    | 0x02 0xce 0x80 0x8a 0x84 0x8d 0x83 0x03
       get camera date/time     8    | 0x02 0xc1 0x80 0x8b 0x84 0x8e 0x8d 0x03
       Get number of pics      16    | 0x02 0xc6 0x88 0x80 0x80 0x83 0x84 0x38 0x2f 0x30 0x32 0x88 0x84 0x8e 0x8b 0x03
       Delete all pics         12    | 0x02 0xb1 0x84 0x8f 0x8f 0x8f 0x8f 0x86 0x80 0x8a 0x86 0x03
  
  
       Single picture comands:
       Allways 12 bytes long
                                     | 0    1cmd 2    3    4    5    6    7    8    9    10    11
      -------------------------------+---------------------------------------------------------------
       Request for a preview         | 0x02 0xb3 0x84 0x80 ...                                 0x03
       Request for a picture         | 0x02 0xb4 0x84 0x80 ...                                 0x03
       Request date/time for preview | 0x02 0xc5 0x84 0x80 ...                                 0x03
       Request for deleting one pic  | 0x02 0xb1 0x84 0x80 ...                                 0x03
  
       0x02 = Startcode for a command
       0x03 = Endcode   for a command
   */
  
  #define _BSD_SOURCE
  
  #include "config.h"
  
  #include <stdio.h>
  #include <string.h>
  
  #include <sys/types.h>
  
  #include <fcntl.h>
  #include <stdlib.h>
  
  #include <gphoto2/gphoto2-port-log.h>
  #include <gphoto2/gphoto2-library.h>
  #include <gphoto2/gphoto2-result.h>
  
  #include "crctab.h"
  
  #ifdef ENABLE_NLS
  #  include <libintl.h>
  #  undef _
  #  define _(String) dgettext (GETTEXT_PACKAGE, String)
  #  ifdef gettext_noop
  #    define N_(String) gettext_noop (String)
  #  else
  #    define N_(String) (String)
  #  endif
  #else
  #  define _(String) (String)
  #  define N_(String) (String)
  #endif
  
  #include "hp215.h"
  
  enum hp215_cmd {
  	SET_TIME			= 0x86,
  	SET_TIME_AS_STR			= 0x87,
  	SET_TIME_MODE			= 0x88,
  	SET_TIME_PERIOD			= 0x89,
  	SET_XFER_PROTOCOL		= 0x8a,
  	SET_AUTO_SHUTDOWN_TIME		= 0x8b,
  	SET_PHOTO_NO_DISPLAY_MODE	= 0x8c,
  	DO_CALIBRATION			= 0x8d,
  	GET_CALIBRATION_PARAM		= 0x8e,
  	SET_SHOOT_AUTO			= 0x90,
  	SET_FLASH			= 0x91,
  	SET_EXPOSURE			= 0x92,
  	SET_FOCUS			= 0x93,
  	SET_SHUTTER			= 0x94,
  	SET_RESOLUTION			= 0x95,
  	SET_COMPRESSION			= 0x96,
  	SET_TIMER			= 0x97,
  	SET_EXT_FLASH			= 0x98,
  	SET_PIC_FORMAT			= 0x99,
  	RECORD_FUNC			= 0xa2,
  	SET_SHOOTING_DEBUG_MODE		= 0xa3,
  	GET_INTERNAL_SHOOT_AUTO_FUNC	= 0xa4,
  	SET_INTERNAL_SHOOT_PARAM	= 0xa6,
  	SET_SHOOT_PARAM			= 0xa7,
  	SET_REVIEW_MODE			= 0xa8,
  	SET_DISPLAY_PICTURE_NO		= 0xa9,
  	SET_SHOOT_MODE_BY_INDEX		= 0xaa,
  	GET_SHOOT_MODE_TABLE		= 0xab,
  	SET_ACTIVE_SHOOTING_MODE	= 0xac,
  	TAKE_PHOTO 			= 0xb0,
  	DELETE_PHOTO			= 0xb1,
  	PROTECT_PHOTO			= 0xb2,
  	DOWNLOAD_THUMBNAIL		= 0xb3,
  	DOWNLOAD_PHOTO			= 0xb4,
  	TAKE_PREVIEW			= 0xb5,
  	SELF_TEST			= 0xb6,
  	DISPLAY_PATTERN			= 0xb7,
  	UPLOAD_PHOTO			= 0xb8,
  	UPLOAD_FIRMWARE			= 0xba,
  	UPDATE_FIRMWARE			= 0xbb,
  	DOWNLOAD_FILE_BY_NAME		= 0xbd,
  	__UPDATE_FIRMWARE		= 0xbc,
  	DOWNLOAD_PHOTO_FROM_ALBUM	= 0xbe,
  	GET_CAMERA_CAPS			= 0xc0,
  	GET_CAMERA_CURINFO		= 0xc1,
  	GET_SHOOT_MODE_2		= 0xc3,
  	GET_PHOTO_INFO			= 0xc5,
  	GET_ALBUM_INFO			= 0xc6,
  	GET_CAMERA_READY		= 0xce,
  	GET_INTERNAL_SHOOT_PARAM	= 0xcf,
  	CREATE_ALBUM			= 0xd0,
  	RENAME_ALBUM			= 0xd2,
  	SET_ACTIVE_ALBUM		= 0xd4,
  };
  
  /*
      Every commands looks like:
      02 AA BB .... CC CC CC CC 03
  
      02 - STX
      AA - cmd byte
      BB - 0x80 | argumentlength
      ... - argumentlength bytes
      CC CC CC CC   16 bit CRC split into 4 bit pieces, | 0x80
      03 - ETX
  
      A generic CRC is done over the buffer, starting with the CMD byte (offset 1).
  
      buffer[7]  = ((crc>>12) & 0xf) | 0x80;
      buffer[8]  = ((crc>>8 ) & 0xf) | 0x80;
      buffer[9]  = ((crc>>4 ) & 0xf) | 0x80;
      buffer[10] = ((crc    ) & 0xf) | 0x80;
  
      buffer[11] = 0x03;
  
  */
  
  static int
  hp_gen_cmd_blob (enum hp215_cmd cmd, int bytes, unsigned char *argdata, unsigned char **buf, int *buflen)
  {
  	int i, crc = 0;
  
  	*buflen = 1+1+1+bytes+4+1; /* STX, CMD, ARGLEN, arguments, 4xCRC, ETX */
  	*buf    = malloc(*buflen);
  	if (!*buf)
  		return GP_ERROR_NO_MEMORY;
  
  	/* store STX */
  	(*buf)[0] = STX;
  
  	/* store CMD */
  	(*buf)[1] = cmd;
  	if (bytes >= 0x7d) {
  		gp_log (GP_LOG_ERROR, "hp215", "Using too large argument buffer %d bytes", bytes);
  		free (*buf);
  		*buf = NULL;
  		return GP_ERROR_BAD_PARAMETERS;
  	}
  	/* store arglen */
  	(*buf)[2] = 0x80 | bytes;
  
  	if (bytes) {
  		/* store arguments */
  		memcpy ((*buf)+3, argdata, bytes);
  	}
  
  	/* generate CRC over cmd, len, and arguments */
  	for (i=1;i<bytes + 3;i++)
  		crc = updcrc((*buf)[i], crc);
  
  	/* store CRC */
  	(*buf)[bytes+3] = ((crc >> 12) & 0xf) | 0x80;
  	(*buf)[bytes+4] = ((crc >>  8) & 0xf) | 0x80;
  	(*buf)[bytes+5] = ((crc >>  4) & 0xf) | 0x80;
  	(*buf)[bytes+6] = ((crc >>  0) & 0xf) | 0x80;
  	/* store ETX */
  	(*buf)[bytes+7] = ETX;
  	return GP_OK;
  }
  
  static int
  hp_gen_cmd_1_16 (enum hp215_cmd cmd, unsigned short val, unsigned char **buf, int *buflen)
  {
  	unsigned char argbuf[4];
  
  	argbuf[0] = ((val&0xf000)>>12) | 0x80;
  	argbuf[1] = ((val&0x0f00)>> 8) | 0x80;
  	argbuf[2] = ((val&0x00f0)>> 4) | 0x80;
  	argbuf[3] = ((val&0x000f)    ) | 0x80;
  	return hp_gen_cmd_blob (cmd, 4, argbuf, buf, buflen);
  }
  
  
  static int
  hp_send_ack (Camera *cam)
  {
  	char byte = ACK;
  	int ret;
  
  	gp_log (GP_LOG_DEBUG, "hp215", "Sending ACK ... ");
  	ret = gp_port_write (cam->port, &byte, 1);
  	if (ret < GP_OK)
  		gp_log (GP_LOG_ERROR, "hp215", "ACK sending failed with %d", ret);
  	return ret;
  }
  
  
  static int
  hp_rcv_ack (Camera *cam, char *ackval)
  {
  	int           ret;
  	char byte = '\0';
  
  	gp_log (GP_LOG_DEBUG, "hp215", "Receiving ACK ... ");
  	ret = gp_port_read (cam->port, &byte, 1);
  	if (ret < GP_OK)
  		return ret;
  	if (ackval)
  		*ackval = byte;
  	if (byte == ACK)
  		return GP_OK;
  	gp_log (GP_LOG_DEBUG, "hp215", "Expected ACK, but read %02x", byte);
  	return GP_ERROR_IO;
  }
  
  static int
  decode_u32(unsigned char **msg, int *msglen, unsigned int *val) {
  	unsigned int x = 0,i;
  	*val = 0;
  	for (i=0;i<8;i++) {
  		if (!*msglen)
  			return GP_ERROR;
  		x <<= 4;
  		x  |= ((**msg) & 0x0f);
  		(*msg)++;
  		(*msglen)--;
  	}
  	*val = x;
  	return GP_OK;
  }
  
  static int
  decode_u16(unsigned char **msg, int *msglen, unsigned short *val) {
  	unsigned int x = 0,i;
  	*val = 0;
  	for (i=0;i<4;i++) {
  		if (!*msglen)
  			return GP_ERROR;
  		x <<= 4;
  		x  |= ((**msg) & 0x0f);
  		(*msg)++;
  		(*msglen)--;
  	}
  	*val = x;
  	return GP_OK;
  }
  
  static int
  hp_send_command_and_receive_blob(
  	Camera *camera, unsigned char *buf, int buflen,
  	unsigned char **msg, int *msglen, unsigned int *retcode
  ) {
  	int tries, ackret, ret, replydatalen;
  	unsigned char msgbuf[0x400];
  	char ackval;
  
  	tries = 0;
  	*msg = NULL;
  	*msglen = 0;
  	for (tries = 0; tries < 3; tries++ ) {
  		ret = gp_port_write (camera->port, (char*)buf, buflen);
  		if (ret < GP_OK)
  			return ret;
  		ret = hp_rcv_ack (camera, &ackval);
  		if (ret < GP_OK) {
  			if (ackval == NAK)
  				continue;
  			return GP_ERROR_IO;
  		}
  		break;
  	}
  	gp_log( GP_LOG_DEBUG, "hp215", "Expecting reply blob");
  	ret = gp_port_read (camera->port, (char*)msgbuf, sizeof(msgbuf));
  	if (ret < GP_OK)
  		return ret;
  	ackret = hp_send_ack (camera);
  	if (ackret < GP_OK)
  		return ackret;
  	if (msgbuf[0] != STX) {
  		gp_log (GP_LOG_ERROR, "hp215", "Expected STX / 02 at begin of buffer, found %02x", msgbuf[0]);
  		return GP_ERROR_IO;
  	}
  	if (msgbuf[ret-1] != ETX) {
  		gp_log (GP_LOG_ERROR, "hp215", "Expected ETX / 03 at end of buffer, found %02x", msgbuf[ret-1]);
  		return GP_ERROR_IO;
  	}
  	replydatalen = msgbuf[2] & 0x7f;
  	if (replydatalen != ret - 8) {
  		gp_log (GP_LOG_ERROR, "hp215", "Reply datablob length says %d, but just %d bytes available?", replydatalen, ret-8);
  		return GP_ERROR_IO;
  	}
  	if (replydatalen < 2) {
  		gp_log (GP_LOG_ERROR, "hp215", "Reply datablob length is smaller than retcode (%d)", replydatalen);
  		return GP_ERROR_IO;
  	}
  	*retcode = (msgbuf[3] << 8) | msgbuf[4];
  
  	if (msgbuf[2] == 0xff) {
  		unsigned char *xmsg = msgbuf+5;
  		int xmsglen = 8;
  		unsigned int newlen;
  		char eot;
  		/* ok, overlong reply */
  
  		ret = decode_u32(&xmsg, &xmsglen, &newlen);
  		if (ret < GP_OK)
  			return ret;
  		replydatalen = newlen;
  		*msglen = replydatalen;
  		*msg = malloc (replydatalen);
  		ret = gp_port_read (camera->port, (char*)*msg, replydatalen);
  		if (ret < GP_OK) {
  			free (*msg);
  			*msg = NULL;
  			return ret;
  		}
  		ret = gp_port_read (camera->port, &eot, 1);
  		if (ret < GP_OK) {
  			free (*msg);
  			*msg = NULL;
  			return ret;
  		}
  		if (ret != 1) {
  			free (*msg);
  			*msg = NULL;
  			return GP_ERROR_IO;
  		}
  		if (eot != EOT) {
  			free (*msg);
  			*msg = NULL;
  			gp_log (GP_LOG_ERROR, "hp215", "read %02x instead of expected 04", eot);
  			return GP_ERROR_IO;
  		}
  		ackret = hp_send_ack (camera);
  		if (ackret < GP_OK) {
  			free (*msg);
  			*msg = NULL;
  			return ackret;
  		}
  	} else {
  		/* short reply - normal mode, do not copy E0 E0  */
  		*msg = malloc (replydatalen-2);
  		*msglen = replydatalen-2;
  		memcpy (*msg, msgbuf+5, replydatalen-2);
  	}
  	gp_log (GP_LOG_DEBUG, "hp215", "Read Blob: retcode is %04x", *retcode);
  	GP_LOG_DATA ((char*)*msg, *msglen, "Read Blob: argument block is:");
  	return GP_OK;
  }
  
  static int
  hp_get_timeDate_cam (Camera *cam, char *txtbuffer, size_t txtbuffersize)
  {
  	int		msglen, buflen, ret;
  	unsigned char	*xmsg, *msg, *buf;
  	unsigned int	retcode;
  	t_date		date;
  	unsigned short	val16;
  	unsigned int	percent, val32, freespace, unixtime, freeimages, usedimages;
  	char		datebuf[15];
  
  	gp_log (GP_LOG_DEBUG, "hp215", "Sending date/time command ... ");
  
  	ret = hp_gen_cmd_blob (GET_CAMERA_CURINFO, 0, NULL, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (cam, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	xmsg = msg;
  	if (msglen < 0x59) {
  		free (msg);
  		gp_log (GP_LOG_ERROR, "hp215", "too short reply block, %d bytes", msglen);
  		return GP_ERROR_IO;
  	}
  	
  	memcpy (datebuf, msg, 15);
  	gp_log (GP_LOG_DEBUG, "hp215", "0f: %02x", msg[0x0f] & 0x7f);
  	gp_log (GP_LOG_DEBUG, "hp215", "10: %02x", msg[0x10] & 0x7f);
  
  	xmsg += 0x12;
  	msglen -= 0x12;
  	decode_u16(&xmsg, &msglen, &val16);
  	gp_log (GP_LOG_DEBUG, "hp215", "12: %04x", val16);
  	gp_log (GP_LOG_DEBUG, "hp215", "16: %02x", msg[0x16] & 0x7f);
  	xmsg++;msglen--;
  	decode_u16(&xmsg, &msglen, &val16);
  	gp_log (GP_LOG_DEBUG, "hp215", "17: %04x", val16);
  	decode_u16(&xmsg, &msglen, &val16);
  	gp_log (GP_LOG_DEBUG, "hp215", "1b: %04x", val16);
  	percent = msg[0x1f] & 0x7f;
  	xmsg++;msglen--;
  	decode_u32(&xmsg, &msglen, &val32);
  	gp_log (GP_LOG_DEBUG, "hp215", "20: %08x", val32);
  	decode_u32(&xmsg, &msglen, &val32);
  	gp_log (GP_LOG_DEBUG, "hp215", "28: %08x", val32);
  	decode_u32(&xmsg, &msglen, &val32);
  	gp_log (GP_LOG_DEBUG, "hp215", "30: %08x", val32);
  	gp_log (GP_LOG_DEBUG, "hp215", "38: %02x", msg[0x38] & 0x7f);
  	xmsg++;msglen--;
  	decode_u32(&xmsg, &msglen, &unixtime);
  	decode_u32(&xmsg, &msglen, &freeimages);
  	decode_u32(&xmsg, &msglen, &usedimages);
  	decode_u32(&xmsg, &msglen, &val32);
  	gp_log (GP_LOG_DEBUG, "hp215", "51: %08x", val32);
  	decode_u32(&xmsg, &msglen, &freespace);
  
  	/* Filter time and date out of the message */
  	date.day   = (msg[3]-48)*10 + (msg[4]-48);
  	date.month = (msg[0]-48)*10 + (msg[1]-48);
  	date.year  = 2000 + (msg[6]-48)*10 + (msg[7]-48);
  	date.hour  = (msg[9]-48)*10 + (msg[10]-48);
  	date.min   = (msg[12]-48)*10 + (msg[13]-48);
  	free (msg);
  
  
  	snprintf (txtbuffer, txtbuffersize, _("Current camera time:  %04d-%02d-%02d  %02d:%02d\nFree card memory: %d\nImages on card: %d\nFree space (Images): %d\nBattery level: %d %%."),
  		date.year, date.month, date.day, date.hour, date.min,
  		freespace, usedimages, freeimages, percent
  	);
  	/* FIXME: communication hangs here ... perhaps need to reset USB pipe */
  	return GP_OK;
  }
  
  static int
  camera_summary (Camera *camera, CameraText *summary, GPContext *context)
  {
  	char	buffer[200];
  	int	ret;
  
  	ret = hp_get_timeDate_cam (camera, buffer, sizeof(buffer));
  	if (ret < GP_OK)
  		return ret;
  	strcpy (summary->text, buffer);
  	return (GP_OK);
  }
  
  static int
  camera_about (Camera *camera, CameraText *about, GPContext *context)
  {
  	strcpy (about->text, _("hp215\n"
  				"Marcus Meissner <marcus@jet.franken.de>\n"
  				"Driver to access the HP Photosmart 215 camera.\n"
  				"Merged from the standalone hp215 program.\n"
  				"This driver allows download of images and previews, and deletion of images.\n"
  	));
  	return (GP_OK);
  }
  
  static int
  get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
  	       CameraFileType type, CameraFile *file, void *data,
  	       GPContext *context)
  {
  	Camera *camera = data;
  	int           ret, buflen, msglen, image_no;
  	unsigned char cmd, *buf, *msg;
  	unsigned int  retcode;
  
  	image_no = gp_filesystem_number(fs, folder, filename, context);
  	if(image_no < 0)
  		return image_no;
  
  	switch (type) {
  	case GP_FILE_TYPE_NORMAL:
  		cmd = DOWNLOAD_PHOTO;
  		break;
  	case GP_FILE_TYPE_PREVIEW:
  		cmd = DOWNLOAD_THUMBNAIL;
  		break;
  	default:
  		return GP_ERROR_BAD_PARAMETERS;
  	}
  
  	ret = hp_gen_cmd_1_16 (cmd, image_no+1, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	gp_file_set_mime_type (file, GP_MIME_JPEG);
  	gp_file_set_data_and_size (file, (char*)msg, msglen);
  	/* do not free (msg), we handed it to the lower layer right now */
  	return GP_OK;
  }
  
  static int
  delete_file_func (CameraFilesystem *fs, const char *folder,
  		  const char *filename, void *data, GPContext *context)
  {
  	Camera *camera = data;
  	int image_no, ret, msglen, buflen;
  	unsigned char  *msg, *buf;
  	unsigned int retcode;
  
  	image_no = gp_filesystem_number(fs, folder, filename, context);
  	if(image_no < 0)
  		return image_no;
  	ret = hp_gen_cmd_1_16 (DELETE_PHOTO, image_no+1, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	free (msg);
  	return (GP_OK);
  }
  
  static int
  delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
  		 GPContext *context)
  {
  	Camera *camera = data;
  	unsigned char *buf;
  	unsigned char *msg;
  	unsigned int retcode;
  	int      ret, buflen, msglen;
  
  	ret = hp_gen_cmd_1_16 (DELETE_PHOTO, 0xFFFF, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	free (msg);
  	return (GP_OK);
  }
  
  
  static int
  get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
  	       CameraFileInfo *info, void *data, GPContext *context)
  {
  	Camera		*camera = data;
  	int		ret, msglen, buflen, /* offset = 13, */ image_no;
  	unsigned int	val, retcode;
  	unsigned char	*xmsg, *msg, *buf;
  
  	gp_log (GP_LOG_DEBUG, "hp215", "folder %s, filename %s", folder, filename);
  
  	image_no = gp_filesystem_number(fs, folder, filename, context);
  	if(image_no < 0)
  		return image_no;
  	ret = hp_gen_cmd_1_16 (GET_PHOTO_INFO, image_no+1, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  
  	if (msglen < 2) {
  		free (msg);
  		return GP_ERROR_IO;
  	}
  
  /*
  0000  80 80 80 85 86 85-84 84 30 31 2f 30 32 2f  ..........01/02/
  0010  30 30 20 31 34 3a 34 38-00 80 81 80 80 80 80 81  00 14:48........
  0020  88 89 84 81 80 80 80 80-80 81 00 00 00 00 00 00  ................
  0030  00 00 00 00 00 00      -                         ......          
   */
  	xmsg = msg;
  	ret = decode_u32(&xmsg, &msglen, &val);
  	if (ret < GP_OK) {
  		free (msg);
  		return ret;
  	}
  	memset (info, 0, sizeof(*info));
  	info->file.fields = GP_FILE_INFO_SIZE;
  	info->file.size = val;
  
  	xmsg	+= 0x0f;
  	msglen	-= 0x0f;
  	gp_log (GP_LOG_DEBUG, "hp215", "byte0 %02x", xmsg[0]);
  	gp_log (GP_LOG_DEBUG, "hp215", "byte1 %02x", xmsg[1]);
  	xmsg	+= 2;
  	msglen	-= 2;
  
  	ret = decode_u32(&xmsg, &msglen, &val);
  	if (ret < GP_OK) {
  		free (msg);
  		return ret;
  	}
  	info->preview.fields = GP_FILE_INFO_SIZE;
  	info->preview.size = val;
  
  	gp_log (GP_LOG_DEBUG, "hp215", "byte2 %02x", xmsg[0]);
  	gp_log (GP_LOG_DEBUG, "hp215", "byte3 %02x", xmsg[1]);
  
  	free (msg);
  	return GP_OK;
  #if 0
  	strcpy ((camera->pic[cnt+1])->date_time, &msg[0x0d]);
  
  	/* Calculate day,month, year, hour and minute of the photo */
  	(camera->pic[cnt+1])->date.day   = (msg[3+offset]-48)*10 + (msg[4+offset]-48);
  	(camera->pic[cnt+1])->date.month = (msg[0+offset]-48)*10 + (msg[1+offset]-48);
  	(camera->pic[cnt+1])->date.year  = 2000 + (msg[6+offset]-48)*10 + (msg[7+offset]-48);
  	(camera->pic[cnt+1])->date.hour  = (msg[ 9+offset]-48)*10 + (msg[10+offset]-48);
  	(camera->pic[cnt+1])->date.min   = (msg[12+offset]-48)*10 + (msg[13+offset]-48);
  
  	/* Get the file info here and write it into <info> */
  
  	return (GP_OK);
  #endif
  }
  
  static int
  file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
  		void *data, GPContext *context)
  {
  	Camera		*camera = data;
  	int		ret, msglen, buflen;
  	unsigned int	retcode, count;
  	unsigned char	*xmsg, *msg, *buf;
  
  	/* Note: The original windows driver sends 0x348, and 4 bytes junk.
  	 * we just send the 0x348 and it works. */
  	ret = hp_gen_cmd_1_16 (GET_ALBUM_INFO, 0x348, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	gp_log (GP_LOG_DEBUG, "hp215", "Sending photo album request ... ");
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	xmsg	= msg + 0x20; /* skip 0x20 byte string */
  	msglen	= msglen - 0x20;
  	ret = decode_u32(&xmsg, &msglen, &count);
  	free (msg);
  	if (ret < GP_OK)
  		return ret;
  	return gp_list_populate(list, "image%i.jpg", count);
  }
  
  #if 0 /* This function is not used by anybody */
  static int
  get_shoot_mode_table (Camera *camera) {
  	unsigned char *buf, *msg, *xmsg;
  	int ret, buflen, msglen;
  	unsigned int i, cnt, val1, val2, val3, retcode;
  
  	ret = hp_gen_cmd_blob (GET_SHOOT_MODE_TABLE, 0, NULL, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	gp_port_set_timeout (camera->port, 10000);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	if (retcode != HP215_OK) {
  		free (msg);
  		return GP_ERROR_BAD_PARAMETERS;
  	}
  	xmsg = msg;
  	ret = decode_u32(&xmsg, &msglen, &val1);
  	if (ret < GP_OK) return ret;
  	ret = decode_u32(&xmsg, &msglen, &val2);
  	if (ret < GP_OK) return ret;
  	ret = decode_u32(&xmsg, &msglen, &val3);
  	if (ret < GP_OK) return ret;
  	
  	gp_log (GP_LOG_DEBUG, "hp215", "shootmode table 0x%08x, 0x%08x, 0x%08x", val1, val2, val3);
  
  	cnt = val1+val2;
  	for (i=0;i<cnt;i++) {
  		unsigned short val;
  		/* 0x35 byte every loop */
  		/* 0x20 byte string */
  		gp_log (GP_LOG_DEBUG , "hp215", "%d: %s", i, xmsg);
  		xmsg += 0x20;
  		msglen -= 0x20;
  		/* 0x15 left */
  		/* u4 u4 u8 u7 u4 u4 u4 u4 u4 u4 */
  		gp_log (GP_LOG_DEBUG, "hp215", "%01x %01x %02x %02x %01x %01x %01x %01x %01x %01x",
  			xmsg[0] & 0x0f,
  			xmsg[1] & 0x0f,
  			((xmsg[2] & 0xf) << 4) | (xmsg[3] & 0xf),
  			xmsg[4] & 0x7f,
  			xmsg[5] & 0x0f,
  			xmsg[6] & 0x0f,
  			xmsg[7] & 0x0f,
  			xmsg[8] & 0x0f,
  			xmsg[9] & 0x0f,
  			xmsg[10] & 0x0f
  		);
  		/* u7 u7 u8 u7 u7 u16 */
  		gp_log (GP_LOG_DEBUG, "hp215", "%02x %02x %02x %02x %02x",
  			xmsg[11] & 0x7f,
  			xmsg[12] & 0x7f,
  			((xmsg[13] & 0xf) << 4) | (xmsg[14] & 0xf),
  			xmsg[15] & 0x7f,
  			xmsg[16] & 0x7f
  		);
  		xmsg += 0x11;
  		msglen -= 0x11;
  		decode_u16 (&xmsg, &msglen, &val);
  		gp_log (GP_LOG_DEBUG, "hp215", "%04x", val);
  	}
  
  	free (msg);
  	return GP_OK;
  }
  #endif
  
  static int
  camera_capture_preview (Camera *camera, CameraFile *file, GPContext *context)
  {
  	unsigned char *buf, *msg;
  	int ret, buflen, msglen;
  	unsigned int retcode;
  
  	/* there is one mode where we send a 16bit 0x1 */
  	ret = hp_gen_cmd_blob (TAKE_PREVIEW, 0, NULL, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	gp_port_set_timeout (camera->port, 10000);
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	if (retcode != HP215_OK) {
  		free (msg);
  		return GP_ERROR_BAD_PARAMETERS;
  	}
  	gp_file_set_mime_type (file, GP_MIME_JPEG);
  	gp_file_set_data_and_size (file, (char*)msg, msglen);
  	/* do not free (msg), we handed it to the lower layer right now */
  	return ret;
  }
  
  
  static int
  camera_capture (Camera *camera, CameraCaptureType type, CameraFilePath *path,
  		GPContext *context)
  {
  	unsigned char *buf, *msg;
  	int ret, buflen, msglen;
  	unsigned int retcode;
  
  /*	get_shoot_mode_table (camera); */
  
  	/*ret = hp_gen_cmd_1_16 (TAKE_PHOTO, 1, &buf, &buflen);*/ /* download after capture */
  	gp_port_set_timeout (camera->port, 60000);
  	ret = hp_gen_cmd_blob (TAKE_PHOTO, 0, NULL, &buf, &buflen); /* store on camera */
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	gp_port_set_timeout (camera->port, 10000);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	free (msg);
  	/*
  	 * tell libgphoto2 where to find it by filling out the path.
  	 */
  	return GP_OK;
  }
  
  
  int
  camera_id (CameraText *id) {
  	strcpy(id->text, "hp215");
  	return (GP_OK);
  }
  
  
  int
  camera_abilities (CameraAbilitiesList *list) {
  	CameraAbilities a;
  
  	memset(&a, 0, sizeof(a));
  	strcpy(a.model, "HP:PhotoSmart 215");
  	a.status		= GP_DRIVER_STATUS_EXPERIMENTAL;
  	a.port			= GP_PORT_USB;
  	a.usb_vendor		= 0x3f0;	/* HP */
  	a.usb_product		= 0x6202;	/* Photosmart 215 */
  	a.operations		= GP_OPERATION_CAPTURE_PREVIEW | GP_OPERATION_CAPTURE_IMAGE;
  	a.file_operations	= GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_PREVIEW;
  	a.folder_operations	= GP_FOLDER_OPERATION_DELETE_ALL;
  	return gp_abilities_list_append(list, a);
  }
  
  static CameraFilesystemFuncs fsfuncs = {
  	.file_list_func = file_list_func,
  	.get_info_func = get_info_func,
  	.get_file_func = get_file_func,
  	.del_file_func = delete_file_func,
  	.delete_all_func = delete_all_func
  };
  
  int
  camera_init (Camera *camera, GPContext *context)
  {
  	unsigned char *msg,*buf;
  	int ret, msglen, buflen;
  	unsigned int retcode;
  	GPPortSettings settings;
  
  	camera->functions->summary              = camera_summary;
  	camera->functions->about                = camera_about;
  	camera->functions->capture              = camera_capture;
  	camera->functions->capture_preview      = camera_capture_preview;
  
  	gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
  
  	gp_port_get_settings (camera->port, &settings);
  	settings.usb.inep  = 0x83;
  	settings.usb.outep = 0x04;
  	gp_port_set_settings (camera->port, settings);
  	/* This function does the initialisation sequence.
  	 * CMD = 0xce, REPLY = 2 byte, 0xe0 0xe0  ... apparently means ok.
  	 */
  	gp_log (GP_LOG_DEBUG, "hp215", "Sending init sequence ... ");
  	ret = hp_gen_cmd_blob (GET_CAMERA_READY, 0, NULL, &buf, &buflen);
  	if (ret < GP_OK)
  		return ret;
  	ret = hp_send_command_and_receive_blob (camera, buf, buflen, &msg, &msglen, &retcode);
  	free (buf);
  	if (ret < GP_OK)
  		return ret;
  	free (msg);
  	if (retcode != HP215_OK)
  		return GP_ERROR_IO;
  	return ret;
  }
  