diff options
Diffstat (limited to 'camlibs/polaroid/pdc640.c')
-rw-r--r-- | camlibs/polaroid/pdc640.c | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/camlibs/polaroid/pdc640.c b/camlibs/polaroid/pdc640.c new file mode 100644 index 000000000..a01b3d6f5 --- /dev/null +++ b/camlibs/polaroid/pdc640.c @@ -0,0 +1,1111 @@ +/* pdc640.c + * + * Copyright © 2001 Lutz Müller <lutz@users.sourceforge.net> + * Copyright © 2002 Marcus Meissner <marcus@jet.franken.de> + * + * 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-library.h> +#include <gphoto2/gphoto2-port-log.h> +#include <bayer.h> + +#include "jd350e.h" +#include "dlink350f.h" + +#define GP_MODULE "pdc640" + +#define PDC640_PING "\x01" +#define PDC640_SPEED "\x69\x0b" + +#define PDC640_MAXTRIES 3 + +#define CHECK_RESULT(result) {int r = (result); if (r < 0) return (r);} + +#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 + + +typedef enum{ + pdc640, + jd350e, + dlink350f +} Model; + +typedef int postproc_func(int,int,unsigned char*); + +struct _CameraPrivateLibrary{ + Model model; + BayerTile bayer_tile; + postproc_func* postprocessor; + char* filespec; +}; + +static int flip_vertical (int width, int height, unsigned char *rgb); +static int flip_both (int width, int height, unsigned char *rgb); + +static struct { + const char* model; + int vendor,product; + struct _CameraPrivateLibrary pl; +} models[] = { + {"Polaroid Fun Flash 640", 0, 0, { + pdc640, + BAYER_TILE_RGGB, + NULL, /* add postprocessor here! */ + "pdc640%04i.ppm" + } + }, + {"Novatech Digital Camera CC30", 0, 0, { + pdc640, + BAYER_TILE_RGGB, + NULL, /* add postprocessor here! */ + "pdc640%04i.ppm" + } + }, + {"Jenoptik JD350 entrance", 0x5da, 0x1006, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350e%04i.ppm" + } + }, + {"Jenoptik JD350 video", 0xd96, 0x0000, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350v%04i.ppm" + } + }, + {"ScanHex SX-35a", 0x797, 0x8901, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350v%04i.ppm" + } + }, + {"ScanHex SX-35b", 0x797, 0x8909, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350v%04i.ppm" + } + }, + {"ScanHex SX-35c", 0x797, 0x8911, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350v%04i.ppm" + } + }, + {"ScanHex SX-35d", 0x84d, 0x1001, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing, + "jd350v%04i.ppm" + } + }, + {"Typhoon StyloCam", 0x797, 0x801a, { + jd350e, + BAYER_TILE_BGGR, + &flip_vertical, + "stylo%04i.ppm" + } + }, + {"Trust PowerC@m 350FS", 0x6d6, 0x002e, { + jd350e, + BAYER_TILE_RGGB, + &trust350fs_postprocessing, + "trust%04i.ppm" + } + }, + {"Trust PowerC@m 350FT", 0x6d6, 0x002d, { + jd350e, + BAYER_TILE_RGGB, + &trust350fs_postprocessing, + "trust%04i.ppm" + } + }, + {"Clever CAM 360", 0x797, 0x8001, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing_and_flip, + "scope%04i.ppm" + } + }, + /* http://www.meade.com/sportsoptics/catalog/captureview/index.html */ + {"GrandTek ScopeCam", 0x797, 0x801c, { + jd350e, + BAYER_TILE_BGGR, + &jd350e_postprocessing_and_flip, + "scope%04i.ppm" + } + }, + /* http://www.sipixdigital.com/cameras/stylecam/ */ + /* Ids glanced from downloaded driver / .inf file. */ + {"SiPix Stylecam", 0xd64, 0x1001, { + jd350e, + BAYER_TILE_BGGR, + &flip_both, + "style%04i.ppm" + } + }, + /* http://www.umax.de/digicam/AstraPix320S.htm */ + /* reportedly has same ids as SiPix StyleCam and also + * looks identical. */ + {"UMAX AstraPix 320s", 0xd64, 0x1001, { + jd350e, + BAYER_TILE_BGGR, + &flip_both, + "astra%04i.ppm" + } + }, + + /* http://www.dlink.com/products/usb/dsc350/ */ + /* ids from driver download */ + {"D-Link DSC 350F", 0xd64, 0x1021, { + dlink350f, + BAYER_TILE_BGGR, + &dlink_dsc350f_postprocessing_and_flip_both, + "dlink%04i.ppm" + } + }, + {NULL,} +}; + +static int +pdc640_read_packet (GPPort *port, char *buf, int buf_size) +{ + int i; + char checksum, c; + + /* Calculate the checksum */ + for (i = 0, checksum = 0; i < buf_size; i++) + buf[i] = 0; + + /* Read the packet */ + CHECK_RESULT (gp_port_read (port, buf, buf_size)); + + /* Calculate the checksum */ + for (i = 0, checksum = 0; i < buf_size; i++) + checksum += buf[i]; + + /* Read the checksum */ + CHECK_RESULT (gp_port_read (port, &c, 1)); + GP_DEBUG ("Checksum: %d calculated, %d received", checksum, c); + if (checksum != c) + return (GP_ERROR_CORRUPTED_DATA); + + return (GP_OK); +} + +static int +pdc640_transmit (GPPort *port, char *cmd, int cmd_size, + char *buf, int buf_size) +{ + int r, tries; + + if (port->type == GP_PORT_USB) { + unsigned char xbuf[64]; + unsigned char xcmd[4]; + int checksum; + + /* USB has always just 3 bytes + 1 byte checksum */ + memset(xcmd,0,4);memcpy(xcmd,cmd,cmd_size); + checksum = (xcmd[0]^0x34)+(xcmd[1]^0xcb)+0x14f+(xcmd[2]^0x67); + xcmd[3] = checksum & 0xff; + + /* Wait until we get back the echo of the command. */ + r = gp_port_usb_msg_read( port, 0x10, xcmd[0]|(xcmd[1]<<8),xcmd[2]|(xcmd[3]<<8),xbuf,sizeof(xbuf)); + /* Sometimes we want to read here, sometimes not. + if (r < GP_OK) + return r; + */ + if (buf && buf_size) { + /* bulk read things. The JD350v can read in 1 block, + * the Stylocam returns only 64byte blocks here. + */ + int curr = 0, readsize = (buf_size + 63) & ~63; + while (curr < readsize) { + r = gp_port_read( port, buf + curr, readsize - curr); + if (r < GP_OK) break; + curr += r; + } + } + return r; + } else { + char c; + + /* In event of a checksum or timing failure, retry */ + for (tries = 0; tries < PDC640_MAXTRIES; tries++) { + /* + * The first byte returned is always the same as the first byte + * of the command. + */ + CHECK_RESULT (gp_port_write (port, cmd, cmd_size)); + r = gp_port_read (port, &c, 1); + if ((r < 0) || (c != cmd[0])) + continue; + + if (buf) { + r = pdc640_read_packet (port, buf, buf_size); + if (r < 0) + continue; + } + + return (GP_OK); + } + return (GP_ERROR_CORRUPTED_DATA); + } +} + + +static int +pdc640_transmit_pic (GPPort *port, char cmd, int width, int thumbnail, + char *buf, int buf_size) +{ + char cmd1[] = {0x61, 0x00}; + char cmd2[] = {0x15, 0x00, 0x00, 0x00, 0x00}; + + /* First send the command ... */ + cmd1[1] = cmd; + CHECK_RESULT (pdc640_transmit (port, cmd1, 2, NULL, 0)); + + if (port->type == GP_PORT_USB) { + cmd2[1] = (buf_size + 63) >> 6; + cmd2[2] = ((buf_size + 63) >> 6) >> 8; + return pdc640_transmit (port, cmd2, 4, buf, buf_size); + } else { + int i, packet_size, result, size, ofs; + char *data; + + /* Set how many scanlines worth of data to get at once */ + cmd2[4] = 0x06; + + /* Packet size is a multiple of the image width */ + packet_size = width * cmd2[4]; + + /* Allocate memory to temporarily store received data */ + data = malloc (packet_size); + if (!data) + return (GP_ERROR_NO_MEMORY); + + /* Now get the packets */ + ofs = 0; + result = GP_OK; + for (i = 0; i < buf_size; i += packet_size) { + /* Read the packet */ + result = pdc640_transmit (port, cmd2, 5, + data, packet_size); + if (result < 0) + break; + + /* Copy from temp buffer -> actual */ + size = packet_size; + if (size > buf_size - i) + size = buf_size - i; + memcpy(buf + i, data, size); + + /* Move to next offset */ + ofs += cmd2[4]; + cmd2[2] = ofs & 0xFF; + cmd2[1] = (ofs >> 8) & 0xFF; + } + free (data); + return (result); + } +} + +static int +pdc640_transmit_packet (GPPort *port, char cmd, char *buf, int buf_size) { + char cmd1[] = {0x61, 0x00}; + /* Send the command and get the packet */ + cmd1[1] = cmd; + CHECK_RESULT (pdc640_transmit (port, cmd1, 2, NULL, 0)); + + if (port->type == GP_PORT_USB) { + char cmd2[] = {0x15, 0x00, 0x00, 0x00}; + + cmd2[1] = ((buf_size+63)/64) & 0xff; + cmd2[2] = (((buf_size+63)/64) >> 8) & 0xff; + return pdc640_transmit (port, cmd2, 4, buf, buf_size); + } else { + char cmd2[] = {0x15, 0x00, 0x00, 0x00, 0x01}; + + return pdc640_transmit (port, cmd2, 5, buf, buf_size); + } +} + + +static int +pdc640_ping_low (GPPort *port) +{ + char cmd[] = {0x01}; + + CHECK_RESULT (pdc640_transmit (port, cmd, 1, NULL, 0)); + + return (GP_OK); +} + +#if 0 +static int +pdc640_push_button (GPPort *port) +{ + char cmd[] = {0xfd}; + + CHECK_RESULT (pdc640_transmit (port, cmd, 1, NULL, 0)); + + return (GP_OK); +} +#endif + +static int +pdc640_ping_high (GPPort *port) +{ + char cmd[] = {0x41}; + + CHECK_RESULT (pdc640_transmit (port, cmd, 1, NULL, 0)); + + return (GP_OK); +} + +static int +pdc640_speed (GPPort *port, int speed) +{ + char cmd[] = {0x69, 0x00}; + + cmd[1] = (speed / 9600) - 1; + CHECK_RESULT (pdc640_transmit (port, cmd, 2, NULL, 0)); + + return (GP_OK); +} + +#if 0 +/* read version I think */ +static int +pdc640_unknown5 (GPPort *port) +{ + char cmd[] = {0x05}; + char buf[3]; + + CHECK_RESULT (pdc640_transmit (port, cmd, 1, buf, 3)); + if ((buf[0] != 0x33) || (buf[1] != 0x02) || (buf[2] != 0x35)) + return (GP_ERROR_CORRUPTED_DATA); + + return (GP_OK); +} +#endif + +#if 0 +/* get calibration param */ +static int +pdc640_unknown20 (GPPort* port) +{ + char buf[128]; + + CHECK_RESULT (pdc640_transmit_packet (port, 0x20, buf, 128)); + + return (GP_OK); +} +#endif + +static int +pdc640_caminfo (GPPort *port, int *numpic) +{ + char buf[1280]; + + CHECK_RESULT (pdc640_transmit_packet (port, 0x40, buf, 1280)); + *numpic = buf[2]; /* thats the only useful info :( */ + return (GP_OK); +} + +static int +pdc640_setpic (GPPort *port, char n) +{ + char cmd[2] = {0xf6, 0x00}; + + cmd[1] = n; + if (port->type == GP_PORT_USB) { + /* USB does not like a bulkread afterwards */ + return pdc640_transmit (port, cmd, 2, NULL, 0); + } else { + char buf[8]; + + return pdc640_transmit (port, cmd, 2, buf, 7); + } +} + +static int +pdc640_picinfo (GPPort *port, char n, + int *size_pic, int *width_pic, int *height_pic, + int *size_thumb, int *width_thumb, int *height_thumb, + int *compression_type ) +{ + unsigned char buf[64]; + + CHECK_RESULT (pdc640_setpic (port, n)); + CHECK_RESULT (pdc640_transmit_packet (port, 0x80, buf, 32)); + + /* Check image number matches */ + if (buf[0] != n) + return (GP_ERROR_CORRUPTED_DATA); + + /* Picture size, width and height */ + *size_pic = buf[2] | (buf[3] << 8) | (buf[4] << 16); + *width_pic = buf[5] | (buf[6] << 8); + *height_pic = buf[7] | (buf[8] << 8); + + /* Compression Type */ + *compression_type = buf[9]; + + *size_thumb = buf[25] | (buf[26] << 8) | (buf[27] << 16); + + *width_thumb = buf[28] | (buf[29] << 8); + *height_thumb = buf[30] | (buf[31] << 8); + + /* Even though it should the be the correct size, the Typhoon + * Stylocam returns junk in there. So calculate for ourselves. + */ + if (*size_thumb > *width_thumb * *height_thumb) + *size_thumb = *width_thumb * *height_thumb; + return (GP_OK); +} + +static int +pdc640_processtn (int width, int height, char **data, int size) { + char *newdata; + int y; + + /* Sanity checks */ + if ((data == NULL) || (size < width * height)) + return (GP_ERROR_CORRUPTED_DATA); + + /* Allocate a new buffer */ + newdata = malloc(size); + if (!newdata) + return (GP_ERROR_NO_MEMORY); + + /* Flip the thumbnail */ + for (y = 0; y < height; y++) { + memcpy(&newdata[(height - y - 1) * width], + &((*data)[y * width]), width); + } + + /* Set new buffer */ + free (*data); + *data = newdata; + + return (GP_OK); +} + +static int +pdc640_getbit (char *data, int *ofs, int size, int *bit) { + static char c; + int b; + + /* Check if next byte required */ + if (*bit == 0) { + if (*ofs >= size) + return (-1); + + c = data[*ofs]; + (*ofs)++; + } + + /* Get current bit value */ + b = (c >> *bit) & 1; + + /* Then move onto the next bit */ + (*bit)++; + if (*bit >= 8) + *bit = 0; + + return (b); +} + +static int +pdc640_deltadecode (int width, int height, char **rawdata, int *rawsize) +{ + char col1, col2; + char *data; + int rawofs, x, y, ofs, bit, ones; + int size; + int e, d, o, val; + + GP_DEBUG ("pdc640_deltacode ()"); + + /* Create a buffer to store RGB data in */ + size = width * height; + data = malloc (size * sizeof (char)); + if (!data) + return (GP_ERROR_NO_MEMORY); + + /* Delta decode scanline by scanline */ + rawofs = 0; + for (y = height-1; y >= 0; y--) { + /* Word alignment */ + if (rawofs & 1) + rawofs++; + + /* Sanity check */ + if (rawofs >= *rawsize) { + free (data); + return (GP_ERROR_CORRUPTED_DATA); + } + + /* Offset into the uncompressed data */ + ofs = y * width; + + /* Get the first two pixel values */ + col1 = (*rawdata)[rawofs]; + col2 = (*rawdata)[rawofs + 1]; + rawofs += 2; + data[ofs + 0] = col1 << 1; + data[ofs + 1] = col2 << 1; + + /* Work out the remaining pixels */ + bit = 0; + for (x = 2; x < width; x++) { + /* Count number of ones */ + ones = 0; + while (pdc640_getbit(*rawdata, &rawofs, *rawsize, &bit) == 1) + ones++; + + /* + * Get the delta value + * (size dictated by number of ones) + */ + val = 0; + for (o = 0, e = 0, d = 1; o < ones; o++, d <<= 1) { + e = pdc640_getbit(*rawdata, &rawofs, *rawsize, &bit); + if (e == 1) val += d; + } + if (e == 0) val += 1 - d; /* adjust for negatives */ + + /* Adjust the corresponding pixel value */ + if (x & 1) + val = (col2 += val); + else + val = (col1 += val); + + data[ofs + x] = val << 1; + } + } + + /* Set new buffer */ + free (*rawdata); + *rawdata = data; + *rawsize = size; + + return (GP_OK); +} + +static int +pdc640_getpic (Camera *camera, int n, int thumbnail, int justraw, + char **data, int *size) +{ + char cmd, ppmheader[100]; + int size_pic, width_pic, height_pic; + int size_thumb, width_thumb, height_thumb; + int height, width, outsize, result, pmmhdr_len; + int compression_type; + char *outdata; + + /* Get the size of the picture */ + CHECK_RESULT (pdc640_picinfo (camera->port, n, + &size_pic, &width_pic, &height_pic, + &size_thumb, &width_thumb, &height_thumb, + &compression_type)); + + /* Evaluate parameters */ + if (thumbnail) { + GP_DEBUG ("Size %d, width %d, height %d, comptype %d", + size_thumb, width_thumb, height_thumb, + (compression_type>>2) & 3); + + *size = size_thumb; + width = width_thumb; + height = height_thumb; + if ((compression_type >> 2) & 3) + cmd = 0x02; + else + cmd = 0x03; + } else { + GP_DEBUG ("Size %d, width %d, height %d, comptype %d", + size_pic, width_pic, height_pic, compression_type & 3); + + *size = size_pic; + width = width_pic; + height = height_pic; + switch (compression_type & 3) { + case 1: + case 2: cmd = 0x10; /* delta compressed */ + break; + case 0: cmd = 0x00; /* uncompressed */ + break; + default: /* unknown compression type */ + GP_DEBUG ("Unknown compression type %d", compression_type & 3); + return (GP_ERROR_CORRUPTED_DATA); + } + } + + /* Sanity check */ + if ((*size <= 0) || (width <= 0) || (height <= 0)) + return (GP_ERROR_CORRUPTED_DATA); + + /* Allocate the memory, including 64byte align */ + *data = malloc ((*size + 64) * sizeof (char)); + if (!*data) + return (GP_ERROR_NO_MEMORY); + + /* Get the raw picture */ + CHECK_RESULT (pdc640_setpic (camera->port, n)); + CHECK_RESULT (pdc640_transmit_pic (camera->port, cmd, width, thumbnail, + *data, *size)); + + if (thumbnail || (compression_type == 0 )) { + /* Process uncompressed data */ + CHECK_RESULT (pdc640_processtn (width, height, + data, *size)); + } else if (compression_type & 3) { + /* Image data is delta encoded so decode it */ + CHECK_RESULT (pdc640_deltadecode (width, height, + data, size)); + } + + /* Just wanted the raw camera data */ + if (justraw) + return(GP_OK); + + GP_DEBUG ("Bayer decode..."); + sprintf (ppmheader, "P6\n" + "# CREATOR: gphoto2, pdc640/jd350e library\n" + "%d %d\n" + "255\n", width, height); + + /* Allocate memory for Interpolated ppm image */ + pmmhdr_len = strlen(ppmheader); + outsize = width * height * 3 + pmmhdr_len + 1; + outdata = malloc(outsize); + if (!outdata) + return (GP_ERROR_NO_MEMORY); + + /* Set header */ + strcpy(outdata, ppmheader); + + /* Decode and interpolate the Bayer Mask */ + result = gp_bayer_decode(*data, width, height, + &outdata[pmmhdr_len], camera->pl->bayer_tile ); + if (result < 0) { + free (outdata); + return (result); + } + + /* Call a specific postprocessor if available*/ + if( camera->pl->postprocessor ){ + result = camera->pl->postprocessor(width,height,&outdata[pmmhdr_len]); + if (result < 0) { + free (outdata); + return (result); + } + } + + /* Fix up data pointers */ + free (*data); + *data = outdata; + *size = outsize; + + return (GP_OK); +} + +static int +pdc640_delpic (GPPort *port) +{ + char cmd[2] = {0x59, 0x01}; + + CHECK_RESULT (pdc640_transmit (port, cmd, 2, NULL, 0)); + + return (GP_OK); +} + +static int +pdc640_takepic (GPPort *port) +{ + char cmd[2] = {0x2D, 0x00}; + + CHECK_RESULT (pdc640_transmit (port, cmd, 2, NULL, 0)); + + return (GP_OK); +} + +int +camera_id (CameraText *id) +{ + strcpy (id->text, "pdc640/jd350e"); + 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); + + if (models[i].vendor) { + a.status = GP_DRIVER_STATUS_TESTING; + a.port = GP_PORT_USB | GP_PORT_SERIAL; + a.usb_vendor = models[i].vendor; + a.usb_product = models[i].product; + a.speed[0] = 0; + } else { + a.status = GP_DRIVER_STATUS_EXPERIMENTAL; + a.port = GP_PORT_SERIAL; + a.speed[0] = 0; + } + + a.operations = GP_OPERATION_CAPTURE_IMAGE; + a.file_operations = GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_PREVIEW; + a.folder_operations = GP_FOLDER_OPERATION_NONE; + + CHECK_RESULT (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; + int n, size; + char *data, *p; + + /* + * Get the number of the picture from the filesystem and increment + * since we need a range starting with 1. + */ + CHECK_RESULT (n = gp_filesystem_number (camera->fs, folder, filename, + context)); + n++; + + CHECK_RESULT (gp_file_set_name (file, filename)); + + /* Get the picture */ + switch (type) { + case GP_FILE_TYPE_NORMAL: + CHECK_RESULT (pdc640_getpic (camera, n, 0, 0, &data, &size)); + CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_PPM)); + break; + case GP_FILE_TYPE_RAW: + CHECK_RESULT (pdc640_getpic (camera, n, 0, 1, &data, &size)); + CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_RAW)); + + /* Change extension to raw */ + n = strlen(filename); + p = malloc (n + 1); + if (p) { + strcpy (p, filename); + p[n-3] = 'r'; + p[n-2] = 'a'; + p[n-1] = 'w'; + CHECK_RESULT (gp_file_set_name (file, p)); + free (p); + } + break; + case GP_FILE_TYPE_PREVIEW: + CHECK_RESULT (pdc640_getpic (camera, n, 1, 0, &data, &size)); + CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_PPM)); + break; + default: + return (GP_ERROR_NOT_SUPPORTED); + } + + CHECK_RESULT (gp_file_set_data_and_size (file, data, size)); + + return (GP_OK); +} + +static int +delete_all_func (CameraFilesystem *fs, const char *folder, void *data, + GPContext *context) +{ + Camera *camera = data; + char cmd[2] = {0x59, 0x00}; + + CHECK_RESULT (pdc640_transmit (camera->port, cmd, 2, NULL, 0)); + + return (GP_OK); +} + +static int +delete_file_func (CameraFilesystem *fs, const char *folder, const char *file, + void *data, GPContext *context) +{ + Camera *camera = data; + int n, count; + + /* We can only delete the last picture */ + CHECK_RESULT (n = gp_filesystem_number (camera->fs, folder, file, + context)); + n++; + + CHECK_RESULT (pdc640_caminfo (camera->port, &count)); + if (count != n) + return (GP_ERROR_NOT_SUPPORTED); + + CHECK_RESULT (pdc640_delpic (camera->port)); + + return (GP_OK); +} + +static int +camera_about (Camera *camera, CameraText *about, GPContext *context) +{ + strcpy (about->text, _("Download program for GrandTek 98x based cameras. " + "Originally written by Chris Byrne <adapt@ihug.co.nz>, " + "and adapted for gphoto2 by Lutz Mueller " + "<lutz@users.sf.net>." + "Protocol enhancements and postprocessing " + " for Jenoptik JD350e by Michael Trawny " + "<trawny99@users.sourceforge.net>." + "Bugfixes by Marcus Meissner <marcus@jet.franken.de>.")); + + return (GP_OK); +} + +static int +camera_capture (Camera *camera, CameraCaptureType type, CameraFilePath *path, + GPContext *context) +{ + int num, numpic; + + if (type != GP_CAPTURE_IMAGE) + return (GP_ERROR_NOT_SUPPORTED); + + /* First get the current number of images */ + CHECK_RESULT (pdc640_caminfo (camera->port, &numpic)); + + /* Take a picture */ + CHECK_RESULT (pdc640_takepic (camera->port)); + + /* Wait a bit for the camera */ + sleep(4); + + /* Picture will be the last one in the camera */ + CHECK_RESULT (pdc640_caminfo (camera->port, &num)); + if (num <= numpic) + return (GP_ERROR); + + /* Set the filename */ + sprintf (path->name, camera->pl->filespec, num); + strcpy (path->folder, "/"); + + CHECK_RESULT (gp_filesystem_append (camera->fs, "/", path->name, + context)); + + 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 */ + CHECK_RESULT (pdc640_caminfo (camera->port, &n)); + CHECK_RESULT (gp_list_populate (list, camera->pl->filespec, n)); + + return (GP_OK); +} + +static int +get_info_func (CameraFilesystem *fs, const char *folder, const char *file, + CameraFileInfo *info, void *data, GPContext *context) +{ + Camera *camera = data; + int n, dummy; + int size_pic, size_thumb; + int width_pic, width_thumb, height_pic, height_thumb; + + CHECK_RESULT (n = gp_filesystem_number (fs, folder, file, context)); + n++; + + CHECK_RESULT (pdc640_picinfo (camera->port, n, + &size_pic, &width_pic, &height_pic, + &size_thumb, &width_thumb, &height_thumb, + &dummy )); + + info->file.fields = GP_FILE_INFO_SIZE | GP_FILE_INFO_WIDTH | + GP_FILE_INFO_HEIGHT | GP_FILE_INFO_TYPE; + info->file.width = width_pic; + info->file.height = height_pic; +/* + info->file.size = size_pic; +*/ + info->file.size = width_pic * height_pic * 3; + strcpy (info->file.type, GP_MIME_PPM); + + info->preview.fields = GP_FILE_INFO_SIZE | GP_FILE_INFO_WIDTH | + GP_FILE_INFO_HEIGHT | GP_FILE_INFO_TYPE; + info->preview.width = width_thumb; + info->preview.height = height_thumb; + info->preview.size = size_thumb * 3; + strcpy (info->preview.type, GP_MIME_PPM); + + return (GP_OK); +} + +static int +camera_exit (Camera *camera, GPContext *context) +{ + if (camera->pl) { + free (camera->pl); + camera->pl = NULL; + } + + return (GP_OK); +} + +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) +{ + int result, i; + GPPortSettings settings; + CameraAbilities abilities; + + /* + * First of all, tell gphoto2 about the functions we + * implement (especially camera_exit so that everything + * gets correctly cleaned up even in case of error). + */ + camera->functions->about = camera_about; + camera->functions->capture = camera_capture; + camera->functions->exit = camera_exit; + + CHECK_RESULT (gp_camera_get_abilities(camera,&abilities) ); + camera->pl = 0; + for( i=0; models[i].model; i++ ){ + if (!strcmp(models[i].model, abilities.model)) { + GP_DEBUG ("Model: %s", abilities.model); + camera->pl = malloc( sizeof(struct _CameraPrivateLibrary) ); + if( camera->pl ){ + *(camera->pl) = models[i].pl; + break; + } + else{ + return (GP_ERROR_NO_MEMORY); + } + } + } + if( ! camera->pl ){ + return (GP_ERROR_NOT_SUPPORTED); + } + /* Tell the filesystem where to get lists and info */ + CHECK_RESULT (gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera)); + + if (camera->port->type == GP_PORT_SERIAL) { + /* Open the port */ + CHECK_RESULT (gp_port_get_settings (camera->port, &settings)); + settings.serial.speed = 9600; + CHECK_RESULT (gp_port_set_settings (camera->port, settings)); + + /* Start with a low timeout (so we don't have to wait if already initialized) */ + CHECK_RESULT (gp_port_set_timeout (camera->port, 1000)); + + /* Is the camera at 9600? */ + result = pdc640_ping_low (camera->port); + if (result == GP_OK) + CHECK_RESULT (pdc640_speed (camera->port, 115200)); + + /* Switch to 115200 */ + settings.serial.speed = 115200; + CHECK_RESULT (gp_port_set_settings (camera->port, settings)); + + /* Is the camera at 115200? */ + CHECK_RESULT (pdc640_ping_high (camera->port)); + + /* Switch to a higher timeout */ + CHECK_RESULT (gp_port_set_timeout (camera->port, 5000)); + } + return (GP_OK); +} + +static int +flip_both (int width, int height, unsigned char *rgb) +{ + unsigned char *end, c; + + end = rgb + ((width * height) * 3); + while (rgb < end) { + c = *rgb; + *rgb++ = *--end; + *end = c; + } + + return GP_OK; +} + +static int +flip_vertical (int width, int height, unsigned char* rgb) { + int i; + unsigned char *buf; + + buf = malloc(width*3); + if (!buf) return GP_ERROR_NO_MEMORY; + for (i=0;i<height/2;i++) { + memcpy(buf,rgb+i*width*3,width*3); + memcpy(rgb+i*width*3,rgb+(height-i-1)*width*3,width*3); + memcpy(rgb+(height-i-1)*width*3,buf,width*3); + } + free(buf); + return GP_OK; +} |