Logo Search packages:      
Sourcecode: libgphoto2 version File versions

pdc320.c

/* pdc320.c
 *
 * Copyright © 2001 Lutz Müller
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* Originally written by Peter Desnoyers <pjd@fred.cambridge.ma.us>,
 * and adapted for gphoto2 by
 * Nathan Stenzel <nathanstenzel@users.sourceforge.net> and
 * Lutz Müller <lutz@users.sourceforge.net>
 *
 * Maintained by Nathan Stenzel <nathanstenzel@users.sourceforge.net>
 *
 * Original windows drivers available at:
 *    http://www.polaroid.com/service/software/index.html
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <gphoto2/gphoto2-library.h>
#include <gphoto2/gphoto2-port-log.h>

#include "pdc320.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 textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#define GP_MODULE "pdc320"

/*******************************************************************************/
/* NOTICE: There is a 16x8 block of pixels after the image data.               */
/* All of them are about the same and I believe they are for image correction. */
/*******************************************************************************/

/* Notes on what needs work:
 *    Make a better header that could correct color problems....
 *          or manage to decode, color correct, and save the image.
 * The quantization tables are incorrect for the PDC320.
 */

/*
 * For the PDC640SE, the init sequence is INIT,ID,ENDINIT,STATE,NUM,SIZE,PIC.
 *
 * During handshake, if the first read byte is not 6 then it is not good. Read
 * the number of bytes mentioned +2 ("FE FF"?)and then INIT and try SIZE
 * again (this is a loop).
 *
 * It is possible that the PDC320 might do a similar handshaking sequence at
 * that same point.
 */

typedef enum {
      PDC320,
      PDC640SE
} PDCModel;

struct _CameraPrivateLibrary {
      PDCModel model;
};

static int
pdc320_escape(
      const unsigned char *inbuf, int inbuflen,
      unsigned char *outbuf
) {
      int i,j;

      j = 0;
      for (i=0;i<inbuflen;i++) {
            switch (inbuf[i]) {
            case 0xe3:
                  outbuf[j++] = 0xe5;outbuf[j++] = 3;
                  break;
            case 0xe4:
                  outbuf[j++] = 0xe5;outbuf[j++] = 2;
                  break;
            case 0xe5:
                  outbuf[j++] = 0xe5;outbuf[j++] = 1;
                  break;
            case 0xe6:
                  outbuf[j++] = 0xe5;outbuf[j++] = 0;
                  break;
            default:outbuf[j++] = inbuf[i];
                  break;
            }
      }
      return j;
}

static int
pdc320_calc_checksum(const unsigned char *buf, int buflen) {
      int checksum, j;

      checksum = 0;
      j = 0;
      for (j=0;j<buflen/2;j++) {
            checksum += buf[j*2];
            checksum += buf[j*2+1]<<8;
      }
      if (buflen & 1)
            checksum += buf[buflen-1];

      while (checksum > 0xffff) {
            checksum =
                  (checksum & 0xffff) + 
                  ((checksum >> 16) & 0xffff);
      }
      return 0xffff-checksum; /* neg checksum actually */
}

static int
pdc320_command(
      GPPort *port,
      const unsigned char *cmd, int cmdlen
) {
      unsigned char csum[2];
      int checksum, off, ret;
      unsigned char *newcmd;

      checksum = pdc320_calc_checksum (cmd, cmdlen);
      csum[0] = checksum & 0xff;
      csum[1] = (checksum >> 8) & 0xff;
      /* 4 times 0xe6, checksum and command, both might be escaped */
      newcmd = malloc (2*(sizeof(csum)+cmdlen)+4);
      if (!newcmd)
            return GP_ERROR_NO_MEMORY;
      memset (newcmd, 0xe6, 4); off = 4;
      off += pdc320_escape (cmd, cmdlen, newcmd + off);
      off += pdc320_escape (csum, 2, newcmd + off);
      ret = gp_port_write (port, newcmd, off);
      free(newcmd);
      return ret;
}

static int
pdc320_simple_command (GPPort *port, const unsigned char cmd) {
      return pdc320_command(port, &cmd, 1);
}

