This is basically a continuation of the work from #1002 but her for the camlibs directory. The purpose was to replace spaces as a means for indentation with tab characters. Note: there are some files which are still offending the general rule but are (mostly) consistent within themselves. I let them be for the moment. Fixing them would basically replace the whole file. Also: there are still a ton of whitespace related inconsistencies like "x=10" vs "x = 10" or "func(10)" vs "func (10)". This is a whitespace only change (if you include newlines as whitespace).
917 lines
24 KiB
C
917 lines
24 KiB
C
/* g3.c
|
|
*
|
|
* Copyright 2003 Marcus Meissner <marcus@jet.franken.de>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser 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
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gphoto2/gphoto2-library.h>
|
|
#include <gphoto2/gphoto2-result.h>
|
|
#include <gphoto2/gphoto2-port-log.h>
|
|
|
|
#include "libgphoto2/i18n.h"
|
|
|
|
|
|
/* channel header:
|
|
*
|
|
* channel host/client length data checksum padding
|
|
* 01 01 00 00 07 00 00 00 <7 byte> <1 byte> <fill to next 4>
|
|
*
|
|
*/
|
|
|
|
static int
|
|
g3_channel_read(GPPort *port, int *channel, char **buffer, unsigned int *len)
|
|
{
|
|
unsigned char xbuf[0x800];
|
|
unsigned int tocopy, curlen;
|
|
int ret;
|
|
|
|
ret = gp_port_read(port, (char *)xbuf, 0x800);
|
|
if (ret < GP_OK) {
|
|
gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
|
|
return ret;
|
|
}
|
|
|
|
if ((xbuf[2] != 0xff) && (xbuf[3] != 0xff)) {
|
|
gp_log(GP_LOG_ERROR, "g3" ,"first bytes do not match.\n");
|
|
return GP_ERROR_IO;
|
|
}
|
|
|
|
*channel = xbuf[1];
|
|
*len = xbuf[4] + (xbuf[5]<<8) + (xbuf[6]<<16) + (xbuf[7]<<24);
|
|
if (*len >= 0xffffffff-0x800-1) return GP_ERROR_CORRUPTED_DATA;
|
|
/* Safety buffer of 0x800 ... we can only read in 0x800 chunks,
|
|
* otherwise the communication gets hickups. However *len might be
|
|
* less.
|
|
*/
|
|
gp_log(GP_LOG_DEBUG, "g3" ,"length %u\n", *len);
|
|
if (!*buffer)
|
|
*buffer = malloc(*len + 1 + 0x800);
|
|
else
|
|
*buffer = realloc(*buffer, *len + 1 + 0x800);
|
|
if (!*buffer) {
|
|
gp_log(GP_LOG_ERROR, "g3" ,"malloc failed, size %d too large?\n", *len+1+0x800);
|
|
return GP_ERROR_NO_MEMORY;
|
|
}
|
|
tocopy = *len;
|
|
if (tocopy > 0x800-8) tocopy = 0x800-8;
|
|
memcpy(*buffer, xbuf+8, tocopy);
|
|
curlen = tocopy;
|
|
while (curlen < *len) {
|
|
int toread = 0x800;
|
|
if (toread + curlen > *len + 1 + 0x800)
|
|
toread = *len + 1 + 0x800 - curlen;
|
|
if (toread <= 0)
|
|
break;
|
|
|
|
ret = gp_port_read(port, *buffer + curlen, toread);
|
|
if (ret < GP_OK) {
|
|
gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
|
|
return ret;
|
|
}
|
|
curlen += ret;
|
|
}
|
|
(*buffer)[*len] = 0x00;
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
g3_channel_read_bytes(
|
|
GPPort *port, int *channel, char **buffer, int expected, GPContext *context,
|
|
const char *msg
|
|
) {
|
|
unsigned char *xbuf;
|
|
int id, ret, len, xlen = 0;
|
|
|
|
if (!*buffer)
|
|
*buffer = malloc (expected);
|
|
else
|
|
*buffer = realloc (*buffer, expected);
|
|
xbuf = malloc(65536 + 12);
|
|
|
|
id = gp_context_progress_start (context, expected, "%s", msg);
|
|
|
|
/* The camera lets us read in packets of at least max 64kb size. */
|
|
while (expected > 0) {
|
|
int rest = expected;
|
|
if (rest > 65536) rest = 65536;
|
|
|
|
rest = (rest + 9 + 3) & ~3;
|
|
if (rest < 0x800) rest = 0x800;
|
|
|
|
ret = gp_port_read(port, (char *)xbuf, rest);
|
|
if (ret < GP_OK) {
|
|
gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
|
|
gp_context_progress_stop (context, id);
|
|
free(xbuf);
|
|
return ret;
|
|
}
|
|
if (ret != rest) {
|
|
gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
|
|
gp_context_progress_stop (context, id);
|
|
free(xbuf);
|
|
return ret;
|
|
}
|
|
|
|
if ((xbuf[2] != 0xff) || (xbuf[3] != 0xff)) {
|
|
gp_log(GP_LOG_ERROR, "g3", "first bytes do not match.\n");
|
|
gp_context_progress_stop (context, id);
|
|
free(xbuf);
|
|
return GP_ERROR_IO;
|
|
}
|
|
len = xbuf[4] + (xbuf[5]<<8) + (xbuf[6]<<16) + (xbuf[7]<<24);
|
|
*channel = xbuf[1];
|
|
if (len > expected)
|
|
gp_log(GP_LOG_ERROR,"g3","len %d is > rest expected %d\n", len, expected);
|
|
memcpy(*buffer+xlen, xbuf+8, len);
|
|
expected -= len;
|
|
xlen += len;
|
|
gp_context_progress_update (context, id, xlen);
|
|
}
|
|
gp_context_progress_stop (context, id);
|
|
free(xbuf);
|
|
return GP_OK;
|
|
}
|
|
|
|
static int
|
|
g3_channel_write(GPPort *port, int channel, char *buffer, int len)
|
|
{
|
|
unsigned char *xbuf;
|
|
int ret = GP_OK, nlen, curlen = 0;
|
|
|
|
while (len > 0) {
|
|
int sendlen = len;
|
|
|
|
if (sendlen>65536) sendlen = 65536;
|
|
nlen = (4 + 4 + sendlen + 1 + 3) & ~3;
|
|
xbuf = calloc(nlen,1);
|
|
|
|
xbuf[0] = 1;
|
|
xbuf[1] = channel;
|
|
xbuf[2] = 0;
|
|
xbuf[3] = 0;
|
|
xbuf[4] = sendlen & 0xff;
|
|
xbuf[5] = (sendlen>>8) & 0xff;
|
|
xbuf[6] = (sendlen>>16) & 0xff;
|
|
xbuf[7] = (sendlen>>24) & 0xff;
|
|
memcpy(xbuf+8, buffer + curlen, sendlen);
|
|
curlen += sendlen;
|
|
xbuf[8+sendlen] = 0x03;
|
|
ret = gp_port_write( port, (char*)xbuf, nlen);
|
|
free(xbuf);
|
|
if (ret < GP_OK) break;
|
|
len -= sendlen;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
g3_ftp_command_and_reply(GPPort *port, char *cmd, char **reply) {
|
|
int ret, channel;
|
|
unsigned int len;
|
|
char *realcmd, *s;
|
|
|
|
realcmd = aprintf("%s\r\n", cmd);
|
|
|
|
gp_log(GP_LOG_DEBUG, "g3" , "sending %s", cmd);
|
|
ret = g3_channel_write(port, 1, realcmd, strlen(realcmd));
|
|
free(realcmd);
|
|
if (ret < GP_OK) {
|
|
gp_log (GP_LOG_ERROR, "g3", "ftp command write failed? %d\n", ret);
|
|
return ret;
|
|
}
|
|
ret = g3_channel_read(port, &channel, reply, &len);
|
|
if (ret < GP_OK) {
|
|
gp_log (GP_LOG_ERROR, "g3", "ftp reply read failed? %d\n", ret);
|
|
return ret;
|
|
}
|
|
s = strchr(*reply, '\r');
|
|
if (s) *s='\0';
|
|
|
|
gp_log(GP_LOG_DEBUG, "g3" , "reply %s", *reply);
|
|
return GP_OK;
|
|
}
|
|
|
|
int
|
|
camera_id (CameraText *id)
|
|
{
|
|
strcpy(id->text, "ricoh_g3");
|
|
return (GP_OK);
|
|
}
|
|
|
|
int
|
|
camera_abilities (CameraAbilitiesList *list)
|
|
{
|
|
CameraAbilities a;
|
|
|
|
memset(&a, 0, sizeof(a));
|
|
strcpy(a.model, "Ricoh:Caplio G3");
|
|
a.status = GP_DRIVER_STATUS_PRODUCTION;
|
|
a.port = GP_PORT_USB;
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2204;
|
|
|
|
a.operations = GP_OPERATION_NONE;
|
|
a.file_operations = GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_EXIF;
|
|
a.folder_operations = GP_FOLDER_OPERATION_MAKE_DIR |
|
|
GP_FOLDER_OPERATION_REMOVE_DIR ;
|
|
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio RR30");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2202;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio 300G");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2203;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Medion:MD 6126");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2205;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio G4");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2208;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Capilo RX");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220b;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio GX");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220c;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R1");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220d;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
/* same id as above */
|
|
strcpy(a.model, "Ricoh:Caplio RZ1");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220d;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Sea & Sea:5000G");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220e;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Rollei:dr5");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x220f;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R1v");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2212;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R2");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2213;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio GX 8");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2214;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R3");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2216;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R4");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x2217;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
strcpy(a.model, "Ricoh:Caplio R5");
|
|
a.usb_vendor = 0x5ca;
|
|
a.usb_product = 0x221a;
|
|
gp_abilities_list_append(list, a);
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
g3_cwd_command( GPPort *port, const char *folder) {
|
|
char *cmd, *reply = NULL;
|
|
int ret;
|
|
|
|
cmd = aprintf("CWD %s", folder);
|
|
ret = g3_ftp_command_and_reply(port, cmd, &reply);
|
|
free(cmd);
|
|
if (ret < GP_OK) {
|
|
free(reply);
|
|
return ret;
|
|
}
|
|
if (reply[0]=='5') /* Failed, most likely no such directory */
|
|
ret = GP_ERROR_DIRECTORY_NOT_FOUND;
|
|
free(reply);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
|
|
CameraFileType type, CameraFile *file, void *data,
|
|
GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *buf = NULL, *reply = NULL, *cmd =NULL, *msg = NULL;
|
|
unsigned int len, bytes;
|
|
int seek;
|
|
int ret, channel;
|
|
|
|
ret = g3_cwd_command (camera->port, folder);
|
|
if (ret < GP_OK) goto out;
|
|
|
|
switch (type) {
|
|
case GP_FILE_TYPE_NORMAL:
|
|
msg = _("Downloading...");
|
|
if (strstr(filename,"AVI") || strstr(filename,"avi"))
|
|
msg = _("Downloading movie...");
|
|
if (strstr(filename,"jpg") || strstr(filename,"JPG") ||
|
|
strstr(filename,"tif") || strstr(filename,"TIF")
|
|
)
|
|
msg = _("Downloading image...");
|
|
if (strstr(filename,"wav") || strstr(filename,"WAV"))
|
|
msg = _("Downloading audio...");
|
|
cmd = aprintf("RETR %s", filename);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
|
|
free(cmd);
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] != '1') { /* error, most likely file not found */
|
|
ret = GP_ERROR_FILE_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
bytes = 0;
|
|
sscanf(buf, "150 data connection for RETR.(%d)", &bytes);
|
|
break;
|
|
case GP_FILE_TYPE_EXIF:
|
|
msg = _("Downloading EXIF data...");
|
|
if (!strstr(filename, ".JPG") && !strstr(filename, ".jpg")) {
|
|
gp_context_error (context, _("No EXIF data available for %s."),
|
|
filename);
|
|
ret = GP_ERROR_FILE_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
cmd = aprintf("-SRET %s", filename);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
|
|
free(cmd);
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] != '1') { /* error, most likely file not found */
|
|
ret = GP_ERROR_FILE_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
bytes = seek = 0;
|
|
sscanf(buf, "150 %d byte Seek=%d", &bytes, &seek);
|
|
if (seek == -2) {
|
|
/* FIXME: pretty bad, the camera has some time out problems
|
|
* if this happens */
|
|
gp_context_error (context, _("No EXIF data available for %s."),
|
|
filename);
|
|
ret = GP_ERROR_FILE_NOT_FOUND;
|
|
g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
|
|
goto out;
|
|
}
|
|
bytes += seek;
|
|
break;
|
|
default:
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
ret = g3_channel_read_bytes(camera->port, &channel, &buf, bytes, context, msg);
|
|
if (ret < GP_OK) goto out;
|
|
ret = g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
|
|
if (ret < GP_OK) goto out;
|
|
gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
|
|
gp_file_set_data_and_size (file, buf, bytes);
|
|
buf = NULL; /* now owned by libgphoto2 filesystem */
|
|
|
|
out:
|
|
free(buf);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
|
|
#if 0
|
|
/* Works to some degree, but the firmware on the camera is not really happy
|
|
* with it and sometimes refuses to send data the correct way
|
|
*/
|
|
static int
|
|
put_file_func (CameraFilesystem *fs, const char *folder, const char *fn, CameraFile *file,
|
|
void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *buf = NULL, *reply = NULL, *cmd =NULL;
|
|
const char *imgdata = NULL;
|
|
int ret, channel, len;
|
|
long size;
|
|
|
|
ret = g3_cwd_command (camera->port, folder);
|
|
if (ret < GP_OK) goto out;
|
|
|
|
ret = gp_file_get_data_and_size (file, &imgdata, &size);
|
|
if (ret < GP_OK) goto out;
|
|
|
|
cmd = aprintf("-STOR %ld %s", size, fn);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
|
|
free(cmd);
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] != '1') { /* error, most likely file not found */
|
|
ret = GP_ERROR;
|
|
goto out;
|
|
}
|
|
ret = g3_channel_write(camera->port, 2, (char*)imgdata, size); /* data */
|
|
if (ret < GP_OK) goto out;
|
|
ret = g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
|
|
if (ret < GP_OK) goto out;
|
|
out:
|
|
free(buf);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
delete_file_func (CameraFilesystem *fs, const char *folder,
|
|
const char *filename, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *reply = NULL, *cmd = NULL;
|
|
int ret;
|
|
|
|
ret = g3_cwd_command (camera->port, folder);
|
|
if (ret < GP_OK)
|
|
return ret;
|
|
|
|
cmd = aprintf("DELE %s", filename);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
|
|
if (ret < GP_OK)
|
|
goto out;
|
|
if (reply[0] == '5') {
|
|
gp_context_error (context, _("Could not delete file."));
|
|
ret = GP_ERROR;
|
|
}
|
|
out:
|
|
free(cmd);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
rmdir_func (CameraFilesystem *fs, const char *folder,
|
|
const char *name, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *reply = NULL, *cmd = NULL;
|
|
int ret;
|
|
|
|
ret = g3_cwd_command (camera->port, folder);
|
|
if (ret<GP_OK)
|
|
return ret;
|
|
|
|
cmd = realloc(cmd,strlen("RMD ")+strlen(name)+1);
|
|
if (!cmd) return GP_ERROR_NO_MEMORY;
|
|
sprintf(cmd,"RMD %s",name);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
|
|
if (ret < GP_OK)
|
|
goto out;
|
|
if (reply[0] == '5') {
|
|
gp_context_error (context, _("Could not remove directory."));
|
|
ret = GP_ERROR;
|
|
}
|
|
out:
|
|
free(cmd);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
mkdir_func (CameraFilesystem *fs, const char *folder,
|
|
const char *name, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *reply = NULL, *cmd = NULL;
|
|
int ret;
|
|
|
|
ret = g3_cwd_command (camera->port, folder);
|
|
if (ret<GP_OK)
|
|
return ret;
|
|
|
|
cmd = realloc(cmd,strlen("MKD ")+strlen(name)+1);
|
|
if (!cmd) return GP_ERROR_NO_MEMORY;
|
|
sprintf(cmd,"MKD %s",name);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
|
|
if (ret < GP_OK)
|
|
goto out;
|
|
if (reply[0] == '5') {
|
|
gp_context_error (context, _("Could not create directory."));
|
|
ret = GP_ERROR;
|
|
}
|
|
out:
|
|
free(cmd);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
camera_summary (Camera *camera, CameraText *summary, GPContext *context)
|
|
{
|
|
char *t = summary->text;
|
|
int ret;
|
|
char *buf = NULL;
|
|
|
|
t[0]='\0';
|
|
ret = g3_ftp_command_and_reply (camera->port, "-VER", &buf);
|
|
if (ret == GP_OK)
|
|
sprintf(t+strlen(t), _("Version: %s\n"), buf+4); /* skip "200 " */
|
|
ret = g3_ftp_command_and_reply(camera->port, "-RTST", &buf); /* RTC test */
|
|
if (ret == GP_OK) {
|
|
int rtcstat;
|
|
if (sscanf(buf, "200 RTC status=%d", &rtcstat))
|
|
sprintf(t+strlen(t), _("RTC Status: %d\n"), rtcstat);
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-TIME", &buf);
|
|
if (ret == GP_OK) {
|
|
char day[20], time[20];
|
|
if (sscanf(buf, "200 %s %s for -TIME", day, time))
|
|
sprintf(t+strlen(t), _("Camera time: %s %s\n"), day, time);
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-GCID", &buf);
|
|
if (ret == GP_OK) {
|
|
char camid[40];
|
|
if (sscanf(buf, "200 CameraID=%s for -GCID", camid))
|
|
sprintf(t+strlen(t), _("Camera ID: %s\n"), camid);
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-GSID", &buf);
|
|
if (ret == GP_OK) {
|
|
char cardid[40];
|
|
if (strstr(buf, "200 SD ID= for -GSID")) {
|
|
sprintf(t+strlen(t), _("No SD Card inserted.\n"));
|
|
} else {
|
|
if (sscanf(buf, "200 SD ID=%s for -GSID", cardid)) {
|
|
sprintf(t+strlen(t), _("SD Card ID: %s\n"), cardid);
|
|
}
|
|
}
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-GTPN", &buf);
|
|
if (ret == GP_OK) {
|
|
int total;
|
|
if (sscanf (buf, "200 TotalPhotoNo=%d for -GTPN", &total))
|
|
sprintf(t+strlen(t), _("Photos on camera: %d\n"), total);
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-DCAP /EXT0", &buf);
|
|
if (ret == GP_OK) {
|
|
int space, sfree;
|
|
if (sscanf (buf, "200 /EXT0 capacity %d byte,free %d byte.", &space, &sfree)) {
|
|
sprintf(t+strlen(t), _("SD memory: %d MB total, %d MB free.\n"), space/1024/1024, sfree/1024/1024);
|
|
}
|
|
}
|
|
ret = g3_ftp_command_and_reply(camera->port, "-DCAP /IROM", &buf);
|
|
if (ret == GP_OK) {
|
|
int space, sfree;
|
|
if (sscanf (buf, "200 /IROM capacity %d byte,free %d byte.", &space, &sfree)) {
|
|
sprintf(t+strlen(t), _("Internal memory: %d MB total, %d MB free.\n"), space/1024/1024, sfree/1024/1024);
|
|
}
|
|
}
|
|
free (buf);
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
camera_about (Camera *camera, CameraText *about, GPContext *context)
|
|
{
|
|
strcpy (about->text, _("Ricoh Caplio G3.\n"
|
|
"Marcus Meissner <marcus@jet.franken.de>\n"
|
|
"Reverse engineered using USB Snoopy, looking\n"
|
|
"at the firmware update image and wild guessing.\n"
|
|
));
|
|
|
|
return (GP_OK);
|
|
}
|
|
|
|
static int
|
|
get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
|
|
CameraFileInfo *info, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
int ret, bytes, width, height, k;
|
|
struct tm xtm;
|
|
char *cmd = NULL, *reply = NULL;
|
|
|
|
info->file.fields = GP_FILE_INFO_TYPE |
|
|
GP_FILE_INFO_SIZE | GP_FILE_INFO_TYPE;
|
|
strcpy(info->file.type,GP_MIME_UNKNOWN);
|
|
|
|
if (!strcmp(filename+9,"JPG") || !strcmp(filename+9,"jpg"))
|
|
strcpy(info->file.type,GP_MIME_JPEG);
|
|
|
|
if (!strcmp(filename+9,"AVI") || !strcmp(filename+9,"avi"))
|
|
strcpy(info->file.type,GP_MIME_AVI);
|
|
|
|
if (!strcmp(filename+9,"WAV") || !strcmp(filename+9,"wav"))
|
|
strcpy(info->file.type,GP_MIME_WAV);
|
|
|
|
if (!strcmp(filename+9,"MTA") || !strcmp(filename+9,"mta"))
|
|
strcpy(info->file.type,"text/plain");
|
|
|
|
cmd = aprintf("-FDAT %s/%s", folder,filename);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
|
|
if (ret < GP_OK) goto out;
|
|
if (sscanf(reply, "200 date=%d:%d:%d %d:%d:%d",
|
|
&xtm.tm_year,
|
|
&xtm.tm_mon,
|
|
&xtm.tm_mday,
|
|
&xtm.tm_hour,
|
|
&xtm.tm_min,
|
|
&xtm.tm_sec
|
|
)) {
|
|
xtm.tm_mon--; /* range 0 - 11 */
|
|
xtm.tm_year -= 1900; /* number of years since 1900 */
|
|
info->file.mtime = mktime(&xtm);
|
|
info->file.fields |= GP_FILE_INFO_MTIME;
|
|
}
|
|
|
|
/* -INFO command only sometimes work on non jpeg/avi files */
|
|
if ( !strcmp(info->file.type,GP_MIME_JPEG) ||
|
|
!strcmp(info->file.type,GP_MIME_AVI)
|
|
) {
|
|
sprintf(cmd, "-INFO %s/%s", folder,filename);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
|
|
if (ret < GP_OK) goto out;
|
|
|
|
/* 200 1330313 byte W=2048 H=1536 K=0 for -INFO */
|
|
if (sscanf(reply, "200 %d byte W=%d H=%d K=%d for -INFO", &bytes, &width, &height , &k)) {
|
|
if (width != 0 && height != 0) {
|
|
info->file.fields |= GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT;
|
|
info->file.height = height;
|
|
info->file.width = width;
|
|
}
|
|
info->file.fields |= GP_FILE_INFO_SIZE;
|
|
info->file.size = bytes;
|
|
if (k != 0)
|
|
gp_log(GP_LOG_ERROR, "g3", "k is %d for %s/%s\n", k, folder,filename);
|
|
}
|
|
}
|
|
out:
|
|
free(reply);
|
|
free(cmd);
|
|
|
|
return (GP_OK);
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
|
|
void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *buf = NULL, *reply = NULL;
|
|
char *cmd = NULL;
|
|
unsigned int len, rlen;
|
|
int ret = GP_OK, channel;
|
|
|
|
if (!strcmp("/",folder)) {
|
|
/* Lets hope they dont invent other names. */
|
|
|
|
/* We check the folder we get, since we should
|
|
* not add EXT0 without SD card.
|
|
*/
|
|
ret = g3_ftp_command_and_reply(camera->port, "-NLST /", &buf);
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] == '4') {
|
|
/* seen with R3: 450 Open Error... likely OK. */
|
|
goto out;
|
|
}
|
|
if (buf[0] != '1') {
|
|
ret = GP_ERROR_IO;
|
|
goto out;
|
|
}
|
|
ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
|
|
if (ret < GP_OK) goto out;
|
|
ret = g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply */
|
|
if (ret < GP_OK) goto out;
|
|
gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
|
|
|
|
if (!strcmp("/EXT0",buf))
|
|
gp_list_append (list, "EXT0", NULL);
|
|
|
|
/* always add internal memory */
|
|
gp_list_append (list, "IROM", NULL);
|
|
return GP_OK;
|
|
}
|
|
|
|
cmd = malloc(6 + strlen(folder) + 1);
|
|
strcpy(cmd, "-NLST ");
|
|
strcat(cmd, folder);
|
|
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
|
|
free(cmd);cmd = NULL;
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] == '1') { /* 1xx means another reply follows */
|
|
unsigned int n = 0;
|
|
|
|
ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
|
|
if (ret < GP_OK) goto out;
|
|
g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply */
|
|
if (ret < GP_OK) goto out;
|
|
gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
|
|
|
|
for (n=0;n<len/32;n++) {
|
|
if (buf[n*32+11] == 0x10) {
|
|
if (buf[n*32] == '.') /* skip . and .. entries */
|
|
continue;
|
|
ret = gp_list_append (list, buf+n*32, NULL);
|
|
if (ret != GP_OK) goto out;
|
|
}
|
|
}
|
|
} else {
|
|
if (buf[0] == '4')
|
|
ret = GP_OK;
|
|
else
|
|
ret = GP_ERROR_IO;
|
|
}
|
|
out:
|
|
free(buf);
|
|
free(reply);
|
|
return ret;
|
|
}
|
|
|
|
/* for DOS FAT -> UNIX time conversion */
|
|
static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
|
|
|
|
static int
|
|
file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
|
|
void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
char *buf = NULL, *reply = NULL, *cmd;
|
|
int ret = GP_OK;
|
|
unsigned char *ubuf;
|
|
|
|
cmd = malloc(6 + strlen(folder) + 1);
|
|
strcpy(cmd, "-NLST ");
|
|
strcat(cmd, folder);
|
|
ret = g3_ftp_command_and_reply(camera->port, cmd, (char**)&buf);
|
|
free(cmd); cmd = NULL;
|
|
if (ret < GP_OK) goto out;
|
|
if (buf[0] == '1') { /* 1xx means another reply follows */
|
|
unsigned int n = 0;
|
|
int channel;
|
|
unsigned int len, rlen;
|
|
ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
|
|
if (ret < GP_OK) goto out;
|
|
ret = g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply */
|
|
if (ret < GP_OK) goto out;
|
|
gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
|
|
|
|
ubuf = (unsigned char*)buf;
|
|
for (n=0;n < len/32;n++) {
|
|
if (buf[n*32+11] == 0x20) {
|
|
CameraFileInfo info;
|
|
char xfn[13];
|
|
int date, time, month, year;
|
|
|
|
strncpy (xfn, buf+n*32, 8);
|
|
xfn[8] = '.';
|
|
strncpy (xfn+9, buf+n*32+8, 3);
|
|
xfn[8+1+3] = '\0';
|
|
|
|
ret = gp_filesystem_append (fs, folder, xfn, context);
|
|
if (ret < GP_OK) goto out;
|
|
|
|
memset (&info, 0, sizeof (info));
|
|
/* we also get parts of fs info for free, so just set it */
|
|
info.file.fields =
|
|
GP_FILE_INFO_SIZE |
|
|
GP_FILE_INFO_MTIME;
|
|
info.file.size =(ubuf[n*32+28]<<24)|
|
|
(ubuf[n*32+29]<<16)|
|
|
(ubuf[n*32+30]<< 8)|
|
|
(ubuf[n*32+31] );
|
|
if (!strcmp(xfn+9,"JPG") || !strcmp(xfn+9,"jpg")) {
|
|
strcpy(info.file.type,GP_MIME_JPEG);
|
|
info.file.fields |= GP_FILE_INFO_TYPE;
|
|
}
|
|
|
|
if (!strcmp(xfn+9,"AVI") || !strcmp(xfn+9,"avi")) {
|
|
strcpy(info.file.type,GP_MIME_AVI);
|
|
info.file.fields |= GP_FILE_INFO_TYPE;
|
|
}
|
|
|
|
if (!strcmp(xfn+9,"WAV") || !strcmp(xfn+9,"wav")) {
|
|
strcpy(info.file.type,GP_MIME_WAV);
|
|
info.file.fields |= GP_FILE_INFO_TYPE;
|
|
}
|
|
|
|
if (!strcmp(xfn+9,"MTA") || !strcmp(xfn+9,"mta")) {
|
|
strcpy(info.file.type,"text/plain");
|
|
info.file.fields |= GP_FILE_INFO_TYPE;
|
|
}
|
|
info.preview.fields = 0;
|
|
date = (ubuf[n*32+16]) | (ubuf[n*32+17]<<8);
|
|
time = (ubuf[n*32+14]) | (ubuf[n*32+15]<<8);
|
|
|
|
/* from kernel fs/fat/, time_dos2unix. */
|
|
month = ((date >> 5) - 1) & 15;
|
|
year = date >> 9;
|
|
info.file.mtime =
|
|
(time & 31)*2+60*((time >> 5) & 63)+
|
|
(time >> 11)*3600+86400*((date & 31)-1+
|
|
day_n[month]+(year/4)+year*365-
|
|
((year & 3) == 0 && month < 2 ? 1 : 0)+
|
|
3653);
|
|
|
|
ret = gp_filesystem_set_info_noop(fs, folder, xfn, info, context);
|
|
|
|
}
|
|
}
|
|
} else {
|
|
if (buf[0] == '4') /* 450 Open Error ... like dir not there */
|
|
ret = GP_OK;
|
|
else
|
|
ret = GP_ERROR_IO;
|
|
}
|
|
out:
|
|
free(buf);
|
|
free(reply);
|
|
return (GP_OK);
|
|
}
|
|
|
|
static CameraFilesystemFuncs fsfuncs = {
|
|
.file_list_func = file_list_func,
|
|
.get_file_func = get_file_func,
|
|
.del_file_func = delete_file_func,
|
|
.get_info_func = get_info_func,
|
|
.folder_list_func = folder_list_func,
|
|
.make_dir_func = mkdir_func,
|
|
.remove_dir_func = rmdir_func,
|
|
};
|
|
|
|
int
|
|
camera_init (Camera *camera, GPContext *context)
|
|
{
|
|
/*char *buf;*/
|
|
GPPortSettings settings;
|
|
|
|
/* First, set up all the needed function pointers */
|
|
camera->functions->summary = camera_summary;
|
|
camera->functions->about = camera_about;
|
|
|
|
/* Now, tell the filesystem where to get lists, files and info */
|
|
gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
|
|
|
|
gp_port_get_settings(camera->port, &settings);
|
|
settings.usb.inep = 0x81;
|
|
settings.usb.outep = 0x02;
|
|
settings.usb.intep = 0x83;
|
|
gp_port_set_settings(camera->port, settings);
|
|
/*
|
|
* The port is already provided with camera->port (and
|
|
* already open). You just have to use functions like
|
|
* gp_port_timeout_set, gp_port_settings_get, gp_port_settings_set.
|
|
*/
|
|
|
|
/*
|
|
* Once you have configured the port, you should check if a
|
|
* connection to the camera can be established.
|
|
*/
|
|
|
|
/* testing code ...
|
|
buf = NULL;
|
|
g3_ftp_command_and_reply(camera->port, "-PWOF STDBY", &buf);
|
|
*/
|
|
return (GP_OK);
|
|
}
|