git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@5969 67ed7778-7388-44ab-90cf-0a291f65f57c
944 lines
25 KiB
C
944 lines
25 KiB
C
/****************************************************************/
|
|
/* gsmart.c - Gphoto2 library for the Mustek gSmart mini 2 */
|
|
/* */
|
|
/* Copyright © 2002 Till Adam */
|
|
/* */
|
|
/* Author: Till Adam <till@adam-lilienthal.de> */
|
|
/* */
|
|
/* This library 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 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 Library General Public License for */
|
|
/* more details. */
|
|
/* */
|
|
/* You should have received a copy of the GNU Library 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 <string.h>
|
|
#include <stdlib.h>
|
|
#include <gphoto2.h>
|
|
#include <gphoto2-endian.h>
|
|
|
|
#include "gsmart.h"
|
|
#include "gsmart-registers.h"
|
|
#include "gsmart-jpeg-header.h"
|
|
#include "gsmart-avi-header.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 "gsmartmini"
|
|
static int gsmart_mode_set_idle (CameraPrivateLibrary * lib);
|
|
static int gsmart_is_idle (CameraPrivateLibrary * lib);
|
|
static int gsmart_mode_set_download (CameraPrivateLibrary * lib);
|
|
static int gsmart_download_data (CameraPrivateLibrary * lib, u_int32_t start,
|
|
unsigned int size, u_int8_t * buf);
|
|
static int gsmart_get_FATs (CameraPrivateLibrary * lib);
|
|
static int yuv2rgb (int y, int u, int v, int *r, int *g, int *b);
|
|
static void create_jpeg_from_data (u_int8_t * dst, u_int8_t * src, int qIndex,
|
|
int w, int h, u_int8_t format,
|
|
int original_size, int *size,
|
|
int omit_huffman_table);
|
|
static inline u_int8_t *put_dword (u_int8_t * ptr, u_int32_t value);
|
|
static int gsmart_get_avi_thumbnail (CameraPrivateLibrary * lib,
|
|
u_int8_t ** buf, unsigned int *len,
|
|
struct GsmartFile *g_file);
|
|
static int gsmart_get_image_thumbnail (CameraPrivateLibrary * lib,
|
|
u_int8_t ** buf, unsigned int *len,
|
|
struct GsmartFile *g_file);
|
|
static int gsmart_get_avi (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file);
|
|
static int gsmart_get_image (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file);
|
|
|
|
int
|
|
gsmart_get_file_count (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0x5, 0, 0, NULL, 0));
|
|
sleep (1);
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0xe15, (u_int8_t *) & lib->num_files, 1));
|
|
LE32TOH (lib->num_files);
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
int
|
|
gsmart_delete_file (CameraPrivateLibrary * lib, unsigned int index)
|
|
{
|
|
struct GsmartFile *g_file;
|
|
u_int16_t fat_index;
|
|
|
|
CHECK (gsmart_get_file_info (lib, index, &g_file));
|
|
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA500)
|
|
fat_index = 0x70FF - g_file->fat_start - 1;
|
|
else /* if (lib->bridge == GSMART_BRIDGE_SPCA504A) */
|
|
fat_index = 0xD8000 - g_file->fat_start - 1;
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x06, fat_index, 0x0007, NULL, 0));
|
|
sleep (1);
|
|
|
|
/* Reread fats the next time it is accessed */
|
|
lib->dirty = 1;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
gsmart_delete_all (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x02, 0x0000, 0x0005, NULL, 0));
|
|
sleep (3);
|
|
|
|
/* Reread fats the next time it is accessed */
|
|
lib->dirty = 1;
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
gsmart_request_file (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, unsigned int number, int *type)
|
|
{
|
|
struct GsmartFile *g_file;
|
|
|
|
CHECK (gsmart_get_file_info (lib, number, &g_file));
|
|
|
|
*type = g_file->mime_type;
|
|
if (g_file->mime_type == GSMART_FILE_TYPE_AVI)
|
|
return (gsmart_get_avi (lib, buf, len, g_file));
|
|
else
|
|
return (gsmart_get_image (lib, buf, len, g_file));
|
|
}
|
|
|
|
int
|
|
gsmart_get_image (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file)
|
|
{
|
|
u_int8_t *p, *lp_jpg;
|
|
u_int8_t qIndex, format;
|
|
u_int32_t start;
|
|
u_int8_t *mybuf;
|
|
int size, o_size, file_size;
|
|
|
|
p = g_file->fat;
|
|
|
|
/* get the position in memory where the image is */
|
|
start = (p[1] & 0xff) + (p[2] & 0xff) * 0x100;
|
|
start *= 128;
|
|
|
|
/* decode the image size */
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
o_size = size =
|
|
(p[13] & 0xff) * 0x10000 + (p[12] & 0xff) * 0x100 +
|
|
(p[11] & 0xff);
|
|
} else { /* if (lib->bridge == GSMART_BRIDGE_SPCA500) */
|
|
o_size = size = (p[5] & 0xff) * 0x100 + (p[6] & 0xff);
|
|
}
|
|
qIndex = p[7] & 0x07;
|
|
format = 0x21;
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
file_size = size + GSMART_JPG_DEFAULT_HEADER_LENGTH + 1024 * 10;
|
|
|
|
/* slurp in the image */
|
|
mybuf = malloc (size);
|
|
if (!mybuf)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
CHECK (gsmart_download_data (lib, start, size, mybuf));
|
|
} else if (lib->bridge == GSMART_BRIDGE_SPCA500) {
|
|
/* find the file index on the camera */
|
|
int index;
|
|
|
|
index = (g_file->fat - lib->fats) / FLASH_PAGE_SIZE;
|
|
gsmart_reset (lib);
|
|
/* trigger upload of image at that index */
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0x06,
|
|
0x70FF - index, 0x01, NULL, 0));
|
|
sleep (1);
|
|
CHECK (gp_port_read (lib->gpdev, mybuf, size));
|
|
/* the smallest ones are in a different format */
|
|
if ( (p[20] & 0xff) == 2)
|
|
format=0x22;
|
|
}
|
|
/* now build a jpeg */
|
|
lp_jpg = malloc (file_size);
|
|
if (!lp_jpg)
|
|
return GP_ERROR_NO_MEMORY;
|
|
create_jpeg_from_data (lp_jpg, mybuf, qIndex, g_file->width,
|
|
g_file->height, format, o_size, &file_size, 0);
|
|
|
|
free (mybuf);
|
|
lp_jpg = realloc (lp_jpg, file_size);
|
|
*buf = lp_jpg;
|
|
*len = file_size;
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
int
|
|
gsmart_get_avi (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file)
|
|
{
|
|
int i, j, length;
|
|
int frame_count = 0, frames_per_fat = 0, fn = 0;
|
|
int size = 0;
|
|
int frame_width, frame_height;
|
|
int file_size;
|
|
int index_size;
|
|
u_int32_t frame_size = 0;
|
|
u_int32_t total_frame_size = 0;
|
|
u_int32_t start = 0;
|
|
u_int8_t *p, *mybuf, *avi, *start_of_file, *start_of_frame, *data;
|
|
u_int8_t qIndex;
|
|
u_int8_t *avi_index, *avi_index_ptr;
|
|
u_int8_t index_item[16];
|
|
|
|
/* FIXME */
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA500)
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
|
|
avi = mybuf = start_of_file = data = NULL;
|
|
|
|
p = g_file->fat;
|
|
/* get the position in memory where the movie is */
|
|
start = (p[1] & 0xff) + (p[2] & 0xff) * 0x100;
|
|
start *= 128;
|
|
|
|
/* Frame w and h and qIndex dont change, so just use the values
|
|
* of the first frame */
|
|
frame_width = (p[8] & 0xFF) * 16;
|
|
frame_height = (p[9] & 0xFF) * 16;
|
|
qIndex = p[7] & 0x07;
|
|
|
|
/* Each movie consists of a number of frames, and can have more than
|
|
* one fat page. Iterate over all fat pages for this movie and count
|
|
* all frames in all fats. Same for size. */
|
|
for (i = g_file->fat_start; i <= g_file->fat_end; i++) {
|
|
frames_per_fat = (p[49] & 0xFF) * 0x100 + (p[48] & 0xFF);
|
|
frame_count += frames_per_fat;
|
|
size += (p[13] & 0xFF) * 0x10000 + (p[12] & 0xFF) * 0x100 +
|
|
(p[11] & 0xFF);
|
|
if (frames_per_fat < 60)
|
|
break;
|
|
p += FLASH_PAGE_SIZE;
|
|
}
|
|
/* align */
|
|
size = (size + 63) & 0xffffffc0;
|
|
|
|
/* mem for index */
|
|
index_size = frame_count * 16;
|
|
avi_index_ptr = avi_index = malloc (index_size);
|
|
if (!avi_index)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
/* slurp in the movie */
|
|
mybuf = malloc (size);
|
|
if (!mybuf) {
|
|
free (avi_index);
|
|
return GP_ERROR_NO_MEMORY;
|
|
}
|
|
|
|
CHECK (gsmart_download_data (lib, start, size, mybuf));
|
|
|
|
/* Now write our data to a file with an avi header */
|
|
file_size = size + GSMART_AVI_HEADER_LENGTH
|
|
+ (GSMART_JPG_DEFAULT_HEADER_LENGTH * frame_count)
|
|
+ 8 + index_size /* for index chunk */
|
|
+ 1024 * 10 * frame_count;
|
|
|
|
avi = malloc (file_size);
|
|
if (!avi)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
start_of_file = avi;
|
|
|
|
/* prepare index item */
|
|
put_dword (index_item, 0x63643030); /* 00dc */
|
|
put_dword (index_item + 4, 0x10); // KEYFRAME
|
|
|
|
/* copy the header from the template */
|
|
memcpy (avi, GsmartAviHeader, GSMART_AVI_HEADER_LENGTH);
|
|
avi += GSMART_AVI_HEADER_LENGTH;
|
|
|
|
/* Reset to the first fat */
|
|
p = g_file->fat;
|
|
data = mybuf;
|
|
|
|
/* Iterate over fats and frames in each fat and build a jpeg for
|
|
* each frame. Add the jpeg to the avi and write an index entry. */
|
|
for (i = g_file->fat_start; i <= g_file->fat_end; i++) {
|
|
frames_per_fat = ((p[49] & 0xFF) * 0x100 + (p[48] & 0xFF));
|
|
|
|
/* frames per fat might be lying, so double check how
|
|
many frames were already processed */
|
|
if (frames_per_fat > 60 || frames_per_fat == 0
|
|
|| fn + frames_per_fat > frame_count)
|
|
break;
|
|
|
|
for (j = 0; j < frames_per_fat; j++) {
|
|
frame_size = ((p[52 + j * 3] & 0xFF) * 0x10000)
|
|
+ ((p[51 + j * 3] & 0xFF) * 0x100) +
|
|
(p[50 + j * 3] & 0xFF);
|
|
|
|
memcpy (avi, GsmartAviFrameHeader,
|
|
GSMART_AVI_FRAME_HEADER_LENGTH);
|
|
|
|
avi += GSMART_AVI_FRAME_HEADER_LENGTH;
|
|
start_of_frame = avi;
|
|
|
|
/* jpeg starts here */
|
|
create_jpeg_from_data (avi, data, qIndex, frame_width,
|
|
frame_height, 0x22, frame_size,
|
|
&length, 1);
|
|
|
|
data += (frame_size + 7) & 0xfffffff8;
|
|
avi += length;
|
|
/* Make sure the next frame is aligned */
|
|
if ((avi - start_of_frame) % 2 != 0)
|
|
avi++;
|
|
|
|
/* Now we know the real frame size after escaping and
|
|
* adding the EOI marker. Put it in the frame header. */
|
|
frame_size = avi - start_of_frame;
|
|
put_dword (start_of_frame - 4, frame_size);
|
|
put_dword (index_item + 8,
|
|
start_of_frame - start_of_file -
|
|
GSMART_AVI_HEADER_LENGTH - 4);
|
|
put_dword (index_item + 12, frame_size);
|
|
memcpy (avi_index_ptr, index_item, 16);
|
|
avi_index_ptr += 16;
|
|
fn++;
|
|
}
|
|
p += FLASH_PAGE_SIZE;
|
|
}
|
|
total_frame_size = avi - (start_of_file + GSMART_AVI_HEADER_LENGTH - 4);
|
|
put_dword (start_of_file + GSMART_AVI_HEADER_LENGTH - 8,
|
|
total_frame_size);
|
|
|
|
avi = put_dword (avi, 0x31786469); /* idx1 */
|
|
avi = put_dword (avi, index_size);
|
|
memcpy (avi, avi_index, index_size);
|
|
avi += index_size;
|
|
free (avi_index);
|
|
put_dword (start_of_file + 0x30, frame_count);
|
|
put_dword (start_of_file + 0x8c, frame_count);
|
|
|
|
/* And reuse total_frame_size for the file size RIFF marker at offset
|
|
* 4 */
|
|
total_frame_size = avi - (start_of_file + 4);
|
|
put_dword (start_of_file + 4, total_frame_size);
|
|
|
|
free (mybuf);
|
|
start_of_file = realloc (start_of_file, avi - start_of_file);
|
|
*buf = start_of_file;
|
|
*len = avi - start_of_file;
|
|
return (GP_OK);
|
|
}
|
|
|
|
int
|
|
gsmart_request_thumbnail (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, unsigned int number, int *type)
|
|
{
|
|
struct GsmartFile *g_file;
|
|
|
|
CHECK (gsmart_get_file_info (lib, number, &g_file));
|
|
|
|
*type = g_file->mime_type;
|
|
if (g_file->mime_type == GSMART_FILE_TYPE_AVI) {
|
|
return (gsmart_get_avi_thumbnail (lib, buf, len, g_file));
|
|
} else {
|
|
|
|
/* the spca500 stores the quality in p[20], the rest of them
|
|
* in [p40]. We need to check for the smallest resolution of
|
|
* the mini, since resolution does not have thumbnails.
|
|
* Download the full image instead.
|
|
* Low: 320x240 2
|
|
* Middle: 640x480 0
|
|
* High: 1024x768 (interpolated) 1*/
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA500
|
|
&& (g_file->fat[20] & 0xFF) == 2) {
|
|
return (gsmart_get_image (lib, buf, len, g_file));
|
|
|
|
} else {
|
|
return (gsmart_get_image_thumbnail
|
|
(lib, buf, len, g_file));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
gsmart_get_avi_thumbnail (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file)
|
|
{
|
|
u_int8_t *p, *lp_jpg;
|
|
u_int8_t qIndex;
|
|
u_int32_t start;
|
|
u_int8_t *mybuf;
|
|
int size, o_size, file_size;
|
|
int w, h;
|
|
|
|
/* FIXME */
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA500)
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
|
|
p = g_file->fat;
|
|
|
|
/* get the position in memory where the image is */
|
|
start = (p[1] & 0xff) + (p[2] & 0xff) * 0x100;
|
|
start *= 128;
|
|
|
|
/* decode the image size */
|
|
o_size = size =
|
|
(p[52] & 0xff) * 0x10000 + (p[51] & 0xff) * 0x100 +
|
|
(p[50] & 0xff);
|
|
qIndex = p[7] & 0x07;
|
|
w = (int) ((p[8] & 0xFF) * 16);
|
|
h = (int) ((p[9] & 0xFF) * 16);
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
file_size = size + GSMART_JPG_DEFAULT_HEADER_LENGTH + 1024 * 10;
|
|
|
|
/* slurp in the image */
|
|
mybuf = malloc (size);
|
|
if (!mybuf)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
CHECK (gsmart_download_data (lib, start, size, mybuf));
|
|
|
|
/* now build a jpeg */
|
|
lp_jpg = malloc (file_size);
|
|
if (!lp_jpg)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
create_jpeg_from_data (lp_jpg, mybuf, qIndex, g_file->width,
|
|
g_file->height, 0x22, o_size, &file_size, 0);
|
|
free (mybuf);
|
|
lp_jpg = realloc (lp_jpg, file_size);
|
|
*buf = lp_jpg;
|
|
*len = file_size;
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
|
|
static int
|
|
gsmart_get_image_thumbnail (CameraPrivateLibrary * lib, u_int8_t ** buf,
|
|
unsigned int *len, struct GsmartFile *g_file)
|
|
{
|
|
unsigned int size;
|
|
u_int8_t *p;
|
|
u_int32_t start;
|
|
u_int8_t *mybuf = NULL;
|
|
u_int8_t *tmp;
|
|
unsigned int t_width, t_height;
|
|
u_int8_t *yuv_p;
|
|
u_int8_t *rgb_p;
|
|
unsigned char pbm_header[16];
|
|
|
|
p = g_file->fat;
|
|
|
|
start = (p[3] & 0xff) + (p[4] & 0xff) * 0x100;
|
|
start *= 128;
|
|
|
|
size = g_file->width * g_file->height * 2 / 64;
|
|
t_width = g_file->width / 8;
|
|
t_height = g_file->height / 8;
|
|
snprintf (pbm_header, sizeof (pbm_header), "P6 %d %d 255\n", t_width,
|
|
t_height);
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
mybuf = malloc (size);
|
|
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
CHECK (gsmart_download_data (lib, start, size, mybuf));
|
|
} else if (lib->bridge == GSMART_BRIDGE_SPCA500) {
|
|
/* find the file index on the camera */
|
|
int index;
|
|
|
|
index = (g_file->fat - lib->fats) / FLASH_PAGE_SIZE;
|
|
gsmart_reset (lib);
|
|
/* trigger upload of thumbnail at that index */
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0x06,
|
|
0x70FF - index, 0x09, NULL, 0));
|
|
sleep (1);
|
|
CHECK (gp_port_read (lib->gpdev, mybuf, size));
|
|
}
|
|
*len = t_width * t_height * 3 + sizeof (pbm_header);
|
|
*len += 45;
|
|
*buf = malloc (*len);
|
|
if (!*buf)
|
|
return (GP_ERROR_NO_MEMORY);
|
|
|
|
tmp = *buf;
|
|
snprintf (tmp, sizeof (pbm_header), pbm_header);
|
|
tmp += sizeof (pbm_header) - 1;
|
|
|
|
yuv_p = mybuf;
|
|
rgb_p = tmp;
|
|
while (yuv_p < mybuf + size) {
|
|
unsigned int u, v, y, y2;
|
|
unsigned int r, g, b;
|
|
|
|
y = yuv_p[0];
|
|
y2 = yuv_p[1];
|
|
u = yuv_p[2];
|
|
v = yuv_p[3];
|
|
|
|
CHECK (yuv2rgb (y, u, v, &r, &g, &b));
|
|
*rgb_p++ = r;
|
|
*rgb_p++ = g;
|
|
*rgb_p++ = b;
|
|
|
|
CHECK (yuv2rgb (y2, u, v, &r, &g, &b));
|
|
*rgb_p++ = r;
|
|
*rgb_p++ = g;
|
|
*rgb_p++ = b;
|
|
|
|
yuv_p += 4;
|
|
}
|
|
|
|
free (mybuf);
|
|
return (GP_OK);
|
|
}
|
|
|
|
|
|
int
|
|
gsmart_get_info (CameraPrivateLibrary * lib)
|
|
{
|
|
unsigned int index;
|
|
u_int8_t *p;
|
|
u_int32_t start_page, end_page;
|
|
u_int8_t file_type;
|
|
|
|
GP_DEBUG ("* gsmart_get_info");
|
|
CHECK (gsmart_get_file_count (lib));
|
|
if (lib->num_files > 0) {
|
|
CHECK (gsmart_get_FATs (lib));
|
|
|
|
index = lib->files[lib->num_files - 1].fat_end;
|
|
p = lib->fats + FLASH_PAGE_SIZE * index;
|
|
/* p now points to the fat of the last image of the last file */
|
|
|
|
file_type = p[0];
|
|
start_page = ((p[2] & 0xFF) << 8) | (p[1] & 0xFF);
|
|
end_page = start_page + (((p[6] & 0xFF) << 8) | (p[5] & 0xFF));
|
|
if (file_type == GSMART_FILE_TYPE_IMAGE)
|
|
end_page = end_page + 0xA0;
|
|
|
|
lib->size_used = (end_page - 0x2800) * FLASH_PAGE_SIZE;
|
|
} else
|
|
lib->size_used = 0;
|
|
|
|
lib->size_free =
|
|
16 * 1024 * 1024 - 0x2800 * FLASH_PAGE_SIZE - lib->size_used;
|
|
lib->dirty = 0;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
gsmart_get_file_info (CameraPrivateLibrary * lib, unsigned int index,
|
|
struct GsmartFile **g_file)
|
|
{
|
|
if (lib->dirty)
|
|
CHECK (gsmart_get_info (lib));
|
|
*g_file = &(lib->files[index]);
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
gsmart_get_firmware_revision (CameraPrivateLibrary *lib)
|
|
{
|
|
u_int8_t firmware = 0;
|
|
CHECK (gp_port_usb_msg_read (lib->gpdev, 0x20, 0x0, 0x0, &firmware, 1));
|
|
return firmware;
|
|
}
|
|
int
|
|
gsmart_reset (CameraPrivateLibrary * lib)
|
|
{
|
|
GP_DEBUG ("* gsmart_reset");
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA500) {
|
|
/* This is not reset but "Change Mode to Idle (Clear Buffer)"
|
|
* Cant hurt, I guess. */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x02, 0x0000, 0x07, NULL, 0));
|
|
} else if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x02, 0x0000, 0x0003, NULL, 0));
|
|
}
|
|
sleep (1);
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
gsmart_capture (CameraPrivateLibrary * lib)
|
|
{
|
|
sleep (2);
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x06, 0x0000, 0x0003, NULL, 0));
|
|
sleep (3);
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
gsmart_mode_set_idle (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, GSMART_CamMode_Idle, GSMART_REG_CamMode, NULL,
|
|
0));
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
gsmart_is_idle (CameraPrivateLibrary * lib)
|
|
{
|
|
int mode;
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, GSMART_REG_CamMode, (u_int8_t *) & mode, 1));
|
|
|
|
return mode == GSMART_CamMode_Idle ? 1 : 0;
|
|
}
|
|
|
|
static int
|
|
gsmart_mode_set_download (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, GSMART_CamMode_Upload, GSMART_REG_CamMode, NULL,
|
|
0));
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
gsmart_download_data (CameraPrivateLibrary * lib, u_int32_t start,
|
|
unsigned int size, u_int8_t * buf)
|
|
{
|
|
|
|
u_int8_t foo;
|
|
u_int8_t vlcAddressL, vlcAddressM, vlcAddressH;
|
|
|
|
if (!gsmart_is_idle (lib))
|
|
gsmart_mode_set_idle (lib);
|
|
|
|
gsmart_mode_set_download (lib);
|
|
|
|
foo = size & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
GSMART_REG_SdramSizeL, NULL, 0));
|
|
foo = (size >> 8) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
GSMART_REG_SdramSizeM, NULL, 0));
|
|
foo = (size >> 16) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
GSMART_REG_SdramSizeH, NULL, 0));
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, GSMART_REG_VlcAddressL,
|
|
(u_int8_t *) & vlcAddressL, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, GSMART_REG_VlcAddressM,
|
|
(u_int8_t *) & vlcAddressM, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, GSMART_REG_VlcAddressH,
|
|
(u_int8_t *) & vlcAddressH, 1));
|
|
|
|
foo = start & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, GSMART_REG_VlcAddressL, NULL, 0));
|
|
foo = (start >> 8) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, GSMART_REG_VlcAddressM, NULL, 0));
|
|
foo = (start >> 16) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, GSMART_REG_VlcAddressH, NULL, 0));
|
|
|
|
/* Set mode to ram -> usb */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, GSMART_DramUsb, GSMART_REG_PbSrc, NULL, 0));
|
|
/* and pull the trigger */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, GSMART_TrigDramFifo, GSMART_REG_Trigger, NULL,
|
|
0));
|
|
|
|
CHECK (gp_port_read (lib->gpdev, buf, size));
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressL, GSMART_REG_VlcAddressL, NULL, 0));
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressM, GSMART_REG_VlcAddressM, NULL, 0));
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressH, GSMART_REG_VlcAddressH, NULL, 0));
|
|
|
|
gsmart_mode_set_idle (lib);
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
gsmart_get_FATs (CameraPrivateLibrary * lib)
|
|
{
|
|
u_int8_t dramtype, type;
|
|
u_int8_t lower, upper;
|
|
u_int32_t fatscount;
|
|
unsigned int index = 0;
|
|
unsigned int file_index = 0;
|
|
u_int8_t *p = NULL;
|
|
u_int8_t buf[14];
|
|
|
|
/* Reset image and movie counter */
|
|
lib->num_images = lib->num_movies = 0;
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x05, 0x0000, 0x0008, NULL, 0));
|
|
|
|
/* The spca500 is a little slower */
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A)
|
|
sleep (0.1);
|
|
else if (lib->bridge == GSMART_BRIDGE_SPCA500)
|
|
sleep (1.0);
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0x0e19, (u_int8_t *) & lower, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0x0e20, (u_int8_t *) & upper, 1));
|
|
|
|
fatscount = ((upper & 0xFF << 8) | (lower & 0xFF));
|
|
|
|
if (lib->fats)
|
|
free (lib->fats);
|
|
lib->fats = malloc (fatscount * FLASH_PAGE_SIZE);
|
|
|
|
if (lib->files)
|
|
free (lib->files);
|
|
lib->files = malloc (lib->num_files * sizeof (struct GsmartFile));
|
|
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
if (!gsmart_is_idle (lib))
|
|
gsmart_mode_set_idle (lib);
|
|
|
|
gsmart_mode_set_download (lib);
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x00, 0x0001, GSMART_REG_AutoPbSize, NULL,
|
|
0));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, GSMART_REG_DramType,
|
|
(u_int8_t *) & dramtype, 1));
|
|
|
|
dramtype &= 0xFF;
|
|
} else if (lib->bridge == GSMART_BRIDGE_SPCA500) {
|
|
/* for the spca500, download the whole fat in one go. */
|
|
gsmart_reset (lib);
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x05, 0x00, 0x07, NULL, 0));
|
|
sleep (1);
|
|
CHECK (gp_port_read
|
|
(lib->gpdev, lib->fats, fatscount * FLASH_PAGE_SIZE));
|
|
}
|
|
|
|
p = lib->fats;
|
|
|
|
while (index < fatscount) {
|
|
switch (dramtype) {
|
|
case 4: /* 128 Mbit */
|
|
CHECK (gsmart_download_data
|
|
(lib, 0x7fff80 - index * 0x80,
|
|
FLASH_PAGE_SIZE, p));
|
|
break;
|
|
case 3: /* 64 Mbit */
|
|
CHECK (gsmart_download_data
|
|
(lib, 0x3fff80 - index * 0x80,
|
|
FLASH_PAGE_SIZE, p));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (p[0] == 0xFF)
|
|
break;
|
|
type = p[0];
|
|
|
|
/* While the spca504a indicates start of avi as 0x08 and cont.
|
|
* of avi as 0x80, the spca500 uses 0x03 for both. Each frame
|
|
* gets its own fat table with a sequence number at p[18]. */
|
|
if ((type == 0x80) || (type == 0x03 && (p[18] != 0x00))) {
|
|
/* continuation of an avi */
|
|
lib->files[file_index - 1].fat_end = index;
|
|
} else {
|
|
/* its an image */
|
|
if (type == 0x00 || type == 0x01) {
|
|
snprintf (buf, 13, "Image%03d.jpg",
|
|
++lib->num_images);
|
|
lib->files[file_index].mime_type =
|
|
GSMART_FILE_TYPE_IMAGE;
|
|
lib->files[file_index].fat_end = index;
|
|
} else if ((type == 0x08) || (type == 0x03)) {
|
|
/* its the start of an avi */
|
|
snprintf (buf, 13, "Movie%03d.avi",
|
|
++lib->num_movies);
|
|
lib->files[file_index].mime_type =
|
|
GSMART_FILE_TYPE_AVI;
|
|
}
|
|
lib->files[file_index].fat = p;
|
|
lib->files[file_index].fat_start = index;
|
|
lib->files[file_index].name = strdup (buf);
|
|
if (lib->bridge == GSMART_BRIDGE_SPCA504A) {
|
|
lib->files[file_index].width =
|
|
(p[8] & 0xFF) * 16;
|
|
lib->files[file_index].height =
|
|
(p[9] & 0xFF) * 16;
|
|
} else if (lib->bridge == GSMART_BRIDGE_SPCA500) {
|
|
int w, h;
|
|
|
|
if (p[20] == 2) {
|
|
w = 320;
|
|
h = 240;
|
|
} else {
|
|
w = 640;
|
|
h = 480;
|
|
}
|
|
lib->files[file_index].width = w;
|
|
lib->files[file_index].height = h;
|
|
}
|
|
file_index++;
|
|
}
|
|
p += FLASH_PAGE_SIZE;
|
|
index++;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
yuv2rgb (int y, int u, int v, int *_r, int *_g, int *_b)
|
|
{
|
|
double r, g, b;
|
|
|
|
r = (char) y + 128 + 1.402 * (char) v;
|
|
g = (char) y + 128 - 0.34414 * (char) u - 0.71414 * (char) v;
|
|
b = (char) y + 128 + 1.772 * (char) u;
|
|
|
|
if (r > 255)
|
|
r = 255;
|
|
if (g > 255)
|
|
g = 255;
|
|
if (b > 255)
|
|
b = 255;
|
|
if (r < 0)
|
|
r = 0;
|
|
if (g < 0)
|
|
g = 0;
|
|
if (b < 0)
|
|
b = 0;
|
|
|
|
*_r = r;
|
|
*_g = g;
|
|
*_b = b;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static void
|
|
create_jpeg_from_data (u_int8_t * dst, u_int8_t * src, int qIndex, int w,
|
|
int h, u_int8_t format, int o_size, int *size, int omit_huffman_table)
|
|
{
|
|
|
|
int i = 0;
|
|
u_int8_t *start;
|
|
u_int8_t value;
|
|
|
|
start = dst;
|
|
/* copy the header from the template */
|
|
memcpy (dst, GsmartJPGDefaultHeaderPart1, GSMART_JPG_DEFAULT_HEADER_PART1_LENGTH);
|
|
|
|
/* modify quantization table */
|
|
memcpy (dst + 7, GsmartQTable[qIndex * 2], 64);
|
|
memcpy (dst + 72, GsmartQTable[qIndex * 2 + 1], 64);
|
|
|
|
dst += GSMART_JPG_DEFAULT_HEADER_PART1_LENGTH;
|
|
|
|
/* copy Huffman table */
|
|
if (!omit_huffman_table) {
|
|
memcpy (dst, GsmartJPGDefaultHeaderPart2, GSMART_JPG_DEFAULT_HEADER_PART2_LENGTH);
|
|
dst += GSMART_JPG_DEFAULT_HEADER_PART2_LENGTH;
|
|
}
|
|
memcpy (dst, GsmartJPGDefaultHeaderPart3, GSMART_JPG_DEFAULT_HEADER_PART3_LENGTH);
|
|
|
|
/* modify the image width, height */
|
|
*(dst + 8) = w & 0xFF; //Image width low byte
|
|
*(dst + 7) = w >> 8 & 0xFF; //Image width high byte
|
|
*(dst + 6) = h & 0xFF; //Image height low byte
|
|
*(dst + 5) = h >> 8 & 0xFF; //Image height high byte
|
|
|
|
/* set the format */
|
|
*(dst + 11) = format;
|
|
|
|
/* point to real JPG compress data start position and copy */
|
|
dst += GSMART_JPG_DEFAULT_HEADER_PART3_LENGTH;
|
|
|
|
for (i = 0; i < o_size; i++) {
|
|
value = *(src + i) & 0xFF;
|
|
*(dst) = value;
|
|
dst++;
|
|
if (value == 0xFF) {
|
|
*(dst) = 0x00;
|
|
dst++;
|
|
}
|
|
}
|
|
/* Add end of image marker */
|
|
*(dst++) = 0xFF;
|
|
*(dst++) = 0xD9;
|
|
|
|
*size = dst - start;
|
|
}
|
|
|
|
static inline u_int8_t *
|
|
put_dword (u_int8_t * ptr, u_int32_t value)
|
|
{
|
|
ptr[0] = (value & 0xff);
|
|
ptr[1] = (value & 0xff00) >> 8;
|
|
ptr[2] = (value & 0xff0000) >> 16;
|
|
ptr[3] = (value & 0xff000000) >> 24;
|
|
return ptr + 4;
|
|
}
|