static int
pdc320_simple_reply (GPPort *port, const unsigned char expcode,
      unsigned int replysize, unsigned char *reply
) {
      unsigned char csum[2];
      int calccsum;

      CR (gp_port_read (port, reply, 1));
      if (reply[0] != expcode) {
            GP_DEBUG("*** reply got 0x%02x, expected 0x%02x\n",
                  reply[0], expcode
            );
            return GP_ERROR;
      }
      CR (gp_port_read (port, reply+1, replysize-1));
      CR (gp_port_read (port, csum, 2));
      calccsum = pdc320_calc_checksum (reply, replysize);
      if (calccsum != ((csum[1] << 8) | csum[0])) {
            GP_DEBUG("csum %x vs %x\n",calccsum,((csum[0]<<8)|csum[1]));
            return GP_ERROR;
      }
      return GP_OK;
}

static int
pdc320_simple_command_reply (GPPort *port,
      const unsigned char cmd, const unsigned char expcode,
      unsigned int replysize, unsigned char *reply
) {
      CR (pdc320_simple_command (port, cmd));
      CR (pdc320_simple_reply (port, expcode, replysize, reply));
      return GP_OK;
}

static int
pdc320_init (GPPort *port)
{
      unsigned char buf[32];
      unsigned char e6[4];
      int i;

      GP_DEBUG ("*** PDC320_INIT ***");
      
      /* The initial command is prefixed by 4 raw E6. */
      memset(e6,0xe6,sizeof(e6));
      CR (gp_port_write (port, e6, sizeof (e6) ));

      GP_DEBUG ("*** PDC320_INIT ***");
      CR (pdc320_simple_command_reply (port, PDC320_INIT, 5, 1, buf));
      GP_DEBUG ("*** PDC320_ID ***");
      CR (pdc320_simple_command_reply (port, PDC320_ID, 0, 12, buf));
      GP_DEBUG ("*** PDC320_STATE ***");
      CR (pdc320_simple_command_reply (port, PDC320_STATE, 2, 22, buf));

      for (i=0;i<9;i++) {
            GP_DEBUG ("%d: %d (0x%x)",i,((buf[2+i*2]<<8)|buf[2+2*i+1]), ((buf[2+i*2]<<8)|buf[2+2*i+1]));
      }
      GP_DEBUG ("*** PDC320_ENDINIT ***");
      return pdc320_simple_command_reply (port, PDC320_ENDINIT, 9, 1, buf);
}

static int
pdc320_num (GPPort *port)
{
      unsigned char buf[2];

      GP_DEBUG ("*** PDC320_NUM ***");
      CR (pdc320_simple_command_reply (port, PDC320_NUM, 3, 2, buf));
      GP_DEBUG ("The camera contains %i files.", buf[1]);
      return buf[1];
}

static int
pdc320_delete (GPPort *port)
{
      unsigned char buf[1];

      GP_DEBUG ("*** PDC320_DELETE ***");
      return pdc320_simple_command_reply(port, PDC320_DEL, 8, 1, buf);
}

static int
pdc320_size (Camera *camera, int n)
{
      int size;
      unsigned char buf[5];
      unsigned char cmd[2];

      GP_DEBUG ("*** PDC320_SIZE ***");
      cmd[0] = PDC320_SIZE;
      cmd[1] = n;
      CR (pdc320_command (camera->port, cmd, sizeof (cmd)));
      CR (pdc320_simple_reply (camera->port, 6, 5, buf));
      size = (buf[1] << 24) + (buf[2] << 16) + (buf[3] << 8) + buf[4];
      GP_DEBUG ("Image %i has size %i.", n, size);
      return (size);
}

/* Unclear. */
static int
pdc320_0c (Camera *camera, int n)
{
      int size, csum, i;
      unsigned char buf[3], *xbuf;
      unsigned char cmd[2];
      
      cmd[0] = 0x0c;
      cmd[1] = n;       /* n is from 1 on up */

      /* Write the command */
      GP_DEBUG ("*** PDC320_0c ***");
      CR (pdc320_command (camera->port, cmd, sizeof (cmd)));
      CR (gp_port_read (camera->port, buf, 3));
      if (buf[0] != 7)
            return GP_ERROR;
      size = (buf[1] << 8) | buf[2];
      xbuf = malloc (size);
      CR (gp_port_read (camera->port, xbuf, size));
      for (i=0; i<size; i++) {
            GP_DEBUG ("buf[%d]=0x%02x", i, xbuf[i]);
      }
      CR (gp_port_read (camera->port, buf, 2));
      csum = (buf[0] << 8) | buf[1];
      /* checksum is calculated from both, but i am not clear how. */
      return GP_OK;
}

