This is basically an amendment to #1015, I just realized that I overlooked some places due to a too restrictive regex. (I hope this subject is not getting old already...) This is a whitespace only change (if you include newlines as whitespace).
975 lines
26 KiB
C
975 lines
26 KiB
C
/****************************************************************/
|
|
/* spca50x_sdram.c - Gphoto2 library for cameras with sunplus */
|
|
/* spca50x chips */
|
|
/* */
|
|
/* Copyright 2002, 2003 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., 51 Franklin Street, Fifth Floor,*/
|
|
/* Boston, MA 02110-1301 USA */
|
|
/****************************************************************/
|
|
#define _DEFAULT_SOURCE
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <gphoto2/gphoto2.h>
|
|
#include "libgphoto2/gphoto2-endian.h"
|
|
|
|
#include "libgphoto2/i18n.h"
|
|
|
|
#include "spca50x.h"
|
|
#include "spca50x-sdram.h"
|
|
#include "spca50x-registers.h"
|
|
#include "spca50x-avi-header.h"
|
|
|
|
|
|
#define GP_MODULE "spca50x"
|
|
|
|
static int spca50x_mode_set_idle (CameraPrivateLibrary * lib);
|
|
static int spca50x_is_idle (CameraPrivateLibrary * lib);
|
|
static int spca50x_mode_set_download (CameraPrivateLibrary * lib);
|
|
static int spca50x_download_data (CameraPrivateLibrary * lib, uint32_t start,
|
|
unsigned int size, uint8_t * buf);
|
|
static int spca50x_get_FATs (CameraPrivateLibrary * lib, int dramtype);
|
|
static int spca50x_sdram_get_file_count_and_fat_count (
|
|
CameraPrivateLibrary * lib, int dramtype);
|
|
static int spca50x_get_avi_thumbnail (CameraPrivateLibrary * lib,
|
|
uint8_t ** buf, unsigned int *len,
|
|
struct SPCA50xFile *g_file);
|
|
static int spca50x_get_image_thumbnail (CameraPrivateLibrary * lib,
|
|
uint8_t ** buf, unsigned int *len,
|
|
struct SPCA50xFile *g_file);
|
|
static int spca50x_get_avi (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file);
|
|
static int spca50x_get_image (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file);
|
|
static inline uint8_t *put_dword (uint8_t * ptr, uint32_t value);
|
|
|
|
|
|
static int
|
|
spca50x_sdram_get_fat_page (CameraPrivateLibrary * lib, int index,
|
|
int dramtype, uint8_t *p)
|
|
{
|
|
switch (dramtype) {
|
|
case 4: /* 128 Mbit */
|
|
CHECK (spca50x_download_data
|
|
(lib, 0x7fff80 -
|
|
index * 0x80,
|
|
SPCA50X_FAT_PAGE_SIZE, p));
|
|
break;
|
|
case 3: /* 64 Mbit */
|
|
CHECK (spca50x_download_data
|
|
(lib, 0x3fff80 -
|
|
index * 0x80,
|
|
SPCA50X_FAT_PAGE_SIZE, p));
|
|
break;
|
|
default:
|
|
gp_log(GP_LOG_ERROR, "spca50x", "spca50x_sdram_get_fat_page: dramtype %d unhandled", dramtype);
|
|
return GP_ERROR;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
spca50x_sdram_get_file_count_and_fat_count (CameraPrivateLibrary * lib,
|
|
int dramtype)
|
|
{
|
|
uint8_t theFat[256];
|
|
|
|
lib->num_fats = 0;
|
|
lib->num_files_on_sdram = 0;
|
|
|
|
if (lib->bridge == BRIDGE_SPCA500){
|
|
uint8_t lower, upper;
|
|
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0x5, 0, 0, NULL, 0));
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0xe15,
|
|
(char *) & lib->num_files_on_sdram, 1));
|
|
le32toh (lib->num_files_on_sdram);
|
|
|
|
/* get fatscount */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x05, 0x0000, 0x0008, NULL, 0));
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0x0e19,
|
|
(char *)&lower, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, 0x0e20,
|
|
(char *)&upper, 1));
|
|
|
|
lib->num_fats = (((upper & 0xFF) << 8) | (lower & 0xFF));
|
|
} else {
|
|
while (1) {
|
|
CHECK (spca50x_sdram_get_fat_page (lib, lib->num_fats,
|
|
dramtype, theFat));
|
|
if (theFat[0] == 0xFF)
|
|
break;
|
|
|
|
if (theFat[0] == 0x08 || theFat[0] == 0x00)
|
|
lib->num_files_on_sdram++;
|
|
|
|
lib->num_fats++;
|
|
}
|
|
}
|
|
return (GP_OK);
|
|
}
|
|
|
|
int
|
|
spca50x_sdram_delete_file (CameraPrivateLibrary * lib, unsigned int index)
|
|
{
|
|
struct SPCA50xFile *g_file;
|
|
uint16_t fat_index;
|
|
|
|
CHECK (spca50x_sdram_get_file_info (lib, index, &g_file));
|
|
|
|
if (lib->bridge == BRIDGE_SPCA500)
|
|
fat_index = 0x70FF - g_file->fat_start - 1;
|
|
else /* if (lib->bridge == BRIDGE_SPCA504) */
|
|
fat_index = 0xD8000 - g_file->fat_start - 1;
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x06, fat_index, 0x0007, NULL, 0));
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
|
|
/* Reread fats the next time it is accessed */
|
|
lib->dirty_sdram = 1;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca50x_sdram_delete_all (CameraPrivateLibrary * lib)
|
|
{
|
|
if (lib->fw_rev == 2) {
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x71, 0x0000, 0x0000, NULL, 0));
|
|
|
|
} else {
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x02, 0x0000, 0x0005, NULL, 0));
|
|
}
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (3);
|
|
#endif
|
|
|
|
/* Reread fats the next time it is accessed */
|
|
lib->dirty_sdram = 1;
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca50x_sdram_request_file (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, unsigned int number, int *type)
|
|
{
|
|
struct SPCA50xFile *g_file;
|
|
|
|
CHECK (spca50x_sdram_get_file_info (lib, number, &g_file));
|
|
|
|
*type = g_file->mime_type;
|
|
if (g_file->mime_type == SPCA50X_FILE_TYPE_AVI)
|
|
return (spca50x_get_avi (lib, buf, len, g_file));
|
|
else
|
|
return (spca50x_get_image (lib, buf, len, g_file));
|
|
}
|
|
|
|
static int
|
|
spca50x_get_image (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file)
|
|
{
|
|
uint8_t *p, *lp_jpg;
|
|
uint8_t qIndex = 0, format;
|
|
uint32_t start;
|
|
uint8_t *mybuf;
|
|
int size, o_size, file_size, ret;
|
|
int omit_escape = 0;
|
|
|
|
p = g_file->fat;
|
|
if (!p) return GP_ERROR;
|
|
|
|
/* 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 == BRIDGE_SPCA500) {
|
|
o_size = size = (p[5] & 0xff) * 0x100 + (p[6] & 0xff) * 0x10000;
|
|
qIndex = p[7] & 0x0f;
|
|
} else {
|
|
o_size = size =
|
|
(p[13] & 0xff) * 0x10000
|
|
+ (p[12] & 0xff) * 0x100
|
|
+ (p[11] & 0xff);
|
|
if (lib->fw_rev == 1) {
|
|
qIndex = p[7] & 0x0f;
|
|
} else if (lib->fw_rev == 2) {
|
|
omit_escape = 1;
|
|
qIndex = p[10] & 0x0f;
|
|
}
|
|
}
|
|
|
|
format = 0x21;
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
file_size = size + SPCA50X_JPG_DEFAULT_HEADER_LENGTH + 1024 * 10;
|
|
|
|
/* slurp in the image */
|
|
mybuf = malloc (size);
|
|
if (!mybuf)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
if (lib->bridge == BRIDGE_SPCA504) {
|
|
ret = spca50x_download_data (lib, start, size, mybuf);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
} else if (lib->bridge == BRIDGE_SPCA500) {
|
|
/* find the file index on the camera */
|
|
int index;
|
|
|
|
index = (g_file->fat - lib->fats) / SPCA50X_FAT_PAGE_SIZE;
|
|
spca50x_reset (lib);
|
|
/* trigger upload of image at that index */
|
|
ret = gp_port_usb_msg_write (lib->gpdev, 0x06,
|
|
0x70FF - index, 0x01, NULL, 0);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
ret = gp_port_read (lib->gpdev, (char *)mybuf, size);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
/* 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) {
|
|
free (mybuf);
|
|
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, omit_escape);
|
|
|
|
free (mybuf);
|
|
lp_jpg = realloc (lp_jpg, file_size);
|
|
*buf = lp_jpg;
|
|
*len = file_size;
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
spca50x_get_avi (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file)
|
|
{
|
|
int i, j, length, ret;
|
|
int frame_count = 0, frames_per_fat = 0, fn = 0;
|
|
unsigned int size = 0;
|
|
int file_size;
|
|
int index_size;
|
|
uint32_t frame_size = 0, frame_width = 0, frame_height = 0;
|
|
uint32_t total_frame_size = 0;
|
|
uint32_t start = 0;
|
|
uint8_t *p, *mybuf, *avi, *start_of_file, *start_of_frame, *data;
|
|
uint8_t qIndex;
|
|
uint8_t *avi_index, *avi_index_ptr;
|
|
uint8_t index_item[16];
|
|
|
|
/* FIXME */
|
|
if (lib->bridge == BRIDGE_SPCA500)
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
|
|
p = g_file->fat;
|
|
|
|
if (lib->fw_rev == 2)
|
|
qIndex = p[10] & 0x0f;
|
|
else
|
|
qIndex = p[7] & 0x0f;
|
|
|
|
avi = mybuf = start_of_file = data = NULL;
|
|
|
|
/* 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;
|
|
|
|
/* 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 += SPCA50X_FAT_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;
|
|
}
|
|
|
|
ret = spca50x_download_data (lib, start, size, mybuf);
|
|
if (ret < GP_OK) {
|
|
free (avi_index);
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
|
|
/* Now write our data to a file with an avi header */
|
|
file_size = size + SPCA50X_AVI_HEADER_LENGTH
|
|
+ (SPCA50X_JPG_DEFAULT_HEADER_LENGTH * frame_count)
|
|
+ 8 + index_size /* for index chunk */
|
|
+ 1024 * 10 * frame_count;
|
|
|
|
GP_DEBUG("file_size = %d", file_size);
|
|
avi = malloc (file_size);
|
|
if (!avi) {
|
|
free (avi_index);
|
|
free (mybuf);
|
|
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, SPCA50xAviHeader, SPCA50X_AVI_HEADER_LENGTH);
|
|
|
|
/* put the width and height into the riff header */
|
|
put_dword(avi + 0x40, frame_width);
|
|
put_dword(avi + 0x44, frame_height);
|
|
|
|
/* and at 0xb0 and 0xb4 */
|
|
put_dword(avi + 0xb0, frame_width);
|
|
put_dword(avi + 0xb4, frame_height);
|
|
|
|
avi += SPCA50X_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);
|
|
|
|
GP_DEBUG("frame_size = %d", frame_size);
|
|
|
|
if ((file_size - (avi - start_of_file)) < SPCA50X_AVI_FRAME_HEADER_LENGTH) {
|
|
free (mybuf);
|
|
GP_DEBUG("BAD: writing more than we allocated (%ld, %ld vs total %d)", (avi-start_of_file), (file_size - (avi - start_of_file)), SPCA50X_AVI_FRAME_HEADER_LENGTH);
|
|
return GP_ERROR_CORRUPTED_DATA;
|
|
}
|
|
|
|
memcpy (avi, SPCA50xAviFrameHeader,
|
|
SPCA50X_AVI_FRAME_HEADER_LENGTH);
|
|
|
|
avi += SPCA50X_AVI_FRAME_HEADER_LENGTH;
|
|
start_of_frame = avi;
|
|
|
|
/* jpeg starts here */
|
|
if ((data - mybuf) + frame_size > size) {
|
|
free (mybuf);
|
|
GP_DEBUG("BAD: accessing more than we read (%u vs total %d)", (unsigned int)((data-mybuf)+frame_size), size);
|
|
return GP_ERROR_CORRUPTED_DATA;
|
|
}
|
|
ret = create_jpeg_from_data (avi, data, qIndex, frame_width,
|
|
frame_height, 0x22, frame_size,
|
|
&length, 1, 0);
|
|
|
|
if (ret != GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
|
|
GP_DEBUG("avi added length = %d, width %d , height %d", length, frame_width, frame_height);
|
|
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 -
|
|
SPCA50X_AVI_HEADER_LENGTH - 4);
|
|
put_dword (index_item + 12, frame_size);
|
|
memcpy (avi_index_ptr, index_item, 16);
|
|
avi_index_ptr += 16;
|
|
fn++;
|
|
}
|
|
p += SPCA50X_FAT_PAGE_SIZE;
|
|
}
|
|
total_frame_size = avi - (start_of_file + SPCA50X_AVI_HEADER_LENGTH - 4);
|
|
put_dword (start_of_file + SPCA50X_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
|
|
spca50x_sdram_request_thumbnail (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, unsigned int number, int *type)
|
|
{
|
|
struct SPCA50xFile *g_file;
|
|
|
|
CHECK (spca50x_sdram_get_file_info (lib, number, &g_file));
|
|
|
|
*type = g_file->mime_type;
|
|
if (g_file->mime_type == SPCA50X_FILE_TYPE_AVI) {
|
|
return (spca50x_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 == BRIDGE_SPCA500
|
|
&& (g_file->fat[20] & 0xFF) == 2) {
|
|
return (spca50x_get_image (lib, buf, len, g_file));
|
|
} else {
|
|
return (spca50x_get_image_thumbnail
|
|
(lib, buf, len, g_file));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
spca50x_get_avi_thumbnail (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file)
|
|
{
|
|
uint8_t *p, *lp_jpg;
|
|
uint8_t qIndex;
|
|
uint32_t start;
|
|
uint8_t *mybuf;
|
|
int size, o_size, file_size;
|
|
int ret;
|
|
|
|
/* FIXME */
|
|
if (lib->bridge == 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] & 0x0f;
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
file_size = size + SPCA50X_JPG_DEFAULT_HEADER_LENGTH + 1024 * 10;
|
|
|
|
/* slurp in the image */
|
|
mybuf = malloc (size);
|
|
if (!mybuf)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
ret = spca50x_download_data (lib, start, size, mybuf);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
|
|
/* now build a jpeg */
|
|
lp_jpg = malloc (file_size);
|
|
if (!lp_jpg) {
|
|
free (mybuf);
|
|
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, 0);
|
|
free (mybuf);
|
|
lp_jpg = realloc (lp_jpg, file_size);
|
|
*buf = lp_jpg;
|
|
*len = file_size;
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
|
|
static int
|
|
spca50x_get_image_thumbnail (CameraPrivateLibrary * lib, uint8_t ** buf,
|
|
unsigned int *len, struct SPCA50xFile *g_file)
|
|
{
|
|
unsigned int size;
|
|
uint8_t *p;
|
|
uint32_t start;
|
|
uint8_t *mybuf = NULL;
|
|
uint8_t *tmp;
|
|
unsigned int t_width, t_height;
|
|
uint8_t *yuv_p;
|
|
uint8_t *rgb_p;
|
|
int headerlength, ret;
|
|
|
|
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;
|
|
/* Adjust the headerlenght */
|
|
headerlength = 13;
|
|
if (t_width > 99) headerlength++;
|
|
if (t_height > 99) headerlength++;
|
|
|
|
/* align */
|
|
if (size % 64 != 0)
|
|
size = ((size / 64) + 1) * 64;
|
|
|
|
mybuf = malloc (size);
|
|
|
|
if (lib->bridge == BRIDGE_SPCA504) {
|
|
ret = spca50x_download_data (lib, start, size, mybuf);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
} else if (lib->bridge == BRIDGE_SPCA500) {
|
|
/* find the file index on the camera */
|
|
int index;
|
|
|
|
index = (g_file->fat - lib->fats) / SPCA50X_FAT_PAGE_SIZE;
|
|
spca50x_reset (lib);
|
|
/* trigger upload of thumbnail at that index */
|
|
ret = gp_port_usb_msg_write (lib->gpdev, 0x06,
|
|
0x70FF - index, 0x09, NULL, 0);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
ret = gp_port_read (lib->gpdev, (char *)mybuf, size);
|
|
if (ret < GP_OK) {
|
|
free (mybuf);
|
|
return ret;
|
|
}
|
|
}
|
|
*len = t_width * t_height * 3 + headerlength;
|
|
*buf = malloc (*len);
|
|
if (!*buf) {
|
|
free (mybuf);
|
|
return (GP_ERROR_NO_MEMORY);
|
|
}
|
|
|
|
tmp = *buf;
|
|
snprintf ((char*)tmp, *len, "P6 %d %d 255\n", t_width, t_height);
|
|
tmp += headerlength;
|
|
|
|
yuv_p = mybuf;
|
|
rgb_p = tmp;
|
|
while (yuv_p < mybuf + (t_width * t_height * 2)) {
|
|
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
|
|
spca50x_sdram_get_info (CameraPrivateLibrary * lib)
|
|
{
|
|
int index;
|
|
uint8_t dramtype = 0;
|
|
uint8_t *p;
|
|
uint32_t start_page, end_page;
|
|
uint8_t file_type;
|
|
|
|
GP_DEBUG ("* spca50x_sdram_get_info");
|
|
|
|
if (lib->bridge == BRIDGE_SPCA504) {
|
|
if (!spca50x_is_idle (lib))
|
|
spca50x_mode_set_idle (lib);
|
|
|
|
spca50x_mode_set_download (lib);
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x00, 0x0001, SPCA50X_REG_AutoPbSize, NULL,
|
|
0));
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, SPCA50X_REG_DramType,
|
|
(char *) & dramtype, 1));
|
|
dramtype &= 0xFF;
|
|
}
|
|
|
|
CHECK (spca50x_sdram_get_file_count_and_fat_count (lib, dramtype));
|
|
|
|
if (lib->num_files_on_sdram > 0) {
|
|
CHECK (spca50x_get_FATs (lib, dramtype));
|
|
|
|
index = lib->sdram_files[lib->num_files_on_sdram - 1].fat_end;
|
|
if (index >= lib->num_fats) {
|
|
gp_log(GP_LOG_ERROR, "spca50x", "%d exceeds num_fats %d", index, lib->num_fats);
|
|
return GP_ERROR;
|
|
}
|
|
p = lib->fats + SPCA50X_FAT_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 == SPCA50X_FILE_TYPE_IMAGE)
|
|
end_page = end_page + 0xA0;
|
|
|
|
lib->size_used = (end_page - 0x2800) * SPCA50X_FAT_PAGE_SIZE;
|
|
} else
|
|
lib->size_used = 0;
|
|
|
|
lib->size_free =
|
|
16 * 1024 * 1024 - 0x2800 * SPCA50X_FAT_PAGE_SIZE - lib->size_used;
|
|
lib->dirty_sdram = 0;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca50x_sdram_get_file_info (CameraPrivateLibrary * lib, unsigned int index,
|
|
struct SPCA50xFile **g_file)
|
|
{
|
|
if (lib->dirty_sdram)
|
|
CHECK (spca50x_sdram_get_info (lib));
|
|
*g_file = &(lib->sdram_files[index]);
|
|
return GP_OK;
|
|
}
|
|
static int
|
|
spca50x_mode_set_idle (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, SPCA50X_CamMode_Idle, SPCA50X_REG_CamMode, NULL,
|
|
0));
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
spca50x_is_idle (CameraPrivateLibrary * lib)
|
|
{
|
|
int mode;
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, SPCA50X_REG_CamMode, (char *) & mode, 1));
|
|
|
|
return mode == SPCA50X_CamMode_Idle ? 1 : 0;
|
|
}
|
|
|
|
static int
|
|
spca50x_mode_set_download (CameraPrivateLibrary * lib)
|
|
{
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, SPCA50X_CamMode_Upload, SPCA50X_REG_CamMode, NULL,
|
|
0));
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
spca50x_download_data (CameraPrivateLibrary * lib, uint32_t start,
|
|
unsigned int size, uint8_t * buf)
|
|
{
|
|
|
|
uint8_t foo;
|
|
uint8_t vlcAddressL, vlcAddressM, vlcAddressH;
|
|
|
|
if (!spca50x_is_idle (lib))
|
|
spca50x_mode_set_idle (lib);
|
|
|
|
spca50x_mode_set_download (lib);
|
|
|
|
foo = size & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
SPCA50X_REG_SdramSizeL, NULL, 0));
|
|
foo = (size >> 8) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
SPCA50X_REG_SdramSizeM, NULL, 0));
|
|
foo = (size >> 16) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev, 0, foo,
|
|
SPCA50X_REG_SdramSizeH, NULL, 0));
|
|
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, SPCA50X_REG_VlcAddressL,
|
|
(char *) & vlcAddressL, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, SPCA50X_REG_VlcAddressM,
|
|
(char *) & vlcAddressM, 1));
|
|
CHECK (gp_port_usb_msg_read
|
|
(lib->gpdev, 0, 0, SPCA50X_REG_VlcAddressH,
|
|
(char *) & vlcAddressH, 1));
|
|
|
|
foo = start & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, SPCA50X_REG_VlcAddressL, NULL, 0));
|
|
foo = (start >> 8) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, SPCA50X_REG_VlcAddressM, NULL, 0));
|
|
foo = (start >> 16) & 0xFF;
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, foo, SPCA50X_REG_VlcAddressH, NULL, 0));
|
|
|
|
/* Set mode to ram -> usb */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, SPCA50X_DramUsb, SPCA50X_REG_PbSrc, NULL, 0));
|
|
/* and pull the trigger */
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, SPCA50X_TrigDramFifo, SPCA50X_REG_Trigger, NULL,
|
|
0));
|
|
|
|
CHECK (gp_port_read (lib->gpdev, (char *)buf, size));
|
|
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressL, SPCA50X_REG_VlcAddressL, NULL, 0));
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressM, SPCA50X_REG_VlcAddressM, NULL, 0));
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0, vlcAddressH, SPCA50X_REG_VlcAddressH, NULL, 0));
|
|
|
|
spca50x_mode_set_idle (lib);
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
spca50x_get_FATs (CameraPrivateLibrary * lib, int dramtype)
|
|
{
|
|
uint8_t type;
|
|
unsigned int index = 0;
|
|
unsigned int file_index = 0;
|
|
uint8_t *p = NULL;
|
|
|
|
/* Reset image and movie counter */
|
|
lib->num_images = lib->num_movies = 0;
|
|
|
|
free (lib->fats);
|
|
lib->fats = NULL;
|
|
|
|
free (lib->sdram_files);
|
|
lib->sdram_files = NULL;
|
|
|
|
lib->fats = calloc (lib->num_fats , SPCA50X_FAT_PAGE_SIZE);
|
|
lib->sdram_files = calloc (lib->num_files_on_sdram , sizeof (struct SPCA50xFile));
|
|
|
|
p = lib->fats;
|
|
if (lib->bridge == BRIDGE_SPCA504) {
|
|
while (index < (unsigned int)lib->num_fats) {
|
|
CHECK (spca50x_sdram_get_fat_page (lib, index,
|
|
dramtype, p));
|
|
if (p[0] == 0xFF)
|
|
break;
|
|
|
|
index++;
|
|
p += SPCA50X_FAT_PAGE_SIZE;
|
|
}
|
|
} else if (lib->bridge == BRIDGE_SPCA500) {
|
|
/* for the spca500, download the whole fat in one go. */
|
|
spca50x_reset (lib);
|
|
CHECK (gp_port_usb_msg_write
|
|
(lib->gpdev, 0x05, 0x00, 0x07, NULL, 0));
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
sleep (1);
|
|
#endif
|
|
CHECK (gp_port_read
|
|
(lib->gpdev, (char *)lib->fats,
|
|
lib->num_fats * SPCA50X_FAT_PAGE_SIZE));
|
|
}
|
|
|
|
p = lib->fats;
|
|
index = 0;
|
|
|
|
while (index < (unsigned int)lib->num_fats) {
|
|
if (file_index >= (unsigned int)lib->num_files_on_sdram) {
|
|
free (lib->fats); lib->fats = NULL;
|
|
free (lib->sdram_files); lib->sdram_files = NULL;
|
|
return GP_ERROR;
|
|
}
|
|
|
|
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 */
|
|
if (!file_index)
|
|
return GP_ERROR;
|
|
lib->sdram_files[file_index - 1].fat_end = index;
|
|
} else {
|
|
/* its an image */
|
|
if (type == 0x00 || type == 0x01) {
|
|
lib->sdram_files[file_index].name =
|
|
aprintf( "Image%03d.jpg", ++lib->num_images);
|
|
lib->sdram_files[file_index].mime_type =
|
|
SPCA50X_FILE_TYPE_IMAGE;
|
|
} else if ((type == 0x08) || (type == 0x03)) {
|
|
/* its the start of an avi */
|
|
lib->sdram_files[file_index].name =
|
|
aprintf ("Movie%03d.avi", ++lib->num_movies);
|
|
lib->sdram_files[file_index].mime_type =
|
|
SPCA50X_FILE_TYPE_AVI;
|
|
} else {
|
|
gp_log(GP_LOG_ERROR, "spca50x", "type %d unhandled - error", type);
|
|
return GP_ERROR;
|
|
}
|
|
lib->sdram_files[file_index].fat = p;
|
|
lib->sdram_files[file_index].fat_start = index;
|
|
lib->sdram_files[file_index].fat_end = index;
|
|
if (lib->bridge == BRIDGE_SPCA504) {
|
|
lib->sdram_files[file_index].width =
|
|
(p[8] & 0xFF) * 16;
|
|
lib->sdram_files[file_index].height =
|
|
(p[9] & 0xFF) * 16;
|
|
} else if (lib->bridge == BRIDGE_SPCA500) {
|
|
int w, h;
|
|
|
|
if (p[20] == 2) {
|
|
w = 320;
|
|
h = 240;
|
|
} else {
|
|
w = 640;
|
|
h = 480;
|
|
}
|
|
lib->sdram_files[file_index].width = w;
|
|
lib->sdram_files[file_index].height = h;
|
|
}
|
|
lib->sdram_files[file_index].thumb = NULL;
|
|
file_index++;
|
|
}
|
|
p += SPCA50X_FAT_PAGE_SIZE;
|
|
index++;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static inline uint8_t *
|
|
put_dword (uint8_t * ptr, uint32_t value)
|
|
{
|
|
ptr[0] = (value & 0xff);
|
|
ptr[1] = (value & 0xff00) >> 8;
|
|
ptr[2] = (value & 0xff0000) >> 16;
|
|
ptr[3] = (value & 0xff000000) >> 24;
|
|
return ptr + 4;
|
|
}
|