Logo Search packages:      
Sourcecode: libgphoto2 version File versions

ultrapocket.c

/* ultrapocket.c
 *
 * Copyright (C) 2003 Lee Benfield <lee@benf.org>
 *
 * 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.
 */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#include "ultrapocket.h"
#include "smal.h"

#define GP_MODULE "Smal Ultrapocket"

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (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

static int
ultrapocket_command(GPPort *port, int iswrite, unsigned char *data, int datasize) {
    int ret;
    if (iswrite)
      ret = gp_port_write(port,data,datasize);
    else
      ret = gp_port_read(port,data,datasize);

    if (ret>=GP_OK)
      ret=GP_OK;
    return ret;
}

int
ultrapocket_exit(GPPort *port, GPContext *context) {
    return GP_OK;
}

/* A generic slimshot-alike camera */
/* we have to determine from the header what the RAW image is, QVGA or VGA */
static int
getpicture_generic(Camera *camera, GPContext *context, unsigned char **rd,int *retwidth, int *retheight, int *retimgstart, const char *filename) {
    GPPort *port = camera->port;
    unsigned char  command[0x10] = { 0x11, 0x01, 0x00, 0x49, 0x4d, 0x47, 0,0,0,0, 0x2e, 0x52, 0x41, 0x57, 0x00, 0x00 };
    unsigned char  retdata[0x1000];
    unsigned char  header[42];
    unsigned char *rawdata;
    int            width, height, imgstart;
    smal_img_type  styp;
    int            ptc,pc,id;
#if DO_GAMMA
    unsigned char  gtable[256];
#endif

    memcpy(command+6, filename+3, 4); /* the id of the image to transfer */

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    /* the first packet has the header in it, plus the start of the data */
    CHECK_RESULT(ultrapocket_command(port, 0, retdata, 0x1000));
    memcpy(header, retdata, 41);
    styp = header[3];

    switch (styp) {
     case TYPE_QVGA:
      /* 24 packets, including one already got */
      ptc = 24;
      width = 320;
      height = 240;
      imgstart = 0x29;
      break;
     case TYPE_VGA:
      /* 80 including one already got. */
      ptc = 80;
      width = 640;
      height = 480;
      imgstart = 0x29;
      break;
     case TYPE_QVGA_BH:
      ptc = 24;
      width = 320;
      height = 240;
      imgstart = 0x100;
      break;
     case TYPE_VGA_BH:
      ptc = 80;
      width = 640;
      height = 480;
      imgstart = 0x100;
      break;
     default:
      return GP_ERROR;
   }
    rawdata = malloc(0x1000 * ptc * sizeof(char));
    if (!rawdata) return (GP_ERROR_NO_MEMORY);

    /* show how far we've got on the current image */
    id = gp_context_progress_start(context, ptc-1, _("Downloading image..."));

    memcpy(rawdata, retdata, 0x1000 * sizeof(char));
    for (pc=1;pc<ptc;pc++) {
      int ret = ultrapocket_command(port, 0, retdata, 0x1000);
      if (ret < GP_OK) {
           free (rawdata);
           gp_context_progress_stop(context, id);
           return ret;
      }
      gp_context_progress_update(context, id, pc);
      memcpy(rawdata + (pc * 0x1000), retdata, 0x1000 * sizeof(char));
    }
    gp_context_progress_stop(context, id);

    *retwidth = width;
    *retheight = height;
    *retimgstart = imgstart;

    *rd = rawdata;
    return GP_OK;
}

/*
 * Getting picture off a logitech pocket digital
 */
static int
getpicture_logitech_pd(Camera *camera, GPContext *context, unsigned char **rd, const char *filename) {
    GPPort *port = camera->port;
    unsigned char  command[0x10] = { 0x11, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    unsigned char  retdata[0x8000];
    unsigned char *rawdata;
    int            ptc,pc,id;
#if DO_GAMMA
    unsigned char  gtable[256];
#endif

    memcpy(command+3, filename, 11); /* the id of the image to transfer */

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    /* the first packet has the header in it, plus the start of the data */
    CHECK_RESULT(ultrapocket_command(port, 0, retdata, 0x8000));

    /* 640 x 480 =ish 10 * 0x8000 */
    ptc = 10;

    rawdata = malloc(0x8000 * ptc * sizeof(char));
    if (!rawdata) return (GP_ERROR_NO_MEMORY);

    /* show how far we've got on the current image */
    id = gp_context_progress_start(context, ptc-1, _("Downloading image..."));

    memcpy(rawdata, retdata, 0x8000 * sizeof(char));
    for (pc=1;pc<ptc;pc++) {
      int ret = ultrapocket_command(port, 0, retdata, 0x8000);
      if (ret < GP_OK) {
          gp_context_progress_stop(context, id);
          free (rawdata);
          return ret;
      }
      gp_context_progress_update(context, id, pc);
      memcpy(rawdata + (pc * 0x8000), retdata, 0x8000 * sizeof(char));
    }
    gp_context_progress_stop(context, id);

    *rd = rawdata;
    return GP_OK;
}

/* Different camera types (pocket digital/generic)
 * use a different protocol - have to differetiate.
 */
int
ultrapocket_getpicture(Camera *camera, GPContext *context, unsigned char **pdata, int *size, const char *filename)
{
    char           ppmheader[100];
    unsigned char *rawdata,*outdata;
    int            width, height, outsize, result;
    int            imgstart = 0;
    int            pc,pmmhdr_len;
#if DO_GAMMA
    unsigned char  gtable[256];
#endif

    switch (camera->pl->up_type) {
     case BADGE_FLATFOTO:
     case BADGE_GENERIC:
     case BADGE_ULTRAPOCKET:
     case BADGE_AXIA:
      CHECK_RESULT(getpicture_generic(camera, context, &rawdata, &width, &height, &imgstart, filename));
      break;
     case BADGE_LOGITECH_PD:
      CHECK_RESULT(getpicture_logitech_pd(camera, context, &rawdata, filename));
      width = 640;
      height = 480;
      imgstart = 0x29;
      break;
     default:
      return GP_ERROR;
    }

   sprintf (ppmheader, "P6\n"
          "# CREATOR: gphoto2, ultrapocket library\n"
          "%d %d\n"
          "255\n", width, height);

   /* Allocate memory for Interpolated ppm image */
   pmmhdr_len = strlen(ppmheader);
   outsize = (width+4) * height * 3 + pmmhdr_len + 1;
   outdata = malloc(outsize);
   if (!outdata) {
     free (rawdata);
     return (GP_ERROR_NO_MEMORY);
   }

   /* Set header */
   strcpy(outdata, ppmheader);

   /* Decode and interpolate the Bayer Mask */
   result = gp_bayer_decode((rawdata+imgstart), width+4, height,
                      &outdata[pmmhdr_len], 2 );

   /* and chop the spare 4 pixels off the RHS */
   for (pc=1;pc<height;pc++) {
      memmove(outdata + pmmhdr_len + (width * 3 * pc), outdata + pmmhdr_len + ((width+4) * 3 * pc), width*3);
   }
   /* modify outsize to refect trim */
   outsize = width * height * 3 + pmmhdr_len + 1;

   free(rawdata);
   if (result < 0) {
      free (outdata);
      return (result);
   }

#if DO_GAMMA
   gp_gamma_fill_table( gtable, 0.5 );
   gp_gamma_correct_single( gtable, &outdata[pmmhdr_len], height * width );
#endif

   *pdata = outdata;
   *size  = outsize;
   return GP_OK;
}

/*
 * Only need to reset the generic camera type, AFAICT -
 * the pocket digital never seems to need it.
 */
static int
ultrapocket_reset(GPPort **pport)
{
   GPPortInfo oldpi;
   GPPort *port = *pport;
   unsigned char cmdbuf[0x10];
   GP_DEBUG ("First connect since camera was used - need to reset cam");

   /*
    * this resets the ultrapocket.  Messy, but it's what the windows
    * software does.   We only reset if it's been plugged in since
    * last reset.
    */
   memset(cmdbuf, 0, 16);
   cmdbuf[0] = 0x28;
   cmdbuf[1] = 0x01;
   CHECK_RESULT(ultrapocket_command(port, 1, cmdbuf, 0x10));
   /* -------------- */
   sleep(2); /* This should do - _might_ need increasing */
   CHECK_RESULT(gp_port_get_info(port, &oldpi));
   CHECK_RESULT(gp_port_free(port));
   CHECK_RESULT(gp_port_new(&port));
   CHECK_RESULT(gp_port_set_info(port, oldpi));
   CHECK_RESULT(gp_port_usb_find_device (port, USB_VENDOR_ID_SMAL, USB_DEVICE_ID_ULTRAPOCKET));
   CHECK_RESULT(gp_port_open(port));
   *pport = port;
   return GP_OK;
}

/*
 * For most ultrapocket compatibles, call this.
 */
static int getpicsoverview_generic(
    Camera *camera, GPContext *context,
    int *numpics, CameraList *list
                           ) {
   GPPort **pport = &camera->port;
   GPPort *port = *pport;
   unsigned char command[0x10];
   unsigned char retbuf[0x1000];
   int x,y;
   int np = 0;
    char fn[20];
   int picid;
   int reset_needed;

   memset(command, 0, 16);
   command[0] = 0x12;

   CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

   CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));
   np = *(unsigned char*)(retbuf + 0x104);
   for (y=0;y<np;y++) {
      picid = retbuf[0x106+y*2] + (retbuf[0x107+y*2] << 8);
      sprintf(fn, "IMG%4.4d.PPM",picid);
      gp_list_append(list, fn, NULL);
   }
   reset_needed = (*(retbuf + 2) & UP_FLAG_NEEDS_RESET);
   for (x=0;x<7;x++) CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));

    /* Who knows what's in all these packets? ... only SmAL! */
    /* The windows driver gets all of this guff - since the camera functions just fine
     * without them, I'm going to ignore it for now */