static int
pdc320_pic (Camera *camera, int n, unsigned char **data, int *size)
{
      unsigned char cmd[2];
      unsigned char buf[2048];
      int remaining, f1, f2, i, len, checksum;
      int chunksize=2000;

      /* Get the size of the picture and allocate the memory */
      GP_DEBUG ("Checking size of image %i...", n);
      CR (*size = pdc320_size (camera, n));
      *data = malloc (sizeof (char) * (*size));
      if (!*data)
            return (GP_ERROR_NO_MEMORY);

      cmd[0] = PDC320_PIC;
      cmd[1] = n;

      CR_FREE (pdc320_command (camera->port, cmd, sizeof (cmd)), *data);
      switch (camera->pl->model) {
      case PDC640SE:
            chunksize = 528;
            break;
      case PDC320:
            chunksize = 2000;
            break;
      }

      len = *size;
      for (i = 0; i < *size; i += chunksize) {

            /* How many bytes do we read in this round? */
            remaining = *size - i;
            len = (remaining > chunksize) ? chunksize : remaining;

            /* Read the frame number */
            usleep (10000);
            CR_FREE (gp_port_read (camera->port, buf, 5), *data);
            f1 = (buf[1] << 8) + buf[2];
            f2 = (buf[3] << 8) + buf[4];
            GP_DEBUG ("Reading frame %d "
                         "(%d)...", f1, f2);

            /* Read the actual data */
            usleep(1000);
            CR_FREE (gp_port_read (camera->port, *data + i, len), *data);
            
            /* Read the checksum */
            CR_FREE (gp_port_read (camera->port, buf, 2), *data);
            checksum = (buf[0] << 8) + buf[1];
      }

      return (GP_OK);
}

int
camera_id (CameraText *id)
{
      strcpy (id->text, "Polaroid DC320");

      return (GP_OK);
}

int
camera_abilities (CameraAbilitiesList *list) 
{
      int i;
      CameraAbilities a;

      for (i = 0; models[i].model; i++) {
            memset(&a, 0, sizeof(a));
            strcpy (a.model, models[i].model);
            a.status          = GP_DRIVER_STATUS_EXPERIMENTAL;
            a.port                  = GP_PORT_SERIAL;
            a.speed[0]        = 115200;
            a.speed[1]        = 0;
            a.operations            = GP_OPERATION_NONE;
            a.file_operations = GP_FILE_OPERATION_NONE;
            a.folder_operations     = GP_FOLDER_OPERATION_DELETE_ALL;
            CR (gp_abilities_list_append (list, a));
      }
      return (GP_OK);
}

static int
get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
             CameraFileType type, CameraFile *file, void *user_data,
             GPContext *context)
{
      Camera *camera = user_data;
      jpeg *myjpeg;
      chunk *tempchunk;
      int n, size, width, height;
      unsigned char *data;

      if ((type != GP_FILE_TYPE_RAW) && (type != GP_FILE_TYPE_NORMAL))
            return (GP_ERROR_NOT_SUPPORTED);

      /*
       * Get the number of the picture from the filesystem and increment
       * since we need a range starting with 1.
       */
      GP_DEBUG ("Getting number from fs...");
      CR (n = gp_filesystem_number (camera->fs, folder, filename, context));
      n++;

      /* Get the file */
      GP_DEBUG ("Getting file %i...", n);
      CR (pdc320_pic (camera, n, &data, &size));
      CR (pdc320_0c (camera, n));

      /* Post-processing */
      switch (type) {
      case GP_FILE_TYPE_RAW:
            CR (gp_file_set_data_and_size (file, data, size));
            CR (gp_file_set_name (file, filename));
            CR (gp_file_set_mime_type (file, GP_MIME_RAW));
            break;
      case GP_FILE_TYPE_NORMAL:
      default:
      GP_DEBUG ("Using Nathan Stenzel's experimental jpeg.c\n");
      GP_DEBUG ("About to make jpeg header\n");
    width = data[4]*256 + data[5];
    height = data[2]*256 + data[3];
      GP_DEBUG ("Width=%i\tHeight=%i\n", width, height);
    myjpeg = gpi_jpeg_header(width,height/2, 0x11,0x11,0x21, 1,0,0, &chrominance, &luminance,
            0,0,0, gpi_jpeg_chunk_new_filled(HUFF_00),
          gpi_jpeg_chunk_new_filled(HUFF_10), NULL, NULL);
      GP_DEBUG ("Turning the picture data into a chunk data type\n");
    tempchunk = gpi_jpeg_chunk_new(size);
    tempchunk->data = data;
      GP_DEBUG ("Adding the picture data to the jpeg structure\n");
    gpi_jpeg_add_marker(myjpeg, tempchunk, 6, size-1);
      GP_DEBUG ("Writing the jpeg file\n");
    gpi_jpeg_write(file, filename, myjpeg);
      GP_DEBUG ("Cleaning up the mess\n");
    gpi_jpeg_destroy(myjpeg);

    }
    return (GP_OK);
}

