Files
libgphoto2/camlibs/kodak/dc210/library.c
2002-09-07 22:47:44 +00:00

1386 lines
35 KiB
C

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gphoto2.h>
#include "dc210.h"
#include "library.h"
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
# define _(String) dgettext (PACKAGE, String)
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
# else
# define N_(String) (String)
# endif
#else
# define 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
int dc210_cmd_error = 0;
char ppmheader[] = "P6\n96 72\n255\n";
#ifdef DEBUG
int firststatus = 1;
char oldstatus[DC210_STATUS_SIZE];
#endif
/***************************************************************************
Global in- and output procedures
****************************************************************************/
static void cfa2ppm
(CameraFile * file)
{
/* this is a very quick and dirty hack to convert cfa into ppm */
unsigned char buf[THUMBHEIGHT][THUMBWIDTH];
unsigned char rgb[THUMBHEIGHT][THUMBWIDTH][3];
unsigned char val;
unsigned int addval;
const char * data;
unsigned long datasize;
int i, x, y;
static char ppmheader[] = "P6\n96 72\n255\n";
/* get data */
DC210_DEBUG("Converting CFA to PPM\n");
gp_file_get_data_and_size(file, &data, &datasize);
/* split 8 bit values into to 4 bit values and blow up again to 8 bit*/
i = 0;
for(y = 0; y < THUMBHEIGHT; y++ )
for(x = 0; x < THUMBWIDTH; x+=2 ){
val = (unsigned char) data[i] >> 4;
buf[y][x] = (val << 4) | val;
val = (unsigned char) data[i++] & 0x0F;
buf[y][x+1] = (val << 4) | val;
};
/* fill in the exact values */
for( y = 0; y < THUMBHEIGHT; y += 2 )
for( x = 0; x < THUMBWIDTH; x += 2 )
{
rgb[y][x][GREEN] = buf[y][x];
rgb[y][x+1][GREEN] = buf[y][x];
rgb[y+1][x][GREEN] = buf[y+1][x+1];
rgb[y+1][x+1][GREEN] = buf[y+1][x+1];
rgb[y][x][RED] = buf[y][x+1];
rgb[y][x+1][RED] = buf[y][x+1];
rgb[y+1][x][RED] = buf[y][x+1];
rgb[y+1][x+1][RED] = buf[y][x+1];
rgb[y][x][BLUE] = buf[y+1][x];
rgb[y][x+1][BLUE] = buf[y+1][x];
rgb[y+1][x][BLUE] = buf[y+1][x];
rgb[y+1][x+1][BLUE] = buf[y+1][x];
};
/* fill in the interpolated values, don't care about borders */
for ( y = 1; y < THUMBHEIGHT - 2; y+= 2)
for ( x = 0; x < THUMBWIDTH - 2; x +=2){
/* 2 missing green values */
addval = rgb[y][x][GREEN] + rgb[y][x+2][GREEN]
+ rgb[y-1][x+1][GREEN] + rgb[y+1][x+1][GREEN];
rgb[y][x+1][GREEN] = addval / 4;
addval = rgb[y+1][x-1][GREEN] + rgb[y+1][x+1][GREEN]
+ rgb[y][x][GREEN] + rgb[y+2][x][GREEN];
rgb[y+1][x][GREEN] = addval / 4;
/* 3 missing red values */
addval = rgb[y-1][x][RED] + rgb[y+1][x][RED];
rgb[y][x][RED] = addval / 2;
addval = rgb[y-1][x][RED] + rgb[y-1][x+2][RED]
+ rgb[y+1][x][RED] + rgb[y+1][x+2][RED];
rgb[y][x+1][RED] = addval / 4;
addval = rgb[y+1][x][RED] + rgb[y+1][x+2][RED];
rgb[y+1][x+1][RED] = addval / 2;
/* 3 missing blue values */
addval = rgb[y][x-1][BLUE] + rgb[y][x+1][BLUE];
rgb[y][x][BLUE] = addval / 2;
addval = rgb[y][x-1][BLUE] + rgb[y][x+1][BLUE]
+ rgb[y+2][x-1][BLUE] + rgb[y+2][x+1][BLUE];
rgb[y+1][x][BLUE] = addval / 4;
addval = rgb[y][x+1][BLUE] + rgb[y+2][x+1][BLUE];
rgb[y+1][x+1][BLUE] = addval / 2;
};
gp_file_clean(file);
gp_file_append(file, ppmheader, sizeof(ppmheader) - 1);
gp_file_append(file, (char *) rgb, THUMBWIDTH * THUMBHEIGHT * 3);
gp_file_set_mime_type(file, GP_MIME_PPM);
};
/* Utility procedures for command execution */
static void dc210_cmd_init
(char * cmd, unsigned char command_byte)
{
/* utility procedure to initalize a command string */
memset (cmd, 0, 8);
cmd[0] = command_byte;
cmd[7] = 0x1a;
};
static void dc210_cmd_packet_init
(char * cmd_packet, const char * filename)
{
/* prepare command packet */
memset (cmd_packet, 0x00, DC210_CMD_DATA_SIZE);
memset (cmd_packet + 48, 0xFF, 8);
strcpy(cmd_packet, "\\PCCARD\\DCIMAGES\\");
strcpy(cmd_packet + 17, filename);
DC210_DEBUG("Complete filename is %s\n", cmd_packet);
};
static int dc210_write_single_char
(Camera *camera, unsigned char response)
{
/* utility procedure to write a single character */
int i;
for (i=0; i< RETRIES; i++){
if (gp_port_write(camera->port, &response, 1) >= 0)
return (GP_OK);
};
return GP_ERROR;
};
static int dc210_read_single_char
(Camera *camera, unsigned char * response)
{
/* utility procedure to read a single character */
int i;
signed char error;
for (i = 0; i < RETRIES; i++){
error = gp_port_read(camera->port, response, 1);
if (error < 0){
if (error == GP_ERROR_TIMEOUT)
continue;
else{
DC210_DEBUG("Real bad error reading single character. Errornumber: %d\n", error);
return GP_ERROR;
};
};
return GP_OK;
};
return GP_ERROR;
};
/* Command execution */
static int dc210_execute_command
(Camera *camera, char *cmd)
{
/* This procedure sends a command to the camera and
waits for acknowledgement. Calling procedures have
to check the returncode; if the command succeeded
there are three possible follow ups:
- Read a DC210_COMMAND_COMPLETE in case of
simple commands
- Send an additional command package in case of
sophisticated commands
- Read data from the camera
But all this depends on the command and should not
be mixed in this procedure. */
int i,k ;
unsigned char response;
signed char error;
dc210_cmd_error = DC210_CMD_OKAY;
for (i = 0; i < RETRIES; i++){
/* write command */
if (gp_port_write(camera->port, cmd, 8) < 0){
DC210_DEBUG("Could not write to port.\n");
dc210_cmd_error = DC210_WRITE_ERROR;
continue; /* try again */
};
for (k = 0; k < RETRIES; k++){
error = gp_port_read(camera->port, &response, 1);
if (error != 1){
if (error == GP_ERROR_TIMEOUT){
dc210_cmd_error = DC210_TIMEOUT_ERROR;
continue; /* wait a little bit longer */
}
else{
DC210_DEBUG("Real bad error reading answer. Errornumber: %d\n", error);
dc210_cmd_error = DC210_READ_ERROR;
return error;
};
};
switch (response){
case DC210_COMMAND_ACK:
DC210_DEBUG("Command 0x%02X acknowledged.\n", (unsigned char) cmd[0]);
return GP_OK; break; /* that's fine, just what we expected */
case DC210_COMMAND_NAK:
DC210_DEBUG("Sorry, but the camera seems not to understand the command 0x%02X\n", (unsigned char) cmd[0]);
dc210_cmd_error = DC210_NAK_ERROR;
break; /* anyway, send the command again */
default:
DC210_DEBUG("Strange. Unexpected response for command 0x%02X: 0x%02X\n", (unsigned char) cmd[0], response);
dc210_cmd_error = DC210_GARBAGE_ERROR;
return GP_ERROR;
break;
};
break;
};
};
/* command does definitely not work */
DC210_DEBUG("Command definitely didn't work.\n");
return GP_ERROR;
};
/* appropriate actions to a command */
static int dc210_write_command_packet
(Camera * camera, char * data)
{
unsigned char checksum;
int i, error;
unsigned char answer;
/* calculating checksum */
checksum = 0;
for (i = 0; i < DC210_CMD_DATA_SIZE; i ++){
checksum ^= data[i];
};
for (i = 0; i < RETRIES; i++){
/* write the startbyte */
dc210_write_single_char(camera, DC210_CMD_PACKET_FOLLOWING);
/* write packet */
gp_port_write(camera->port, data, DC210_CMD_DATA_SIZE);
/* write checksum */
dc210_write_single_char(camera, checksum);
/* read answer */
error = gp_port_read(camera->port, &answer, 1);
if (error < 0) return GP_ERROR;
if (answer == DC210_CORRECT_PACKET) return GP_OK;
if (answer != DC210_ILLEGAL_PACKET) {
DC210_DEBUG("Strange answer to command packet: 0x%02X.\n", answer);
return GP_ERROR;
};
};
DC210_DEBUG("Could not send command packet.\n");
return GP_ERROR;
};
static int dc210_wait_for_response
(Camera *camera, int expect_busy, GPContext *context)
{
/* Waits for a command to finish.
Expects either a timeout, a DC210_BUSY,
a DC210_COMMAND_COMPLETE or a
DC210_PACKET_FOLLOWING; all other answers
are considered errors */
unsigned char response;
int counter = 0;
int progress_id = 0;
if (context != NULL)
progress_id = gp_context_progress_start (context, expect_busy, _("Waiting..."));
while (1){
if (dc210_read_single_char(camera, &response) < 0){
if (context != 0)
gp_context_progress_stop (context, progress_id);
return GP_ERROR;
};
switch (response){
case DC210_BUSY:
/* wait a little bit longer */
if (context != NULL && counter <= expect_busy)
gp_context_progress_update (context, progress_id, counter++);
break;
case DC210_COMMAND_COMPLETE:
case DC210_PACKET_FOLLOWING:
if (context != 0)
gp_context_progress_stop (context, progress_id);
return response;
break;
default:
if (context != 0)
gp_context_progress_stop (context, progress_id);
DC210_DEBUG("Command terminated with errorcode 0x%02X.\n", response);
return GP_ERROR;
};
};
};
static int dc210_read_single_block
(Camera *camera, unsigned char * b, int blocksize)
{
int i, k, error;
char cs_read, cs_computed;
for (i = 0; i < RETRIES; i++){
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR)
return GP_ERROR;
error = 1;
for (k = 0; k < RETRIES; k++){
if (gp_port_read(camera->port, b, blocksize) < 0){
continue;
};
error = 0;
break;
};
if (error) return GP_ERROR;
if (dc210_read_single_char(camera, &cs_read) < 0)
return GP_ERROR;
cs_computed = 0;
for (k = 0 ; k < blocksize; k++)
cs_computed ^= b[k];
if (cs_computed == cs_read){
dc210_write_single_char(camera, DC210_CORRECT_PACKET);
return GP_OK;
};
dc210_write_single_char(camera, DC210_ILLEGAL_PACKET);
};
return GP_ERROR;
};
static int dc210_read_to_file
(Camera *camera, CameraFile * f, int blocksize, long int expectsize, GPContext *context)
{
int packets, k, l, fatal_error, packet_following;
unsigned int progress_id = 0;
unsigned char cs_read, cs_computed;
unsigned char * b;
int blocks = expectsize / blocksize;
int remaining = expectsize % blocksize;
if (NULL == (b = malloc(blocksize))) return GP_ERROR;
if (remaining) blocks++;
if (context != NULL)
progress_id = gp_context_progress_start (context, blocks, _("Getting data..."));
fatal_error = 0;
packets = 0;
while (1 == (packet_following = dc210_wait_for_response(camera, 0, NULL))){
fatal_error = 1;
for (k = 0; k < RETRIES; k++){
/* read packet */
if (gp_port_read(camera->port, b, blocksize) < 0)
continue;
/* read checksum */
if (dc210_read_single_char(camera, &cs_read) == GP_ERROR){
free(b);
return GP_ERROR;
};
/* test checksum */
cs_computed = 0;
for (l = 0 ; l < blocksize; l++)
cs_computed ^= b[l];
if (cs_computed != cs_read){
dc210_write_single_char(camera, DC210_ILLEGAL_PACKET);
continue;
};
/* append to file */
if (packets == blocks - 1 && remaining)
gp_file_append(f, b, remaining);
else
gp_file_append(f, b, blocksize);
/* request next packet */
dc210_write_single_char(camera, DC210_CORRECT_PACKET);
fatal_error = 0;
if (context != NULL)
gp_context_progress_update (context, progress_id, packets);
packets++;
break;
};
if (fatal_error) break;
};
if (packet_following < 0)
fatal_error = 1;
if (context != 0)
gp_context_progress_stop (context, progress_id);
free(b);
if (fatal_error) return GP_ERROR;
return GP_OK;
};
/***************************************************************************
Working procedures
****************************************************************************/
/***** Set procedures *****/
static int dc210_set_option (Camera * camera, char command, unsigned int value, int valuesize){
char cmd[8];
dc210_cmd_init(cmd, command);
switch(valuesize){
case 0:
break;
case 1:
cmd[2] = value & 0xFF;
break;
case 2:
cmd[3] = value & 0xFF;
cmd[2] = (value >> 8) & 0xFF;
break;
case 4:
cmd[5] = value & 0xFF;
cmd[4] = (value >> 8) & 0xFF;
cmd[3] = (value >> 16) & 0xFF;
cmd[2] = (value >> 24) & 0xFF;
break;
default:
return GP_ERROR;
};
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_set_compression (Camera * camera, dc210_compression_type compression){
return dc210_set_option(camera, DC210_SET_QUALITY, compression, 1);
};
int dc210_set_flash (Camera * camera, dc210_flash_type flash, char preflash){
if (flash != DC210_FLASH_NONE && preflash){
return dc210_set_option(camera, DC210_SET_FLASH, flash + 3, 1);
}
else
return dc210_set_option(camera, DC210_SET_FLASH, flash, 1);
};
int dc210_set_zoom (Camera * camera, dc210_zoom_type zoom){
return dc210_set_option(camera, DC210_SET_ZOOM, zoom, 1);
};
int dc210_set_file_type (Camera * camera, dc210_file_type_type file_type){
return dc210_set_option(camera, DC210_SET_FILE_TYPE, file_type, 1);
};
int dc210_set_resolution (Camera * camera, dc210_resolution_type res){
return dc210_set_option(camera, DC210_SET_RESOLUTION, res, 1);
};
int dc210_set_delay (Camera * camera){
/* This function works, but I do no have the slightest idea,
what the effect is / should be.
There is *no* delay, when you execute this function before
capturing. */
return dc210_set_option(camera, DC210_SET_DELAY, 10, 4);
};
int dc210_set_exp_compensation (Camera * camera, signed int compensation){
unsigned char param = abs(compensation);
if (compensation < 0) param |= 0x80;
return dc210_set_option(camera, DC210_SET_EXPOSURE, param, 1);
};
int dc210_system_time_callback (Camera * camera, CameraWidget * widget, GPContext * context){
return dc210_set_option(camera, DC210_SET_TIME, (time(NULL) - CAMERA_SET_EPOC) << 1, 4);
};
int dc210_format_card (Camera * camera, char * album_name, GPContext * context){
char data[DC210_CMD_DATA_SIZE];
char cmd[8];
char * subst;
unsigned char answer[16];
unsigned char checksum_read, checksum;
int i;
memset (data, 0, DC210_CMD_DATA_SIZE);
/* Make the album name acceptable - we need at least 8 characters
which are not blank and there are no blanks allowed before the
last character.
*/
if (album_name != NULL && strlen(album_name) > 0){
strncpy(data, album_name, 11);
/* substitute blanks in the first 8 characters*/
while (NULL != (subst = strchr(data, ' '))) *subst = '_';
if (strlen(data) < 8) strncat(data, "________", 8 - strlen(data));
};
DC210_DEBUG("Album name is '%s'\n", data);
dc210_cmd_init(cmd, DC210_CARD_FORMAT);
dc210_execute_command(camera, cmd);
dc210_write_command_packet(camera, data);
if (dc210_wait_for_response(camera, 3, context) == GP_ERROR) return GP_ERROR;
gp_port_read(camera->port, answer, 16);
gp_port_read(camera->port, &checksum_read, 1);
checksum = 0;
for (i = 0; i < 16; i++) checksum ^= answer[i];
if (checksum_read != checksum) return GP_ERROR;
DC210_DEBUG("Flash card formated.\n");
if (dc210_write_single_char(camera, DC210_CORRECT_PACKET) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
gp_filesystem_reset(camera->fs);
return GP_OK;
};
static int dc210_space_on_card(Camera * camera, int * space){
char cmd[8];
unsigned char answer[16];
unsigned char checksum_read, checksum;
int i, sectors;
dc210_cmd_init(cmd, DC210_CARD_GET_STATUS);
dc210_execute_command(camera, cmd);
*space = -1;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
gp_port_read(camera->port, answer, 16);
gp_port_read(camera->port, &checksum_read, 1);
checksum = 0;
for (i = 0; i < 16; i++) checksum ^= answer[i];
if (checksum_read != checksum)
DC210_DEBUG("Error reading card status.\n");
else
DC210_DEBUG("Card status correctly read.\n");
sectors = answer[2] * 0x1000000 + answer[3] * 0x10000 + answer[4] * 0x100 + answer[5];
*space = sectors >> 2; /* sectorsize seems to be 128, now we get kb */
DC210_DEBUG("Space on card: %d\n", *space);
if (dc210_write_single_char(camera, DC210_CORRECT_PACKET) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_format_callback(Camera * camera, CameraWidget * widget, GPContext * context){
CameraWidget * window;
char * album_name;
gp_widget_get_root(widget, &window);
gp_widget_get_child_by_label (window, _("Album name"), &widget);
gp_widget_get_value (widget, &album_name);
return dc210_format_card(camera, album_name, context);
};
static int dc210_check_battery (Camera *camera){
unsigned char cmd[8];
dc210_cmd_init(cmd, DC210_CHECK_BATTERY);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
void dc210_reset_speed (Camera *camera){
/* sending a break command resets the
speed to 9600 */
int camera_speeds[] = {115200, 19200, 38400, 57600};
int i;
GPPortSettings settings;
gp_port_get_settings (camera->port, &settings);
gp_port_set_timeout (camera->port, TIMEOUT);
settings.serial.bits = 8;
settings.serial.parity = 0;
settings.serial.stopbits = 1;
settings.serial.speed = 9600;
gp_port_set_settings (camera->port, settings);
/* ok, run a dummy command; if the camera was off,
this will activate it and set the speed to 9600;
but the command will return an error */
if (dc210_check_battery(camera) == GP_OK) return;
/* Fine, the last command didn't work, but we are now
sure, the camera is working, if it is connected
and has enough power.
This is the time to send a break to reset the speed
to 9600 */
gp_port_send_break(camera->port, 300);
GP_SYSTEM_SLEEP(300);
/* Excellent. Now our dummy command should work */
if (dc210_check_battery(camera) == GP_OK) return;
/* My, my, this is not nice. We change the timeout
and try other speeds */
gp_port_set_timeout (camera->port, 100);
for (i = 0; i < 4; i++){
settings.serial.speed = camera_speeds[i];
gp_port_set_settings (camera->port, settings);
if (dc210_check_battery(camera) == GP_OK) break;
DC210_DEBUG("What a pity. Speed %d dos not work.\n", camera_speeds[i]);
};
/* reset the timeout */
gp_port_set_timeout (camera->port, TIMEOUT);
/* wether it worked or not: it's time to say goodbye */
return;
};
int dc210_set_speed (Camera *camera, int speed) {
unsigned char cmd[8];
GPPortSettings settings;
dc210_cmd_init(cmd, DC210_SET_SPEED);
switch (speed) {
case 0: /* Default */
case 9600:
cmd[2] = 0x96; cmd[3] = 0x00; break;
case 19200:
cmd[2] = 0x19; cmd[3] = 0x20; break;
case 38400:
cmd[2] = 0x38; cmd[3] = 0x40; break;
case 57600:
cmd[2] = 0x57; cmd[3] = 0x60; break;
case 115200:
cmd[2] = 0x11; cmd[3] = 0x52; break;
default:
return (GP_ERROR);
};
/* try to get a defined state */
dc210_reset_speed (camera);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
gp_port_get_settings (camera->port, &settings);
settings.serial.speed = speed;
gp_port_set_settings (camera->port, settings);
return GP_OK;
};
/****** Picture actions *****/
static int dc210_take_picture
(Camera * camera, GPContext * context)
{
char cmd[8];
dc210_cmd_init(cmd, DC210_TAKE_PICTURE);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 5, context) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_delete_picture
(Camera * camera, unsigned int picno)
{
char cmd[8];
unsigned int pic_offset = picno - 1;
dc210_cmd_init(cmd, DC210_DELETE_PICTURE);
cmd[3] = pic_offset & 0xFF;
cmd[2] = (pic_offset >> 8) & 0xFF;
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_delete_last_picture
(Camera * camera )
{
dc210_status status;
if (dc210_get_status(camera, &status) == GP_ERROR)
return GP_ERROR;
if (0 == status.numPicturesInCamera)
return GP_ERROR;
return dc210_delete_picture(camera, status.numPicturesInCamera);
};
int dc210_delete_picture_by_name
(Camera * camera, const char * filename )
{
char cmd[8];
char cmd_packet[DC210_CMD_DATA_SIZE];
dc210_cmd_init(cmd, DC210_CARD_FILE_DEL);
dc210_cmd_packet_init(cmd_packet, filename);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_write_command_packet(camera, cmd_packet) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_download_picture
(Camera * camera, CameraFile *file, unsigned int picno, int thumb, GPContext *context)
{
dc210_status status;
dc210_picture_info picinfo;
char cmd[8];
unsigned int pic_offset = picno - 1;
/* this function is not used anymore */
if (thumb)
dc210_cmd_init(cmd, DC210_GET_THUMB);
else
dc210_cmd_init(cmd, DC210_GET_PICTURE);
cmd[3] = pic_offset & 0xFF;
cmd[2] = (pic_offset >> 8) & 0xFF;
if (thumb) cmd[4] = 1; /* rgb colour image instead of cfa */
if (dc210_get_status(camera, &status) == GP_ERROR)
return GP_ERROR;
if (picno > status.numPicturesInCamera)
return GP_ERROR;
if (dc210_get_picture_info(camera, &picinfo, picno) == GP_ERROR)
return GP_ERROR;
gp_file_set_name(file, picinfo.image_name);
dc210_execute_command(camera, cmd);
if (thumb){
/* The windows bitmaps byte order differs from the one
the camera delivers, so the following seems to be rotated:
gp_file_set_mime_type(file, GP_MIME_BMP);
gp_file_append(file, bmpheader, sizeof(bmpheader));
We use ppm instead, which is very friendly and needs
nothing more than the correct header */
gp_file_set_mime_type(file, GP_MIME_PPM);
gp_file_append(file, ppmheader, sizeof(ppmheader) - 1);
dc210_read_to_file(camera, file, DC210_DOWNLOAD_BLOCKSIZE, 96 * 72 * 3, NULL);
}
else
dc210_read_to_file(camera, file, DC210_DOWNLOAD_BLOCKSIZE, picinfo.picture_size, context);
return GP_OK;
};
int dc210_open_card
(Camera * camera)
{
char cmd[8];
dc210_cmd_init(cmd, DC210_CARD_OPEN);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_close_card
(Camera * camera)
{
char cmd[8];
dc210_cmd_init(cmd, DC210_CARD_CLOSE);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
return GP_OK;
};
int dc210_download_picture_by_name
(Camera * camera, CameraFile *file, const char *filename, dc210_picture_type type, GPContext *context)
{
char cmd[8];
char cmd_packet[DC210_CMD_DATA_SIZE];
dc210_picture_info picinfo;
/* prepare command */
if (type == DC210_FULL_PICTURE){
/* we need the file size for the progress bar */
if (dc210_get_picture_info_by_name(camera, &picinfo, filename) == GP_ERROR)
return GP_ERROR;
DC210_DEBUG("Picture size is %d\n", picinfo.picture_size);
dc210_cmd_init(cmd, DC210_CARD_READ_FILE);
}
else
dc210_cmd_init(cmd, DC210_CARD_READ_THUMB);
if (type == DC210_RGB_THUMB) cmd[4] = 1;
dc210_cmd_packet_init(cmd_packet, filename);
gp_file_set_name(file, filename);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_write_command_packet(camera, cmd_packet) == GP_ERROR) return GP_ERROR;
switch (type){
case DC210_FULL_PICTURE:
if (picinfo.file_type == DC210_FILE_TYPE_JPEG)
gp_file_set_mime_type(file, GP_MIME_JPEG);
if (dc210_read_to_file(camera, file,
DC210_CARD_BLOCK_SIZE,
picinfo.picture_size, context) == GP_ERROR)
return GP_ERROR;
break;
case DC210_CFA_THUMB:
/* this thumb type is much faster to download, because it has just
a 6th of the size of rgb data */
if (dc210_read_to_file(camera, file,
DC210_DOWNLOAD_BLOCKSIZE, 48 * 72, NULL) == GP_ERROR)
return GP_ERROR;
/* convert cfa data to rgb: */
cfa2ppm(file);
break;
break;
case DC210_RGB_THUMB:
/* if you really need nice thumbs, you can use this, but I don't think it's
worth the 6 times longer download time */
gp_file_set_mime_type(file, GP_MIME_PPM);
gp_file_append(file, ppmheader, sizeof(ppmheader) - 1);
if (dc210_read_to_file(camera, file,
DC210_DOWNLOAD_BLOCKSIZE, 96 * 72 * 3, NULL) == GP_ERROR)
return GP_ERROR;
break;
};
return GP_OK;
};
int dc210_capture (Camera *camera, CameraFilePath *path, GPContext *context)
{
dc210_status status;
dc210_picture_info picinfo;
if (dc210_take_picture(camera, context) == GP_ERROR)
return GP_ERROR;
if (dc210_get_status(camera, &status) == GP_ERROR) return GP_ERROR;
if (dc210_get_picture_info(camera, &picinfo, status.numPicturesInCamera) == GP_ERROR) return GP_ERROR;
strcpy(path->folder, "/");
strcpy(path->name, picinfo.image_name);
return GP_OK;
};
/***** Retrieve information *****/
int dc210_get_picture_number (Camera *camera, const char * filename){
/* This function is terrible slow if you have a lot of pictures
in the camera; therefor it should not be used */
dc210_status status;
dc210_picture_info picinfo;
int i;
if (dc210_get_status(camera, &status) == GP_ERROR) return -1;
for (i = 1; i <= status.numPicturesInCamera; i++){
if (dc210_get_picture_info(camera, &picinfo, i) == GP_ERROR) return -1;
if (strcmp(picinfo.image_name, filename) == 0){
return i;
};
};
return -1;
};
int dc210_get_filenames (Camera *camera, CameraList *list, GPContext *context)
{
char cmd[8];
CameraFile *file;
char filename[13];
const char * data;
unsigned long datasize;
int pictures, i;
gp_file_new(&file);
dc210_cmd_init(cmd, DC210_GET_ALBUM_FILENAMES);
dc210_execute_command(camera, cmd);
dc210_read_to_file(camera, file, DC210_DIRLIST_SIZE, 0, NULL);
gp_file_get_data_and_size(file, &data, &datasize);
pictures = (unsigned char) data[0] * 0x100 + (unsigned char) data[1];
DC210_DEBUG("There are %d pictures in the camera\n", pictures);
filename[8] = '.';
filename[12] = '\0';
for (i = 0; i < pictures; i++){
strncpy(filename, data + 2 + i*20, 8);
strncpy(filename + 9, data + 10 + i*20, 3);
DC210_DEBUG("Adding filename %s to list\n", filename);
gp_list_append(list, filename, NULL);
};
gp_file_free(file);
return GP_OK;
};
static void dc210_picinfo_from_block (dc210_picture_info * picinfo, unsigned char * data){
picinfo->camera_type = data[1];
picinfo->file_type = data[2];
picinfo->resolution = data[3];
picinfo->compression = data[4];
picinfo->picture_number = data[6] * 0x100 + data[7];
picinfo->picture_size = data[8] * 0x1000000 + data[9] * 0x10000 + data[10] * 0x100 + data[11];
picinfo->preview_size = 96 * 72 * 3 + sizeof(ppmheader);
picinfo->picture_time = CAMERA_GET_EPOC + ((data[12] * 0x1000000 +
data[13] * 0x10000 +
data[14] * 0x100 +
data[15]) >> 1);
picinfo->flash_used = data[16];
picinfo->flash = data[17];
picinfo->preflash = (data[17] > 2);
if (picinfo->preflash) picinfo->flash -= 3;
picinfo->zoom = data[21];
picinfo->f_number = data[26];
picinfo->battery = data[27];
picinfo->exposure_time = data[28] * 0x1000000 + data[29] * 0x10000 + data[30] * 0x100 + data[31];
strncpy(picinfo->image_name, &data[32], 12);
picinfo->image_name[12] = 0;
}
int dc210_get_picture_info (Camera *camera, dc210_picture_info *picinfo, unsigned int picno) {
char cmd[8];
unsigned char data[DC210_PICINFO_SIZE];
unsigned int pic_offset = picno - 1;
dc210_cmd_init(cmd, DC210_GET_PICINFO);
cmd[3] = pic_offset & 0xFF;
cmd[2] = (pic_offset >> 8) & 0xFF;
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_read_single_block(camera, data, DC210_PICINFO_SIZE) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
dc210_picinfo_from_block(picinfo, data);
return GP_OK;
};
int dc210_get_picture_info_by_name (Camera *camera, dc210_picture_info *picinfo, const char * filename) {
char cmd[8];
char cmd_packet[DC210_CMD_DATA_SIZE];
unsigned char data[DC210_CARD_BLOCK_SIZE];
dc210_cmd_init(cmd, DC210_CARD_GET_PICINFO);
dc210_cmd_packet_init(cmd_packet, filename);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_write_command_packet(camera, cmd_packet) == GP_ERROR) return GP_ERROR;
if (dc210_read_single_block(camera, data, DC210_CARD_BLOCK_SIZE) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
dc210_picinfo_from_block(picinfo, data);
return GP_OK;
};
int dc210_get_status (Camera *camera, dc210_status *status) {
#ifdef DEBUG
int i;
#endif
char data[DC210_STATUS_SIZE];
char cmd[8];
/* you have to check the battery status explicitely
before reading the status table*/
dc210_check_battery(camera);
dc210_cmd_init(cmd, DC210_GET_STATUS);
if (dc210_execute_command(camera, cmd) == GP_ERROR) return GP_ERROR;
if (dc210_read_single_block(camera, data, DC210_STATUS_SIZE) == GP_ERROR) return GP_ERROR;
if (dc210_wait_for_response(camera, 0, NULL) == GP_ERROR) return GP_ERROR;
#ifdef DEBUG
if (firststatus){
firststatus = 0;
}
else{
for (i = 0; i < DC210_STATUS_SIZE; i++){
if (oldstatus[i] != data[i] && (i < 12 || i > 15)){
DC210_DEBUG("Statusdata differs at offset %03d (old: 0x%02X, new: 0x%02X)\n",
i, (unsigned char) oldstatus[i], (unsigned char) data[i]);
};
};
};
memcpy(oldstatus, data, DC210_STATUS_SIZE);
#endif
status->firmwareMajor = data[2];
status->firmwareMinor = data[3];
status->fast_preview = data[8];
status->battery = data[9];
status->acstatus = data[10];
/* seconds since unix epoc */
status->time = CAMERA_GET_EPOC + ((unsigned char) data[12] * 0x1000000 +
(unsigned char) data[13] * 0x10000 +
(unsigned char) data[14] * 0x100 +
(unsigned char) data[15]) / 2;
status->zoom = data[16];
status->compression_type = data[19];
status->flash = data[20];
status->exp_compensation = data[21] & 0x7F;
if (data[21] & 0x80) status->exp_compensation *= -1;
status->preflash = (data[20] > 2);
if (status->preflash) status->flash -= 3;
status->resolution = data[22];
status->file_type = data[23];
status->totalPicturesTaken = (unsigned char) data[25] * 0x100 + (unsigned char) data[26];
status->totalFlashesFired = (unsigned char) data[27] * 0x100 + (unsigned char) data[28];
status->numPicturesInCamera = (unsigned char) data[56] * 0x100 + (unsigned char) data[57];
status->remainingLow = (unsigned char) data[68] * 0x100 + (unsigned char) data[69];
status->remainingMedium = (unsigned char) data[70] * 0x100 + (unsigned char) data[71];
status->remainingHigh = (unsigned char) data[72] * 0x100 + (unsigned char) data[73];
strncpy(status->album_name, &data[77], 11);
if (dc210_space_on_card(camera, &status->card_space) == GP_ERROR)
DC210_DEBUG("No card in camera.\n");
status->album_name[11] = 0;
return GP_OK;
}
/* Debugging stuff, not really needed in the driver */
#ifdef DEBUG
static int dc210_read_dummy_packet(Camera * camera){
int i;
signed char error;
unsigned char answer, lastanswer, checksum;
checksum = 0;
lastanswer = 0;
for ( i = 0; i < 2048; i++){
error = gp_port_read(camera->port, &answer, 1);
if (error < 0){
DC210_DEBUG("Packet read exhausted. Packet size: %d\n", i-1);
if (checksum == lastanswer)
DC210_DEBUG("Checksum is okay. Sending okay.\n");
else
DC210_DEBUG("Checksum is not okay. Sending correct packet anyway.\n");
answer = DC210_CORRECT_PACKET;
gp_port_write(camera->port, &answer, 1);
return GP_OK;
};
DC210_DEBUG(" Byte %03d: 0x%02X", i, answer);
if (answer > 32)
DC210_DEBUG(" (%c)", answer);
DC210_DEBUG("\n");
checksum ^= lastanswer;
lastanswer = answer;
};
DC210_DEBUG("Packet too long. Sending cancel.\n");
answer = DC210_CANCEL;
gp_port_write(camera->port, &answer, 1);
return GP_OK;
};
static int dc210_test_command
(Camera * camera, unsigned char cmdbyte, unsigned char *databytes){
/* this is a debug command to test the behaviour
of certain camera commands */
unsigned char cmd[8];
unsigned char answer;
signed char error;
int packetcount = 0;
char dummy_cmd_packet[DC210_CMD_DATA_SIZE];
memset (dummy_cmd_packet, 0, DC210_CMD_DATA_SIZE);
dc210_cmd_init(cmd, cmdbyte);
memcpy(cmd + 2, databytes, 4);
/* okay, write the command and wait for acknowledge */
gp_port_write(camera->port, cmd, 8);
error = gp_port_read(camera->port, &answer, 1);
if (error < 0){
DC210_DEBUG("Command 0x%02X produced port read error %d",
cmdbyte, error);
return GP_OK;
};
switch (answer){
case DC210_COMMAND_ACK:
DC210_DEBUG("Okay, command 0x%02X acknowledged.\n", cmdbyte);
break;
case DC210_COMMAND_NAK:
DC210_DEBUG("Sorry, command 0x%02X not acknowledged.\n", cmdbyte);
return GP_OK;
default:
DC210_DEBUG("Unexpected response 0x%02X to command 0x%02X.\n",
answer, cmdbyte);
return GP_OK;
};
wait_finish:
error = gp_port_read(camera->port, &answer, 1);
if (error < 0){
DC210_DEBUG("Command 0x%02X produced port read error %d\n",
cmdbyte, error);
DC210_DEBUG("Probably the command expects a command package.\n");
DC210_DEBUG("Sending one...\n");
dc210_write_command_packet(camera, dummy_cmd_packet);
goto wait_finish;
};
switch(answer){
case DC210_COMMAND_COMPLETE:
DC210_DEBUG("Okay. Command has been accepted and executed.\n");
return GP_OK;
case DC210_EXECUTION_ERROR:
DC210_DEBUG("Sorry. An error occurred while executing command.\n");
return GP_OK;
case DC210_CORRECT_PACKET:
DC210_DEBUG("Okay. Packet successfully received.\n");
goto wait_finish;
case DC210_PACKET_FOLLOWING:
DC210_DEBUG("My, my, you got more than you bargained for. ");
DC210_DEBUG("Awaiting packet...\n");
dc210_read_dummy_packet(camera);
DC210_DEBUG("Read %d packets.\n", ++packetcount);
goto wait_finish;
case DC210_BUSY:
DC210_DEBUG("Camera is busy.\n");
goto wait_finish;
default:
DC210_DEBUG("Unexpected response 0x%02X to command 0x%02X.\n",
answer, cmdbyte);
return GP_OK;
};
};
int dc210_debug_callback(Camera * camera, CameraWidget * widget, GPContext * context){
/*CameraFile *file;*/
CameraWidget * window;
char * s_param1, *s_param2, *s_param3;
int param1, param2, param3;
int value;
unsigned char params[4];
dc210_status status;
memset(params, 0, 4);
gp_widget_get_root(widget, &window);
gp_widget_get_child_by_label (window, _("Parameter 1"), &widget);
gp_widget_get_value (widget, &s_param1);
param1 = atoi(s_param1);
gp_widget_get_child_by_label (window, _("Parameter 2"), &widget);
gp_widget_get_value (widget, &s_param2);
param2 = atoi(s_param2);
gp_widget_get_child_by_label (window, _("Parameter 3"), &widget);
gp_widget_get_value (widget, &s_param3);
param3 = atoi(s_param3);
/* Put the stuff you want to test here */
dc210_get_status(camera, &status);
dc210_test_command(camera, 0x85, params);
dc210_get_status(camera, &status);
return GP_OK;
};
#endif