#if COPY_WINDOWS_DRIVER
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x31;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x12;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x31;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x30;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<16;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x29;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
#endif

    if (reset_needed) {
      CHECK_RESULT(ultrapocket_reset(pport));
      port = *pport;
    }

#if COPY_WINDOWS_DRIVER
    memset(command, 0, 16);
    command[0] = 0x12;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x31;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x12;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
   /* -------------- */
    memset(command, 0, 16);
    command[0] = 0x31;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    for (x=0;x<8;x++)  { CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));}
    /* -------------- */
#endif

    *numpics = np;

    return GP_OK;
}

static int getpicsoverview_logitech_pd(
                               Camera *camera, GPContext *context,
                               int *numpics, CameraList *list
                               ) {
    GPPort *port = camera->port;
    unsigned char command[0x10];
    unsigned char retbuf[0x8000];
    int y;
    int np = 0;
    char fn[20];

    memset(command, 0, 16);
    command[0] = 0x12;

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x8000));
    np = *(unsigned char*)(retbuf + 0x105);
    for (y=0;y<np;y++) {
      memset(fn, 0, 20);
      memcpy(fn, retbuf+0x106+(y*0x10),11);
      /* bizzarely, the logitech camera returns a fname with a space
       * in it, but requires a '.'. Go figure, eh?
       */
      fn[7] = '.';
      gp_list_append(list, fn, NULL);
    }
    CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x8000));

    *numpics = np;

    return GP_OK;
}

