/* Sony DSC-F55 & MSAC-SR1 - gPhoto2 camera library * Copyright © 2001, 2002, 2004 Raymond Penners <raymond@dotsphinx.com> * Copyright © 2000 Mark Davies <mdavies@dial.pipex.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 <stdio.h> #include <stdlib.h> #include <string.h> #include <gphoto2/gphoto2.h> #include "sony.h" #define GP_MODULE "sony55" /** * Constants */ #define SONY_CONVERSE_RETRY 5 #define SONY_INVALID_CHECKSUM 0x40 #define SONY_INVALID_SEQUENCE 0x41 #define SONY_RESET_SEQUENCE 0x42 #define SONY_RESEND_PACKET 0x43 #define SONY_ESCAPE_CHAR 0x7d #define SONY_START_CHAR 0xc0 #define SONY_END_CHAR 0xc1 static const unsigned char START_PACKET = 192; static const unsigned char END_PACKET = 193; static const char ESC_START_STRING[] = { 0x7d, 0xe0 }; static const char ESC_END_STRING[] = { 0x7d, 0xe1 }; static const char ESC_ESC_STRING[] = { 0x7d, 0x5d }; static const unsigned char PacketCodes[2] = { 192, 193 }; #if defined(USE_ALL_TYPES) static unsigned char EmailImage[] = { 0, 2, 2, 0, 16, '/', 'M', 'S', 'S', 'O', 'N', 'Y', '/', 'I', 'M', 'C', 'I', 'F', '1', '0', '0' }; #endif static unsigned char IdentString[] = { 0, 1, 1, 'S', 'O', 'N', 'Y', ' ', ' ', ' ', ' ', ' ' }; static unsigned char EmptyPacket[] = { 0 }; /* null packet */ static unsigned char SetTransferRate[] = { 0, 1, 3, 0 }; static unsigned char SendImageCount[] = { 0, 2, 1 }; static unsigned char StillImage[] = { 0, 2, 2, 0, 14, '/', 'D', 'C', 'I', 'M', '/', '1', '0', '0', 'M', 'S', 'D', 'C', 'F' }; static unsigned char MpegImage[] = { 0, 2, 2, 0, 16, '/', 'M', 'S', 'S', 'O', 'N', 'Y', '/', 'M', 'O', 'M', 'L', '0', '0', '0', '1' }; static unsigned char SelectImage[] = { 0, 2, 48, 0, 0, 0, 0 }; static unsigned char SendImage[] = { 0, 2, '1', 0, 1, 0, 0 }; static unsigned char SendNextImagePacket[] = { 0, 2, '1', 0 }; static unsigned char SendThumbnail[] = { 0, 2, '0', 0 }; #if 0 static unsigned char SelectCamera[] = { 0, 1, 2 }; static unsigned char DownloadComplete[] = { 0, 2, '0', 255 }; static unsigned char X10Camera[] = { 0, 1, 5 }; static unsigned char X5Camera[] = { 0, 2, 1 }; static unsigned char X13Camera[] = { 0, 2, 18 }; #endif /* * This array contains the expected packet sequence code to to be applied/ * checked for. */ static const unsigned char sony_sequence[] = { 14, 0, 32, 34, 66, 68, 100, 102, 134, 136, 168, 170, 202, 204, 236, 238, 255 }; #ifdef __linux__ static const int baud_rate = 115200; #else # ifdef __sun__ static const int baud_rate = 38400; # else static const int baud_rate = 9600; # endif #endif /** * Returns transfer rate ID */ static int sony_baud_to_id(long baud) { int r; switch (baud) { case 115200: r = 4; break; case 57600: r = 3; break; /* FIXME ??? */ case 38400: r = 2; break; /* works on sun */ case 19200: r = 1; break; /* works on sun */ default: case 9600: r = 0; break; /* works on sun */ } return r; } /** * Reads a byte */ static int sony_read_byte(Camera * camera, unsigned char *b) { int n = gp_port_read(camera->port, b, 1); if (n != 1) return GP_ERROR; else return GP_OK; } /** * Returns the checksum for a packet */ static unsigned char sony_packet_checksum(Packet * p) { unsigned short int o = 0; unsigned long int sum = 0; sum = 0; while (o < p->length) sum += p->buffer[o++]; return 256 - (sum & 255); } /** * Returns TRUE iff the packet is valid */ static int sony_packet_validate(Camera * camera, Packet * p) { unsigned char c = sony_packet_checksum(p); if (c != p->checksum) { GP_DEBUG( "sony_packet_validate: invalid checksum"); return SONY_INVALID_CHECKSUM; } if (129 == p->buffer[0]) { GP_DEBUG( "sony_packet_validate: resend packet"); return SONY_RESEND_PACKET; } if (sony_sequence[camera->pl->sequence_id] != p->buffer[0]) { GP_DEBUG( "sony_packet_validate: invalid sequence"); return SONY_INVALID_SEQUENCE; } return GP_OK; } /** * Constructs a packet. */ static int sony_packet_make(Camera * camera, Packet * p, unsigned char *buffer, unsigned short int length) { p->length = 0; while (length--) p->buffer[p->length++] = *(buffer++); if (255 == sony_sequence[++(camera->pl->sequence_id)]) camera->pl->sequence_id = 0; p->buffer[0] = sony_sequence[camera->pl->sequence_id++]; if (255 == sony_sequence[camera->pl->sequence_id]) camera->pl->sequence_id = 0; p->checksum = sony_packet_checksum(p); return TRUE; } /** * Reads a packet. */ static int sony_packet_read(Camera * camera, Packet * pack) { unsigned int n; unsigned char byte = 0; static Packet p; GP_DEBUG( "sony_packet_read()"); p.length = 0; for (n = 0; n < 2; n++) { for (byte = 0; byte != (unsigned char) PacketCodes[n];) { if (sony_read_byte(camera, &byte) == GP_ERROR) { return FALSE; } if (n > 0) { if (SONY_ESCAPE_CHAR == byte) { unsigned char extra; sony_read_byte(camera, &extra); switch (extra) { case 1: case 7: case 0xe1: case 0xe0: extra &= 0xcf; p.buffer[p.length++] = extra; continue; case 0x5d: p.buffer[p.length++] = byte; continue; default: p.buffer[p.length++] = byte; p.buffer[p.length++] = extra; continue; } } else p.buffer[p.length++] = byte; } } } p.length -= 2; p.checksum = p.buffer[p.length]; memcpy(pack, &p, sizeof(Packet)); return TRUE; } /** * Sends a packet */ static int sony_packet_write(Camera * camera, Packet * p) { unsigned short int count; int rc; GP_DEBUG( "sony_packet_write()"); /** * This usleep is necessary when using (relatively) fast CPUs, * as P4 2Ghz. Without it gphoto2 stops with a segmentation fault * and the --debug message sony_packet_validate: invalid sequence * Failed to read packet during transfer. The delay is somewhat * arbitrary. (alberto-g@(remove_me)inventati.org) **/ usleep(10000); rc = gp_port_write(camera->port, &START_PACKET, 1); p->buffer[p->length] = p->checksum; for (count = 0; count < p->length + 1 && rc != GP_ERROR; count++) { switch ((unsigned char) p->buffer[count]) { case SONY_ESCAPE_CHAR: rc = gp_port_write(camera->port, ESC_ESC_STRING, 2); break; case SONY_START_CHAR: rc = gp_port_write(camera->port, ESC_START_STRING, 2); break; case SONY_END_CHAR: rc = gp_port_write(camera->port, ESC_END_STRING, 2); break; default: rc = gp_port_write(camera->port, (char *) &p->buffer[count], 1); break; } } if (rc != GP_ERROR) rc = gp_port_write(camera->port, (char *) &END_PACKET, 1); return rc; } /** * Communicates packets */ static int sony_converse(Camera * camera, Packet * out, unsigned char *str, int len) { Packet ps; char old_sequence = 33; int sequence_count = 0; int invalid_sequence = 0; int count; int rc; GP_DEBUG( "sony_converse()"); sony_packet_make(camera, &ps, str, len); for (count = 0; count < SONY_CONVERSE_RETRY; count++) { rc = sony_packet_write(camera, &ps); if (rc == GP_OK) { if (sony_packet_read(camera, out)) { switch (sony_packet_validate(camera, out)) { case SONY_INVALID_CHECKSUM: if (invalid_sequence) { sony_packet_make(camera, &ps, str, len); break; } GP_DEBUG ("Checksum invalid"); ps.buffer[0] = 129; ps.checksum = sony_packet_checksum(&ps); break; case SONY_INVALID_SEQUENCE: if (camera->pl->model != SONY_MODEL_DSC_F55) { invalid_sequence = 1; sony_packet_make(camera, &ps, str, len); break; } if (old_sequence == out->buffer[0]) sequence_count++; else if (0 == sequence_count) old_sequence = out->buffer[0]; if (sequence_count == 4) { GP_DEBUG ("Attempting to reset sequence id - image may be corrupt."); camera->pl->sequence_id = 0; while (sony_sequence [camera->pl-> sequence_id] != old_sequence) camera->pl-> sequence_id++; return GP_OK; } GP_DEBUG ("Invalid Sequence"); ps.buffer[0] = 129; ps.checksum = sony_packet_checksum(&ps); break; case SONY_RESET_SEQUENCE: camera->pl->sequence_id = 0; return GP_OK; case SONY_RESEND_PACKET: GP_DEBUG ("Resending Packet"); break; case GP_OK: return GP_OK; default: GP_DEBUG ("Unknown Error"); break; } } else { /* printf("Incomplete packet\n"); */ ps.buffer[0] = 129; ps.checksum = sony_packet_checksum(&ps); } } } GP_DEBUG( "Failed to read packet during transfer."); return GP_ERROR; } /** * Sets baud rate */ static int sony_baud_port_set(Camera * camera, long baud) { gp_port_settings settings; gp_port_get_settings(camera->port, &settings); settings.serial.speed = baud; gp_port_set_settings(camera->port, settings); usleep(70000); return GP_OK; } /** * Sets baud rate */ static int sony_baud_set(Camera * camera, long baud) { Packet dp; int rc; GP_DEBUG( "sony_baud_set(%ld)", baud); if (camera->pl->current_baud_rate != baud) { /* FIXME */ SetTransferRate[3] = sony_baud_to_id(baud); rc = sony_converse(camera, &dp, SetTransferRate, 4); if (rc == GP_OK) { sony_baud_port_set(camera, baud); rc = sony_converse(camera, &dp, EmptyPacket, 1); if (rc == GP_OK) { camera->pl->current_baud_rate = baud; } usleep(100000); /* 50000 was good too, jw */ } } else { rc = GP_OK; } return rc; } /** * Port initialisation */ static int sony_init_port (Camera *camera) { gp_port_settings settings; int rc; rc = gp_port_set_timeout (camera->port, 5000); if (rc == GP_OK) { gp_port_get_settings(camera->port, &settings); settings.serial.speed = 9600; settings.serial.bits = 8; settings.serial.parity = 0; settings.serial.stopbits = 1; rc = gp_port_set_settings(camera->port, settings); if (rc == GP_OK) { rc = gp_port_flush(camera->port, 0); } } return rc; } /** * Establish first contact (remember the prime directive? :) */ static int sony_init_first_contact (Camera *camera) { int count = 0; Packet dp; int rc = GP_ERROR; for (count = 0; count < 3; count++) { camera->pl->sequence_id = 0; rc = sony_converse(camera, &dp, IdentString, 12); if (rc == GP_OK) { GP_DEBUG( "Init OK"); break; } usleep(2000); GP_DEBUG( "Init - Fail %u", count + 1); } return rc; } /** * Device supports MPEG? */ static int sony_is_mpeg_supported (Camera * camera) { return camera->pl->model == SONY_MODEL_DSC_F55; } /** * Initialises camera */ int sony_init (Camera * camera, SonyModel model) { int rc; camera->pl->model = model; camera->pl->current_baud_rate = -1; camera->pl->current_mpeg_mode = -1; rc = sony_init_port (camera); if (rc == GP_OK) rc = sony_init_first_contact (camera); return rc; } /** * Reset the camera sequence count and baud rate. */ int sony_exit(Camera * camera) { Packet dp; int rc = GP_ERROR; rc = sony_baud_set(camera, 9600); while (rc == GP_OK && camera->pl->sequence_id > 0) { rc = sony_converse(camera, &dp, EmptyPacket, 1); } return rc; } static int sony_set_file_mode(Camera * camera, SonyFileType file_type) { int rc = GP_OK; Packet dp; if (file_type == SONY_FILE_MPEG) { if (camera->pl->current_mpeg_mode != 1) { rc = sony_converse(camera, &dp, MpegImage, 21); if (rc == GP_OK) { camera->pl->current_mpeg_mode = 1; } } } else { if (camera->pl->current_mpeg_mode != 0) { rc = sony_converse(camera, &dp, StillImage, 19); if (rc == GP_OK) { camera->pl->current_mpeg_mode = 0; } } } return rc; } /** * Return count of images taken. */ int sony_file_count(Camera * camera, SonyFileType file_type, int *count) { Packet dp; int rc; GP_DEBUG( "sony_file_count()"); if (file_type == SONY_FILE_MPEG && (! sony_is_mpeg_supported(camera))) { rc = GP_OK; *count = 0; } else { *count = -1; rc = sony_converse(camera, &dp, SetTransferRate, 4); if (rc == GP_OK) { int rc = sony_set_file_mode(camera, file_type); if (rc == GP_OK) { rc = sony_converse(camera, &dp, SendImageCount, 3); if (rc == GP_OK) { int nr = dp.buffer[5] | (dp.buffer[4]<<8); GP_DEBUG ("count = %d", nr); *count = nr; } } } } return rc; } /** * Fetches file name. */ int sony_file_name_get(Camera *camera, int imageid, SonyFileType mpeg, char buf[13]) { Packet dp; int rc; GP_DEBUG( "sony_file_name_get()"); rc = sony_set_file_mode(camera, mpeg); if (rc == GP_OK) { sony_baud_set(camera, baud_rate); /* FIXME: Not nice, changing global data like this. */ SelectImage[3] = (imageid >> 8); SelectImage[4] = imageid & 0xff; rc = sony_converse(camera, &dp, SelectImage, 7); if (rc == GP_OK) { memcpy(buf, &dp.buffer[5], 8); buf[8] = '.'; memcpy(buf+9, &dp.buffer[5+8], 3); buf[12] = 0; } } return rc; } /** * Is it an MPEG file? */ int sony_is_mpeg_file_name(const char * file_name) { return strncmp(file_name,"MOV",3)==0; } /** * Fetches an image. */ static int sony_file_get(Camera * camera, int imageid, int file_type, CameraFile * file, GPContext *context) { int sc; /* count of bytes to skip at start of packet */ Packet dp; int rc; char buffer[128]; GP_DEBUG( "sony_file_get()"); rc = sony_set_file_mode(camera, file_type); if (rc != GP_OK) { return rc; } if (gp_context_cancel(context) == GP_CONTEXT_FEEDBACK_CANCEL) { return GP_ERROR_CANCEL; } rc = gp_file_clean(file); if (rc == GP_OK) { gp_file_set_mime_type (file, GP_MIME_JPEG); /* The file name we specify here is not important, * since the calling function, gp_filesystem_get_file_impl, * overwrites it with the correct one. */ sprintf(buffer, SONY_FILE_NAME_FMT, imageid); gp_file_set_name (file, buffer); sony_baud_set(camera, baud_rate); rc = sony_set_file_mode(camera, file_type); if (rc == GP_OK) { if (file_type == SONY_FILE_THUMBNAIL) { sc = 0x247; SelectImage[3] = (imageid >> 8); SelectImage[4] = imageid & 0xff; sony_converse(camera, &dp, SelectImage, 7); if (camera->pl->model != SONY_MODEL_DSC_F55) { gp_file_append(file, "\xff\xd8\xff", 3); } for (;;) { if (gp_context_cancel(context) == GP_CONTEXT_FEEDBACK_CANCEL) { rc = GP_ERROR_CANCEL; break; } gp_context_idle(context); sony_converse(camera, &dp, SendThumbnail, 4); gp_file_append(file, (char *) dp.buffer + sc, dp.length - sc); sc = 7; if (3 == dp.buffer[4]) break; } } else { sc = 11; SendImage[3] = (imageid >> 8); SendImage[4] = imageid; sony_converse(camera, &dp, SendImage, 7); for (;;) { if (gp_context_cancel(context) == GP_CONTEXT_FEEDBACK_CANCEL) { rc = GP_ERROR_CANCEL; break; } gp_context_idle(context); gp_file_append(file, (char *) dp.buffer + sc, dp.length - sc); if (file_type == SONY_FILE_EXIF) { const char *fdata; unsigned long fsize; gp_file_get_data_and_size (file, &fdata, &fsize); /* FIXME: 4096 is a rather */ /* arbitrary value */ if (fsize > 4096) break; } sc = 7; if (3 == dp.buffer[4]) break; sony_converse(camera, &dp, SendNextImagePacket, 4); } } } sony_baud_set(camera, 9600); if (rc != GP_OK) { gp_file_clean(file); } } return rc; } /** * Fetches a thumbnail image. */ int sony_thumbnail_get(Camera * camera, int imageid, CameraFile * file, GPContext *context) { return sony_file_get(camera, imageid, SONY_FILE_THUMBNAIL, file, context); } /** * Fetches an image. */ int sony_image_get(Camera * camera, int imageid, CameraFile * file, GPContext *context) { return sony_file_get(camera, imageid, SONY_FILE_IMAGE, file, context); } /** * Fetches EXIF information. */ int sony_exif_get(Camera * camera, int imageid, CameraFile * file, GPContext *context) { return sony_file_get(camera, imageid, SONY_FILE_EXIF, file, context); } /** * Fetches an Mpeg. */ int sony_mpeg_get(Camera * camera, int imageid, CameraFile * file, GPContext *context) { return sony_file_get(camera, imageid, SONY_FILE_MPEG, file, context); } /** * Fetches image details. */ int sony_image_info(Camera * camera, int imageid, SonyFileType file_type, CameraFileInfo * info, GPContext *context) { unsigned int l = 0; int rc; Packet dp; rc = sony_set_file_mode(camera, file_type); if (rc != GP_OK) { return rc; } if (gp_context_cancel(context) == GP_CONTEXT_FEEDBACK_CANCEL) { return GP_ERROR_CANCEL; } SelectImage[3] = (imageid >> 8); SelectImage[4] = imageid & 0xff; rc = sony_converse(camera, &dp, SelectImage, 7); if (rc == GP_OK) { l = (l << 8) | dp.buffer[16]; l = (l << 8) | dp.buffer[17]; l = (l << 8) | dp.buffer[18]; l = (l << 8) | dp.buffer[19]; info->file.fields = GP_FILE_INFO_SIZE | GP_FILE_INFO_TYPE; info->file.size = l; info->preview.fields = GP_FILE_INFO_TYPE; if (file_type == SONY_FILE_MPEG) { strcpy (info->file.type, GP_MIME_AVI); } else { strcpy (info->file.type, GP_MIME_JPEG); } } return rc; } /* * Local Variables: * c-file-style:"linux" * indent-tabs-mode:t * End: */