Martyn Ranyard <ranyardm@lineone.net> git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@5967 67ed7778-7388-44ab-90cf-0a291f65f57c
398 lines
12 KiB
C
398 lines
12 KiB
C
/****************************************************************/
|
|
/* spca504_flash.c - Gphoto2 library for cameras with a sunplus */
|
|
/* spca504 chip and flash memory */
|
|
/* */
|
|
/* */
|
|
/* Authors: Till Adam <till@adam-lilienthal.de> */
|
|
/* */
|
|
/* based on work done by: */
|
|
/* Mark A. Zimmerman <mark@foresthaven.com> */
|
|
/* */
|
|
/* 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 <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "spca504_flash.h"
|
|
|
|
#include <gphoto2.h>
|
|
#include <gphoto2-port.h>
|
|
#include <gphoto2-port-log.h>
|
|
|
|
#define GP_MODULE "spca504_flash"
|
|
|
|
static int yuv2rgb (int y, int u, int v, int *_r, int *_g, int *_b);
|
|
|
|
static int
|
|
spca504_flash_wait_for_ready(GPPort *port)
|
|
{
|
|
/* FIXME tweak this. What is reasonable? 30 s seems a bit long */
|
|
int timeout = 30;
|
|
u_int8_t ready = 0;
|
|
while (timeout--) {
|
|
sleep(1);
|
|
CHECK (gp_port_usb_msg_read (port, 0x0b, 0x0000, 0x0004,
|
|
(char*)&ready, 0x01));
|
|
if (ready)
|
|
return GP_OK;
|
|
}
|
|
return GP_ERROR;
|
|
}
|
|
|
|
int
|
|
spca504_flash_get_TOC(CameraPrivateLibrary *pl, int *filecount)
|
|
{
|
|
u_int16_t n_toc_entries;
|
|
int toc_size = 0;
|
|
|
|
if (!pl->dirty)
|
|
return GP_OK;
|
|
CHECK (gp_port_usb_msg_read (pl->gpdev, 0x0b, 0x0000, 0x0000,
|
|
(char*)&n_toc_entries, 0x02));
|
|
/* Each file gets two toc entries, one for the image, one for the
|
|
* thumbnail */
|
|
*filecount = n_toc_entries/2;
|
|
|
|
/* If empty, return now */
|
|
if (n_toc_entries == 0)
|
|
return GP_OK;
|
|
|
|
/* Request the TOC */
|
|
CHECK (gp_port_usb_msg_read (pl->gpdev, 0x0a, n_toc_entries, 0x000c,
|
|
NULL, 0x00));
|
|
|
|
/* slurp it in. Supposedly there's 32 bytes in each entry. */
|
|
toc_size = n_toc_entries * 32;
|
|
/* align */
|
|
if (toc_size % 512 != 0)
|
|
toc_size = ((toc_size / 512) + 1) * 512;
|
|
|
|
if (pl->toc)
|
|
free (pl->toc);
|
|
pl->toc = malloc(toc_size);
|
|
if (!pl->toc)
|
|
return GP_ERROR_NO_MEMORY;
|
|
CHECK (spca504_flash_wait_for_ready(pl->gpdev));
|
|
CHECK (gp_port_read (pl->gpdev, pl->toc, toc_size));
|
|
|
|
pl->dirty = 0;
|
|
return GP_OK;
|
|
}
|
|
|
|
|
|
int
|
|
spca504_flash_get_filecount (GPPort *port, int *filecount)
|
|
{
|
|
u_int16_t response = 0;
|
|
CHECK (gp_port_usb_msg_read (port, 0x0b, 0x0000, 0x0000,
|
|
(char*)&response, 0x02));
|
|
/* Each file gets two toc entries, one for the image, one for the
|
|
* thumbnail */
|
|
*filecount = response/2;
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca504_flash_get_file_name (CameraPrivateLibrary *lib, int index, char *name)
|
|
{
|
|
u_int8_t *p;
|
|
|
|
p = lib->toc + index*2*32;
|
|
memcpy (name, p, 8);
|
|
name[8] = '.';
|
|
memcpy (name+9, p+8, 3);
|
|
name[12] = '\0';
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca504_flash_get_file_dimensions (CameraPrivateLibrary *lib, int index, int *w, int *h)
|
|
{
|
|
u_int8_t *p;
|
|
|
|
p = lib->toc + index*2*32;
|
|
*w = (p[0x0c] & 0xff) + (p[0x0d] & 0xff) * 0x100;
|
|
*h = (p[0x0e] & 0xff) + (p[0x0f] & 0xff) * 0x100;
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca504_flash_get_file_size (CameraPrivateLibrary *lib, int index, int *size)
|
|
{
|
|
u_int8_t *p;
|
|
|
|
p = lib->toc + index*2*32;
|
|
*size = (p[0x1c] & 0xff)
|
|
+ (p[0x1d] & 0xff) * 0x100
|
|
+ (p[0x1e] & 0xff) * 0x10000
|
|
+ (p[0x1f] & 0xff) * 0x1000000;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
spca504_flash_get_file (CameraPrivateLibrary *lib, GPContext *context,
|
|
u_int8_t ** data, unsigned int *len, int index, int thumbnail)
|
|
{
|
|
u_int32_t file_size = 0, aligned_size = 0;
|
|
u_int32_t addr = 0;
|
|
u_int8_t *p, *buf;
|
|
u_int8_t *tmp, *rgb_p, *yuv_p;
|
|
unsigned char pbm_header[15];
|
|
|
|
if (thumbnail) {
|
|
p = lib->toc + (index*2+1) * 32;
|
|
} else {
|
|
p = lib->toc + index*2*32;
|
|
}
|
|
|
|
aligned_size = file_size =
|
|
(p[0x1c] & 0xff)
|
|
+ (p[0x1d] & 0xff) * 0x100
|
|
+ (p[0x1e] & 0xff) * 0x10000;
|
|
|
|
addr = (p[0x18] & 0xff)
|
|
+ (p[0x19] & 0xff) * 0x100
|
|
+ (p[0x1a] & 0xff) * 0x10000
|
|
+ (p[0x1b] & 0xff) * 0x1000000;
|
|
|
|
/* Trigger download of image data */
|
|
if (thumbnail) {
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev,
|
|
0x0c, index+1, 0x0006, NULL, 0x00));
|
|
} else {
|
|
CHECK (gp_port_usb_msg_write (lib->gpdev,
|
|
0x0a, index+1, 0x000d, NULL, 0x00));
|
|
}
|
|
/* align */
|
|
if (file_size % 16384 != 0)
|
|
aligned_size = ((file_size / 16384) + 1) * 16384;
|
|
|
|
buf = malloc (aligned_size);
|
|
if (!buf)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
CHECK (spca504_flash_wait_for_ready(lib->gpdev));
|
|
CHECK (gp_port_read (lib->gpdev, buf, aligned_size));
|
|
/* For images, we are done now, thumbnails need to be converted from
|
|
* yuv to rgb and a pbm header added. */
|
|
if (thumbnail) {
|
|
int size,w,h;
|
|
u_int8_t *p2 = lib->toc + index*2*32;
|
|
|
|
/* thumbnails are generated from dc coefficients and are
|
|
* therefor w/8 x h/8
|
|
* Since the dimensions of the thumbnail in the TOC are
|
|
* always [0,0], Use the TOC entry for the full image.
|
|
*/
|
|
w = ((p2[0x0c] & 0xff) + (p2[0x0d] & 0xff) * 0x100) / 8;
|
|
h = ((p2[0x0e] & 0xff) + (p2[0x0f] & 0xff) * 0x100) / 8;
|
|
|
|
snprintf (pbm_header, sizeof (pbm_header), "P6 %d %d 255\n",
|
|
w, h );
|
|
size = w * h * 3 + sizeof(pbm_header);
|
|
tmp = malloc (size);
|
|
if (!tmp)
|
|
return GP_ERROR_NO_MEMORY;
|
|
|
|
snprintf ( tmp, sizeof(pbm_header), pbm_header);
|
|
yuv_p = buf;
|
|
rgb_p = tmp + sizeof (pbm_header);
|
|
while (yuv_p < buf + file_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 (buf);
|
|
*data = tmp;
|
|
*len = size;
|
|
} else {
|
|
*data = buf;
|
|
*len = file_size;
|
|
}
|
|
return GP_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Deinit camera
|
|
*/
|
|
int
|
|
spca504_flash_close (GPPort *port, GPContext *context)
|
|
{
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x01, 0x2306, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x00, 0x0d04, NULL, 0x00));
|
|
return GP_OK;
|
|
}
|
|
|
|
/*
|
|
* Initialize the camera and write the jpeg registers. Read them back in to
|
|
* check whether the values are set correctly (wtf?)
|
|
*/
|
|
int
|
|
spca504_flash_init (GPPort *port, GPContext *context)
|
|
{
|
|
struct JPREG {
|
|
int reg;
|
|
int val;
|
|
};
|
|
struct JPREG jpReg[] = {
|
|
{ 0x2800, 0x0005 }, { 0x2840, 0x0005 },
|
|
{ 0x2801, 0x0003 }, { 0x2841, 0x0005 },
|
|
{ 0x2802, 0x0003 }, { 0x2842, 0x0007 },
|
|
{ 0x2803, 0x0005 }, { 0x2843, 0x000e },
|
|
{ 0x2804, 0x0007 }, { 0x2844, 0x001e },
|
|
{ 0x2805, 0x000c }, { 0x2845, 0x001e },
|
|
{ 0x2806, 0x000f }, { 0x2846, 0x001e },
|
|
{ 0x2807, 0x0012 }, { 0x2847, 0x001e },
|
|
{ 0x2808, 0x0004 }, { 0x2848, 0x0005 },
|
|
{ 0x2809, 0x0004 }, { 0x2849, 0x0006 },
|
|
{ 0x280a, 0x0004 }, { 0x284a, 0x0008 },
|
|
{ 0x280b, 0x0006 }, { 0x284b, 0x0014 },
|
|
{ 0x280c, 0x0008 }, { 0x284c, 0x001e },
|
|
{ 0x280d, 0x0011 }, { 0x284d, 0x001e },
|
|
{ 0x280e, 0x0012 }, { 0x284e, 0x001e },
|
|
{ 0x280f, 0x0011 }, { 0x284f, 0x001e },
|
|
{ 0x2810, 0x0004 }, { 0x2850, 0x0007 },
|
|
{ 0x2811, 0x0004 }, { 0x2851, 0x0008 },
|
|
{ 0x2812, 0x0005 }, { 0x2852, 0x0011 },
|
|
{ 0x2813, 0x0007 }, { 0x2853, 0x001e },
|
|
{ 0x2814, 0x000c }, { 0x2854, 0x001e },
|
|
{ 0x2815, 0x0011 }, { 0x2855, 0x001e },
|
|
{ 0x2816, 0x0015 }, { 0x2856, 0x001e },
|
|
{ 0x2817, 0x0011 }, { 0x2857, 0x001e },
|
|
{ 0x2818, 0x0004 }, { 0x2858, 0x000e },
|
|
{ 0x2819, 0x0005 }, { 0x2859, 0x0014 },
|
|
{ 0x281a, 0x0007 }, { 0x285a, 0x001e },
|
|
{ 0x281b, 0x0009 }, { 0x285b, 0x001e },
|
|
{ 0x281c, 0x000f }, { 0x285c, 0x001e },
|
|
{ 0x281d, 0x001a }, { 0x285d, 0x001e },
|
|
{ 0x281e, 0x0018 }, { 0x285e, 0x001e },
|
|
{ 0x281f, 0x0013 }, { 0x285f, 0x001e },
|
|
{ 0x2820, 0x0005 }, { 0x2860, 0x001e },
|
|
{ 0x2821, 0x0007 }, { 0x2861, 0x001e },
|
|
{ 0x2822, 0x000b }, { 0x2862, 0x001e },
|
|
{ 0x2823, 0x0011 }, { 0x2863, 0x001e },
|
|
{ 0x2824, 0x0014 }, { 0x2864, 0x001e },
|
|
{ 0x2825, 0x0021 }, { 0x2865, 0x001e },
|
|
{ 0x2826, 0x001f }, { 0x2866, 0x001e },
|
|
{ 0x2827, 0x0017 }, { 0x2867, 0x001e },
|
|
{ 0x2828, 0x0007 }, { 0x2868, 0x001e },
|
|
{ 0x2829, 0x000b }, { 0x2869, 0x001e },
|
|
{ 0x282a, 0x0011 }, { 0x286a, 0x001e },
|
|
{ 0x282b, 0x0013 }, { 0x286b, 0x001e },
|
|
{ 0x282c, 0x0018 }, { 0x286c, 0x001e },
|
|
{ 0x282d, 0x001f }, { 0x286d, 0x001e },
|
|
{ 0x282e, 0x0022 }, { 0x286e, 0x001e },
|
|
{ 0x282f, 0x001c }, { 0x286f, 0x001e },
|
|
{ 0x2830, 0x000f }, { 0x2870, 0x001e },
|
|
{ 0x2831, 0x0013 }, { 0x2871, 0x001e },
|
|
{ 0x2832, 0x0017 }, { 0x2872, 0x001e },
|
|
{ 0x2833, 0x001a }, { 0x2873, 0x001e },
|
|
{ 0x2834, 0x001f }, { 0x2874, 0x001e },
|
|
{ 0x2835, 0x0024 }, { 0x2875, 0x001e },
|
|
{ 0x2836, 0x0024 }, { 0x2876, 0x001e },
|
|
{ 0x2837, 0x001e }, { 0x2877, 0x001e },
|
|
{ 0x2838, 0x0016 }, { 0x2878, 0x001e },
|
|
{ 0x2839, 0x001c }, { 0x2879, 0x001e },
|
|
{ 0x283a, 0x001d }, { 0x287a, 0x001e },
|
|
{ 0x283b, 0x001d }, { 0x287b, 0x001e },
|
|
{ 0x283c, 0x0022 }, { 0x287c, 0x001e },
|
|
{ 0x283d, 0x001e }, { 0x287d, 0x001e },
|
|
{ 0x283e, 0x001f }, { 0x287e, 0x001e },
|
|
{ 0x283f, 0x001e }, { 0x287f, 0x001e }
|
|
};
|
|
|
|
int len = sizeof (jpReg) / sizeof (jpReg[0]);
|
|
u_int8_t bytes[4];
|
|
int i;
|
|
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x0000, 0x2000, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x0013, 0x2301, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x0001, 0x2883, NULL, 0x00));
|
|
|
|
for (i=0 ; i<len ; i++) {
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, jpReg[i].val,
|
|
jpReg[i].reg, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_read (port, 0x00, 0x0000,
|
|
jpReg[i].reg, bytes, 0x01));
|
|
}
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x0001, 0x2501, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_write (port, 0x00, 0x0000, 0x2306, NULL, 0x00));
|
|
CHECK (gp_port_usb_msg_write (port, 0x08, 0x0000, 0x0006, NULL, 0x00));
|
|
|
|
/* Read the same thing thrice, for whatever reason.
|
|
* From working on getting iso traffic working with this chip, I've found
|
|
* out, that 0x01, 0x0000, 0x0001 polls the chip for completion of a
|
|
* command. Unfortunately it is of yet unclear, what the exact procedure
|
|
* is. Until we know, let's keep this here. */
|
|
CHECK (gp_port_usb_msg_read (port, 0x01, 0x0000, 0x0001, bytes, 0x01));
|
|
CHECK (gp_port_usb_msg_read (port, 0x01, 0x0000, 0x0001, bytes, 0x01));
|
|
CHECK (gp_port_usb_msg_read (port, 0x01, 0x0000, 0x0001, bytes, 0x01));
|
|
|
|
/* Set to idle? */
|
|
CHECK (gp_port_usb_msg_read (port, 0x01, 0x0000, 0x000f, NULL, 0x00));
|
|
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;
|
|
}
|