/*
 * First, we send a request of the form
 * 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 *
 * Fuji Slimshot + Axia etc
 * The camera responds with 8 * 0x1000 bytes
 *
 * Logitech Digital Pocket
 * The camera responds with 2 * 0x8000 bytes
 *
 */
int
ultrapocket_getpicsoverview(
    Camera *camera, GPContext *context,
    int *numpics, CameraList *list
) {
    switch (camera->pl->up_type) {
     case BADGE_FLATFOTO:
     case BADGE_GENERIC:
     case BADGE_ULTRAPOCKET:
     case BADGE_AXIA:
      return getpicsoverview_generic(camera, context, numpics, list);
      break;
     case BADGE_LOGITECH_PD:
      return getpicsoverview_logitech_pd(camera, context, numpics, list);
      break;
     default:
      break;
    }
    return GP_ERROR;
}

static int deletefile_generic(GPPort *port, const char *filename)
{
    unsigned char command[0x10] = { 0x22, 0x01, 0x00, 0x49, 0x4d, 0x47, 0,0,0,0, 0x2e, 0x52, 0x41, 0x57, 0x00, 0x00 };
    memcpy(command+6, filename+3, 4); /* the id of the image to delete */

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    return GP_OK;
}

static int deletefile_logitech_pd(GPPort *port, const char *filename)
{
    unsigned char  command[0x10] = { 0x11, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    memcpy(command+3, filename, 11); /* the id of the image to delete */

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));
    return GP_OK;
}