static int
delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
             GPContext *context)
{
      Camera *camera = data;

      CR (pdc320_delete (camera->port));
      return (GP_OK);
}

static int
camera_about (Camera *camera, CameraText *about, GPContext *context)
{
      strcpy (about->text, _("Download program for several Polaroid cameras. "
            "Originally written by Peter Desnoyers "
            "<pjd@fred.cambridge.ma.us>, and adapted for gphoto2 by "
            "Nathan Stenzel <nathanstenzel@users.sourceforge.net> and "
            "Lutz Mueller <lutz@users.sf.net>.\n"
            "Polaroid 640SE testing was done by Michael Golden "
            "<naugrim@juno.com>."));
      return (GP_OK);
}

static int
file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
            void *data, GPContext *context)
{
      int n;
      Camera *camera = data;

      /* Fill the list */
      CR (n = pdc320_num (camera->port));
      gp_list_populate (list, "PDC320%04i.jpg", n);
      return (GP_OK);
}

static int
camera_summary (Camera *camera, CameraText *summary, GPContext *context)
{
      char buf[12];

      GP_DEBUG ("*** PDC320_ID ***");
      CR (pdc320_simple_command_reply (camera->port, PDC320_ID, 0, 12, buf));
      sprintf (summary->text, _("Model: %x, %x, %x, %x"), buf[8],buf[9],buf[10],buf[11]);
      return (GP_OK);
}

static int
camera_exit (Camera *camera, GPContext *context)
{
      if (!camera)
            return (GP_ERROR_BAD_PARAMETERS);

      if (camera->pl) {
            free (camera->pl);
            camera->pl = NULL;
      }

      return (GP_OK);
}

static CameraFilesystemFuncs fsfuncs = {
      .file_list_func = file_list_func,
      .get_file_func = get_file_func,
      .delete_all_func = delete_all_func
};

int
camera_init (Camera *camera, GPContext *context)
{
      GPPortSettings settings;
      CameraAbilities abilities;
      int result;

        /* First, set up all the function pointers */
      camera->functions->exit       = camera_exit;
        camera->functions->about        = camera_about;
      camera->functions->summary      = camera_summary;

      /* Now, tell the filesystem where to get lists and info */
      gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);

      camera->pl = malloc (sizeof (CameraPrivateLibrary));
      if (!camera->pl)
            return (GP_ERROR_NO_MEMORY);

      /* What model are we talking to? */
      gp_camera_get_abilities (camera, &abilities);
      if (  !strcmp (abilities.model, "Polaroid:Fun! 320") ||
            !strcmp (abilities.model, "Polaroid Fun! 320")
      )
            camera->pl->model = PDC320;
      else if (!strcmp (abilities.model, "Polaroid:640SE") ||
             !strcmp (abilities.model, "Polaroid 640SE")
      )
            camera->pl->model = PDC640SE;
      else {
            free (camera->pl);
            camera->pl = NULL;
            return (GP_ERROR_MODEL_NOT_FOUND);
      }

      /* Open the port and check if the camera is there */
      gp_port_get_settings (camera->port, &settings);
      if (!settings.serial.speed)
            settings.serial.speed = 115200;
      gp_port_set_settings (camera->port, settings);
      gp_port_set_timeout (camera->port, 30000);

      /* Check if the camera is really there */
      result = pdc320_init (camera->port);
      if (result < 0) {
            free (camera->pl);
            camera->pl = NULL;
            return (result);
      }
      return (GP_OK);
}

Generated by  Doxygen 1.6.0   Back to index