/* The windows software doesn't allow one to delete a file without deleting previous
 * ones.
 *
 * Careful!
 *
 * This appears to be the same for Logitech pocket digital and the generic
 * camera.
 */
int
ultrapocket_deletefile(Camera *camera, const char *filename)
{
    GPPort *port = camera->port;

    switch (camera->pl->up_type) {
     case BADGE_FLATFOTO:
     case BADGE_GENERIC:
     case BADGE_ULTRAPOCKET:
     case BADGE_AXIA:
      return deletefile_generic(port, filename);
      break;
     case BADGE_LOGITECH_PD:
      return deletefile_logitech_pd(port, filename);
      break;
     default:
      break;
    }
    return GP_ERROR;

}

static
int deleteall_logitech_pd(GPPort **pport)
{
    unsigned char command[0x10];
    unsigned char retbuf[0x8000];
    GPPort *port = *pport;

    memset(command, 0, 16);
    command[0] = 0x12;

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x8000));
    CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x8000));

    /* as far as I can tell, the logitech pocket digital doesn't need a
     * reset - if it did, it would go here. */
    memset(command, 0, 0x10);
    command[0] = 0x18;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    return GP_OK;
}

static
int deleteall_generic(GPPort **pport)
{
    unsigned char command[0x10];
    unsigned char retbuf[0x1000];
    int x,reset_needed = 0;
    GPPort *port = *pport;

    memset(command, 0, 16);
    command[0] = 0x12;

    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));
    reset_needed = (*(retbuf + 2) & UP_FLAG_NEEDS_RESET);
    for (x=0;x<7;x++) CHECK_RESULT(ultrapocket_command(port, 0, retbuf, 0x1000));

    if (reset_needed) {
      CHECK_RESULT(ultrapocket_reset(pport));
      port = *pport;
    }

    memset(command, 0, 0x10);
    command[0] = 0x18;
    command[1] = 0x01;
    CHECK_RESULT(ultrapocket_command(port, 1, command, 0x10));

    return GP_OK;
}

int
ultrapocket_deleteall(Camera *camera)
{
    GPPort **pport = &camera->port;

    switch (camera->pl->up_type) {
     case BADGE_FLATFOTO:
     case BADGE_GENERIC:
     case BADGE_ULTRAPOCKET:
     case BADGE_AXIA:
      return deleteall_generic(pport);
      break;
     case BADGE_LOGITECH_PD:
      return deleteall_logitech_pd(pport);
      break;
     default:
      break;
    }
    return GP_ERROR;
}


Generated by  Doxygen 1.6.0   Back to index