Files
libgphoto2/camlibs/ptp/library.c
2003-01-10 21:37:23 +00:00

1127 lines
34 KiB
C

/* library.c
*
* Copyright (C) 2001 Mariusz Woloszyn <emsi@ipartners.pl>
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gphoto2-library.h>
#include <gphoto2-port-log.h>
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
# define _(String) dgettext (GETTEXT_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
#include "ptp.h"
#define GP_MODULE "PTP"
#define CPR(context,result) {short r=(result); if (r!=PTP_RC_OK) {report_result ((context), r); return (translate_ptp_result (r));}}
#define CPR_free(context,result, freeptr) {\
short r=(result);\
if (r!=PTP_RC_OK) {\
report_result ((context), r);\
free(freeptr);\
return (translate_ptp_result (r));\
}\
}
#define CR(result) {int r=(result);if(r<0) return (r);}
/*
#define CR_free(result, freeptr) {\
int r=(result);\
if(r<0){\
free(freeptr);\
return(r);\
}\
}
*/
#define find_folder_handle(fn,s,p,d) { \
{ \
char *backfolder=malloc(strlen(fn)); \
char *tmpfolder; \
memcpy(backfolder,fn+1, strlen(fn)); \
if ((tmpfolder=strchr(backfolder+1,'/'))==NULL) tmpfolder="/";\
p=folder_to_handle(tmpfolder+1,s,0,(Camera *)d);\
free(backfolder); \
} \
}
#define folder_to_storage(fn,s) { \
{ \
if (!strncmp(fn,"/"STORAGE_FOLDER_PREFIX,strlen(STORAGE_FOLDER_PREFIX)+1)) {\
if (strlen(fn)<strlen(STORAGE_FOLDER_PREFIX)+8+1) return (GP_ERROR);\
s = strtol(fn + strlen(STORAGE_FOLDER_PREFIX)+1, NULL, 16);\
} else return (GP_ERROR); \
} \
}
#define STORAGE_FOLDER_PREFIX "store_"
static struct {
short n;
const char *txt;
} ptp_errors[] = {
{PTP_RC_Undefined, N_("PTP Undefined Error")},
{PTP_RC_OK, N_("PTP OK!")},
{PTP_RC_GeneralError, N_("PTP General Error")},
{PTP_RC_SessionNotOpen, N_("PTP Session Not Open")},
{PTP_RC_InvalidTransactionID, N_("PTP Invalid Transaction ID")},
{PTP_RC_OperationNotSupported, N_("PTP Operation Not Supported")},
{PTP_RC_ParameterNotSupported, N_("PTP Parameter Not Supported")},
{PTP_RC_IncompleteTransfer, N_("PTP Incomplete Transfer")},
{PTP_RC_InvalidStorageId, N_("PTP Invalid Storage ID")},
{PTP_RC_InvalidObjectHandle, N_("PTP Invalid Object Handle")},
{PTP_RC_DevicePropNotSupported, N_("PTP Device Prop Not Supported")},
{PTP_RC_InvalidObjectFormatCode, N_("PTP Invalid Object Format Code")},
{PTP_RC_StoreFull, N_("PTP Store Full")},
{PTP_RC_ObjectWriteProtected, N_("PTP Object Write Protected")},
{PTP_RC_StoreReadOnly, N_("PTP Store Read Only")},
{PTP_RC_AccessDenied, N_("PTP Access Denied")},
{PTP_RC_NoThumbnailPresent, N_("PTP No Thumbnail Present")},
{PTP_RC_SelfTestFailed, N_("PTP Self Test Failed")},
{PTP_RC_PartialDeletion, N_("PTP Partial Deletion")},
{PTP_RC_StoreNotAvailable, N_("PTP Store Not Available")},
{PTP_RC_SpecificationByFormatUnsupported,
N_("PTP Specification By Format Unsupported")},
{PTP_RC_NoValidObjectInfo, N_("PTP No Valid Object Info")},
{PTP_RC_InvalidCodeFormat, N_("PTP Invalid Code Format")},
{PTP_RC_UnknownVendorCode, N_("PTP Unknown Vendor Code")},
{PTP_RC_CaptureAlreadyTerminated,
N_("PTP Capture Already Terminated")},
{PTP_RC_DeviceBusy, N_("PTP Device Bus")},
{PTP_RC_InvalidParentObject, N_("PTP Invalid Parent Object")},
{PTP_RC_InvalidDevicePropFormat, N_("PTP Invalid Device Prop Format")},
{PTP_RC_InvalidDevicePropValue, N_("PTP Invalid Device Prop Value")},
{PTP_RC_InvalidParameter, N_("PTP Invalid Parameter")},
{PTP_RC_SessionAlreadyOpened, N_("PTP Session Already Opened")},
{PTP_RC_TransactionCanceled, N_("PTP Transaction Canceled")},
{PTP_RC_SpecificationOfDestinationUnsupported,
N_("PTP Specification Of Destination Unsupported")},
{PTP_RC_EK_FilenameRequired, N_("PTP EK Filename Required")},
{PTP_RC_EK_FilenameConflicts, N_("PTP EK Filename Conflicts")},
{PTP_RC_EK_FilenameInvalid, N_("PTP EK Filename Invalid")},
{PTP_ERROR_IO, N_("PTP I/O error")},
{PTP_ERROR_BADPARAM, N_("PTP Error: bad parameter")},
{PTP_ERROR_DATA_EXPECTED, N_("PTP Protocol error, data expected")},
{PTP_ERROR_RESP_EXPECTED, N_("PTP Protocol error, response expected")},
{0, NULL}
};
static void
report_result (GPContext *context, short result)
{
unsigned int i;
for (i = 0; ptp_errors[i].txt; i++)
if (ptp_errors[i].n == result)
gp_context_error (context, dgettext(GETTEXT_PACKAGE, ptp_errors[i].txt));
}
static int
translate_ptp_result (short result)
{
switch (result) {
case PTP_RC_ParameterNotSupported:
return (GP_ERROR_BAD_PARAMETERS);
case PTP_ERROR_BADPARAM:
return (GP_ERROR_BAD_PARAMETERS);
case PTP_RC_OK:
return (GP_OK);
default:
return (GP_ERROR);
}
}
static short
translate_gp_result (int result)
{
switch (result) {
case GP_OK:
return (PTP_RC_OK);
case GP_ERROR:
return (PTP_RC_GeneralError);
default:
return (PTP_RC_GeneralError);
}
}
static struct {
const char *model;
unsigned short usb_vendor;
unsigned short usb_product;
} models[] = {
/*
* The very first PTP camera (with special firmware only), also
* called "PTP Prototype", may report non PTP interface class
*/
{"Kodak:DC240 (PTP mode)", 0x040a, 0x0121},
/*
* Old DC-4800 firmware reported custom interface class, so we have
* to detect it by product/vendor IDs
*/
{"Kodak:DC4800", 0x040a, 0x0160},
/* Below other camers known to be detected by interface class */
{"Kodak:DC3900", 0x040a, 0x0170},
{"Kodak:DX3215", 0x040a, 0x0525},
{"Kodak:DX3500", 0x040a, 0x0500},
{"Kodak:DX3600", 0x040a, 0x0510},
{"Kodak:DX3700", 0x040a, 0x0530},
{"Kodak:DX3900", 0x040a, 0x0170},
{"Kodak:DX4230", 0x040a, 0x0535},
{"Kodak:DX4300", 0x040a, 0x0566},
{"Kodak:DX4330", 0x040a, 0x0555},
{"Kodak:DX4900", 0x040a, 0x0550},
{"Kodak:MC3", 0x040a, 0x0400},
{"Kodak:LS420", 0x040a, 0x0540},
{"Kodak:LS443", 0x040a, 0x0568},
{"Kodak:CX4210", 0x040a, 0x0560},
{"Kodak:CX4230", 0x040a, 0x0535},
{"Kodak:CX4300", 0x040a, 0x0566},
/* HP PTP cameras */
{"HP:PhotoSmart 318 (PTP mode)", 0x03f0, 0x6302},
{"HP:PhotoSmart 612 (PTP mode)", 0x03f0, 0x6302},
{"HP:PhotoSmart 715 (PTP mode)", 0x03f0, 0x6402},
{"HP:PhotoSmart 850 (PTP mode)", 0x03f0, 0x4302},
/* I got information that all SONY PTP cameras use the same
product/vendor IDs */
{"Sony:DSC-P5 (PTP mode)", 0x054c, 0x004e},
{"Sony:DSC-F707V (PTP mode)", 0x054c, 0x004e},
{"Sony:DSC-P30 (PTP mode)", 0x054c, 0x004e},
{"Sony:DSC-P50 (PTP mode)", 0x054c, 0x004e},
{"Sony:DSC-S75 (PTP mode)", 0x054c, 0x004e},
{"Sony:DSC-S85 (PTP mode)", 0x054c, 0x004e},
{"Sony:MVC-CD300 (PTP mode)", 0x054c, 0x004e},
/* Nikon D100 has a PTP mode: westin 2002.10.16 */
{"Nikon:DSC D100 (PTP mode)", 0x04b0, 0x0402},
/* Nikon Coolpix 5700: A. Tanenbaum, 29 Oct 2002 */
{"Nikon:Coolpix 5700 (PTP mode)", 0x04b0, 0x010d},
/* Nikon Coolpix 885: S. Anderson, 19 nov 2002 */
{"Nikon:Coolpix 885 (PTP mode)", 0x04b0, 0x0112},
/* Nikon Coolpix 4300: Marco Rodriguez, 10 dic 2002 */
{"Nikon:Coolpix 4300 (PTP mode)", 0x04b0, 0x010f},
/* (at least some) newer Canon cameras can be switched between
* PTP and "normal" (i.e. Canon) mode
* Canon PS G3: A. Marinichev, 20 nov 2002
*/
{"Canon:PowerShot S45 (PTP mode)", 0x04a9, 0x306d},
/* 0x306c is S45 in normal (canon) mode */
{"Canon:PowerShot G3 (PTP mode)", 0x04a9, 0x306f},
/* 0x306e is G3 in normal (canon) mode */
{"Canon:PowerShot S230 (PTP mode)", 0x04a9, 0x3071},
/* 0x3070 is S230 in normal (canon) mode */
{"Canon:Digital IXUS v3 (PTP mode)", 0x04a9, 0x3071},
/* it's the same as S230 */
/* more coming soon :) */
{NULL, 0, 0}
};
static struct {
unsigned short format_code;
const char *txt;
} object_formats[] = {
{PTP_OFC_Undefined, "application/x-unknown"},
{PTP_OFC_Association, "application/x-association"},
{PTP_OFC_Script, "application/x-script"},
{PTP_OFC_Executable, "application/octet-stream"},
{PTP_OFC_Text, "text/plain"},
{PTP_OFC_HTML, "text/html"},
{PTP_OFC_DPOF, "text/plain"},
{PTP_OFC_AIFF, "audio/x-aiff"},
{PTP_OFC_WAV, GP_MIME_WAV},
{PTP_OFC_MP3, "audio/basic"},
{PTP_OFC_AVI, GP_MIME_AVI},
{PTP_OFC_MPEG, "video/mpeg"},
{PTP_OFC_ASF, "vide/x-asf"},
{PTP_OFC_EXIF_JPEG, GP_MIME_JPEG},
{PTP_OFC_TIFF_EP, "image/x-tiffep"},
{PTP_OFC_FlashPix, "image/x-flashpix"},
{PTP_OFC_BMP, GP_MIME_BMP},
{PTP_OFC_CIFF, "image/x-ciff"},
{PTP_OFC_Undefined_0x3806, "application/x-unknown"},
{PTP_OFC_GIF, "image/gif"},
{PTP_OFC_JFIF, GP_MIME_JPEG},
{PTP_OFC_PCD, "image/x-pcd"},
{PTP_OFC_PICT, "image/x-pict"},
{PTP_OFC_PNG, GP_MIME_PNG},
{PTP_OFC_Undefined_0x380C, "application/x-unknown"},
{PTP_OFC_TIFF, GP_MIME_TIFF},
{PTP_OFC_TIFF_IT, "image/x-tiffit"},
{PTP_OFC_JP2, "image/x-jpeg2000bff"},
{PTP_OFC_JPX, "image/x-jpeg2000eff"},
{0, NULL}
};
static int
set_mimetype (Camera *camera, CameraFile *file, uint16_t ofc)
{
int i;
for (i = 0; object_formats[i].format_code; i++)
if (object_formats[i].format_code == ofc)
{
CR (gp_file_set_mime_type (file, object_formats[i].txt));
return (GP_OK);
}
CR (gp_file_set_mime_type (file, "application/x-unknown"));
return (GP_OK);
}
static void
strcpy_mime(char * dest, uint16_t ofc) {
int i;
for (i = 0; object_formats[i].format_code; i++)
if (object_formats[i].format_code == ofc) {
strcpy(dest, object_formats[i].txt);
return;
}
strcpy(dest, "application/x-unknown");
}
static uint32_t
get_mimetype (Camera *camera, CameraFile *file)
{
int i;
const char *mimetype;
gp_file_get_mime_type (file, &mimetype);
for (i = 0; object_formats[i].format_code; i++)
if (!strcmp(mimetype,object_formats[i].txt))
return (object_formats[i].format_code);
return (PTP_OFC_Undefined);
}
struct _CameraPrivateLibrary {
PTPParams params;
};
struct _PTPData {
Camera *camera;
GPContext *context;
};
typedef struct _PTPData PTPData;
static short
ptp_read_func (unsigned char *bytes, unsigned int size, void *data)
{
Camera *camera = ((PTPData *)data)->camera;
int result;
/*
* gp_port_read returns (in case of success) the number of bytes
* read. libptp doesn't need that.
*/
result = gp_port_read (camera->port, bytes, size);
if (result==0) result = gp_port_read (camera->port, bytes, size);
if (result >= 0)
return (PTP_RC_OK);
else
return (translate_gp_result (result));
}
static short
ptp_write_func (unsigned char *bytes, unsigned int size, void *data)
{
Camera *camera = ((PTPData *)data)->camera;
int result;
/*
* gp_port_write returns (in case of success) the number of bytes
* write. libptp doesn't need that.
*/
result = gp_port_write (camera->port, bytes, size);
if (result >= 0)
return (PTP_RC_OK);
else
return (translate_gp_result (result));
}
static short
ptp_check_int (unsigned char *bytes, unsigned int size, void *data)
{
Camera *camera = ((PTPData *)data)->camera;
int result;
/*
* gp_port_check_int returns (in case of success) the number of bytes
* read. libptp doesn't need that.
*/
result = gp_port_check_int (camera->port, bytes, size);
if (result==0) result = gp_port_check_int (camera->port, bytes, size);
if (result >= 0)
return (PTP_RC_OK);
else
return (translate_gp_result (result));
}
static short
ptp_check_int_fast (unsigned char *bytes, unsigned int size, void *data)
{
Camera *camera = ((PTPData *)data)->camera;
int result;
/*
* gp_port_check_int returns (in case of success) the number of bytes
* read. libptp doesn't need that.
*/
result = gp_port_check_int_fast (camera->port, bytes, size);
if (result==0) result = gp_port_check_int_fast (camera->port, bytes, size);
if (result >= 0)
return (PTP_RC_OK);
else
return (translate_gp_result (result));
}
static void
ptp_debug_func (void *data, const char *format, va_list args)
{
gp_logv (GP_LOG_DEBUG, "ptp", format, args);
}
static void
ptp_error_func (void *data, const char *format, va_list args)
{
PTPData *ptp_data = data;
char buf[2048];
vsnprintf (buf, sizeof (buf), format, args);
gp_context_error (ptp_data->context, "%s", buf);
}
int
camera_abilities (CameraAbilitiesList *list)
{
int i;
CameraAbilities a;
memset(&a,0, sizeof(a));
for (i = 0; models[i].model; i++) {
strcpy (a.model, models[i].model);
a.status = GP_DRIVER_STATUS_TESTING;
a.port = GP_PORT_USB;
a.speed[0] = 0;
a.usb_vendor = models[i].usb_vendor;
a.usb_product= models[i].usb_product;
a.operations = GP_CAPTURE_IMAGE;
a.file_operations = GP_FILE_OPERATION_PREVIEW|
GP_FILE_OPERATION_DELETE;
a.folder_operations = GP_FOLDER_OPERATION_PUT_FILE
| GP_FOLDER_OPERATION_MAKE_DIR |
GP_FOLDER_OPERATION_REMOVE_DIR;
CR (gp_abilities_list_append (list, a));
memset(&a,0, sizeof(a));
}
strcpy(a.model, "USB PTP Class Camera");
a.status = GP_DRIVER_STATUS_TESTING;
a.port = GP_PORT_USB;
a.speed[0] = 0;
a.usb_class = 6;
a.usb_subclass = -1;
a.usb_protocol = -1;
a.operations = GP_OPERATION_NONE;
a.file_operations = GP_FILE_OPERATION_PREVIEW|
GP_FILE_OPERATION_DELETE;
a.folder_operations = GP_FOLDER_OPERATION_PUT_FILE
| GP_FOLDER_OPERATION_MAKE_DIR;
CR (gp_abilities_list_append (list, a));
return (GP_OK);
}
int
camera_id (CameraText *id)
{
strcpy (id->text, "PTP");
return (GP_OK);
}
static int
camera_exit (Camera *camera, GPContext *context)
{
GPPortSettings settings;
/* get port settings */
CR (gp_port_get_settings (camera->port, &settings));
if (camera->pl) {
#if 0
/* it won't hurt */
GP_DEBUG ("Clearing STALL condition on ep: 0x%x",
settings.usb.outep);
gp_port_usb_clear_halt(camera->port, settings.usb.outep);
#endif
/* close ptp session */
ptp_closesession (&camera->pl->params);
free (camera->pl);
camera->pl = NULL;
}
/* FIXME: free all camera->pl->params.objectinfo[] and
other malloced data */
return (GP_OK);
}
static int
camera_about (Camera *camera, CameraText *text, GPContext *context)
{
strncpy (text->text,
_("Written by Mariusz Woloszyn <emsi@ipartners.pl>. "
"Enjoy!"), sizeof (text->text));
return (GP_OK);
}
static int
camera_capture (Camera *camera, CameraCaptureType type, CameraFilePath *path,
GPContext *context)
{
PTPEvent event;
if (type != GP_CAPTURE_IMAGE) {
return GP_ERROR_NOT_SUPPORTED;
}
CPR(context,ptp_initiatecapture(&camera->pl->params, 0x00000000, 0x00000000));
while (ptp_event_wait (&camera->pl->params, &event)!=PTP_RC_OK);
if (event.EventCode==PTP_EC_ObjectAdded) {
while (ptp_event_wait (&camera->pl->params, &event)!=PTP_RC_OK);
if (event.EventCode==PTP_EC_CaptureComplete) {
return GP_OK;
}
}
/* we're not going to set path, ptp does not use paths anyway ;) */
return GP_ERROR;
}
/* following functions are used for fs testing only */
#if 0
static void
add_dir (Camera *camera, uint32_t parent, uint32_t handle, const char *foldername)
{
int n;
n=camera->pl->params.handles.n++;
camera->pl->params.objectinfo = (PTPObjectInfo*)
realloc(camera->pl->params.objectinfo,
sizeof(PTPObjectInfo)*(n+1));
camera->pl->params.handles.handler[n]=handle;
camera->pl->params.objectinfo[n].Filename=malloc(strlen(foldername)+1);
strcpy(camera->pl->params.objectinfo[n].Filename, foldername);
camera->pl->params.objectinfo[n].ObjectFormat=PTP_OFC_Association;
camera->pl->params.objectinfo[n].AssociationType=PTP_AT_GenericFolder;
camera->pl->params.objectinfo[n].ParentObject=parent;
}
#endif
#if 0
static void
move_object_by_handle (Camera *camera, uint32_t parent, uint32_t handle)
{
int n;
for (n=0; n<camera->pl->params.handles.n; n++)
if (camera->pl->params.handles.handler[n]==handle) break;
if (n==camera->pl->params.handles.n) return;
camera->pl->params.objectinfo[n].ParentObject=parent;
}
#endif
#if 0
static void
move_object_by_number (Camera *camera, uint32_t parent, int n)
{
if (n>=camera->pl->params.handles.n) return;
camera->pl->params.objectinfo[n].ParentObject=parent;
}
#endif
static inline int
handle_to_n (uint32_t handle, Camera *camera)
{
int i;
for (i = 0; i < camera->pl->params.handles.n; i++)
if (camera->pl->params.handles.handler[i]==handle) return i;
/* else not found */
return (PTP_HANDLER_SPECIAL);
}
static uint32_t
find_child (const char *file, uint32_t storage, uint32_t handle, Camera *camera)
{
int i;
PTPObjectInfo *oi = camera->pl->params.objectinfo;
for (i = 0; i < camera->pl->params.handles.n; i++) {
if ((oi[i].StorageID==storage) && (oi[i].ParentObject==handle))
if (!strcmp(oi[i].Filename,file))
return (camera->pl->params.handles.handler[i]);
}
/* else not found */
return (PTP_HANDLER_SPECIAL);
}
static uint32_t
folder_to_handle(const char *folder, uint32_t storage, uint32_t parent, Camera *camera)
{
char *c;
if (!strlen(folder)) return PTP_HANDLER_ROOT;
if (!strcmp(folder,"/")) return PTP_HANDLER_ROOT;
c=strchr(folder,'/');
if (c!=NULL) {
*c=0;
parent=find_child (folder, storage, parent, camera);
return folder_to_handle(c+1, storage, parent, camera);
} else {
return find_child (folder, storage, parent, camera);
}
}
static int
file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
void *data, GPContext *context)
{
PTPParams *params = &((Camera *)data)->pl->params;
uint32_t parent, storage;
int i;
/*((PTPData *)((Camera *)data)->pl->params.data)->context = context;*/
/* There should be NO files in root folder */
if (!strcmp(folder, "/")) {
return (GP_OK);
}
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* Get (parent) folder handle omiting storage pseudofolder */
find_folder_handle(folder,storage,parent,data);
for (i = 0; i < params->handles.n; i++) {
if (params->objectinfo[i].ParentObject==parent)
if (params->objectinfo[i].ObjectFormat != PTP_OFC_Association)
if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs)
|| (params->objectinfo[i].StorageID == storage))
CR (gp_list_append (list, params->objectinfo[i].Filename, NULL));
}
return (GP_OK);
}
static int
folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
void *data, GPContext *context)
{
PTPParams *params = &((Camera *)data)->pl->params;
int i;
/*((PTPData *)((Camera *)data)->pl->params.data)->context = context;*/
/* add storage pseudofolders in root folder */
if (!strcmp(folder, "/")) {
PTPStorageIDs storageids;
if (ptp_operation_issupported(params,PTP_OC_GetStorageIDs)) {
CPR (context, ptp_getstorageids(params,
&storageids));
} else {
storageids.n = 1;
storageids.storage[0]=0xdeadbeef;
}
for (i=0; i<storageids.n; i++) {
char fname[MAXFILENAMELEN];
PTPStorageInfo storageinfo;
if ((storageids.storage[i]&0x0000ffff)==0) continue;
if (ptp_operation_issupported(params,PTP_OC_GetStorageIDs)) {
CPR (context, ptp_getstorageinfo(params,
storageids.storage[i], &storageinfo));
}
/* let it be 8 chars and \0, no more :) */
snprintf(fname, strlen(STORAGE_FOLDER_PREFIX)+9, STORAGE_FOLDER_PREFIX"%08x",
storageids.storage[i]);
CR (gp_list_append (list, fname, NULL));
}
return (GP_OK);
}
{
uint32_t handler,storage;
/* compute storage ID value from folder path */
folder_to_storage(folder,storage);
/* Get folder handle omiting storage pseudofolder */
find_folder_handle(folder,storage,handler,data);
for (i = 0; i < params->handles.n; i++) {
if (params->objectinfo[i].ParentObject==handler)
if ((!ptp_operation_issupported(params,PTP_OC_GetStorageIDs)) ||
(params->objectinfo[i].StorageID == storage))
if (params->objectinfo[i].ObjectFormat==PTP_OFC_Association &&
params->objectinfo[i].AssociationType!=PTP_AT_Undefined)
CR (gp_list_append (list, params->objectinfo[i].Filename, NULL));
}
}
return (GP_OK);
}
static int
get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
CameraFileType type, CameraFile *file, void *data,
GPContext *context)
{
Camera *camera = data;
PTPReq *fdata = NULL;
char * image;
uint32_t object_id;
uint32_t size;
uint32_t storage;
PTPObjectInfo * oi;
((PTPData *) camera->pl->params.data)->context = context;
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* Get file number omiting storage pseudofolder */
find_folder_handle(folder, storage, object_id, data);
object_id = find_child(filename, storage, object_id, camera);
if ((object_id=handle_to_n(object_id, camera))==PTP_HANDLER_SPECIAL)
return (GP_ERROR_BAD_PARAMETERS);
oi=&camera->pl->params.objectinfo[object_id];
switch (type) {
case GP_FILE_TYPE_NORMAL:
if ((oi->ObjectFormat == PTP_OFC_Undefined) ||
(oi->ObjectFormat == PTP_OFC_Association))
return (GP_ERROR_NOT_SUPPORTED);
size=oi->ObjectCompressedSize;
fdata=malloc(size+PTP_REQ_HDR_LEN);
CPR_free (context, ptp_getobject(&camera->pl->params,
camera->pl->params.handles.handler[object_id],
size, fdata), fdata);
image=malloc(size);
memcpy(image, fdata->data, size);
free(fdata);
CR (gp_file_set_data_and_size (file, image, size));
/* XXX does gp_file_set_data_and_size free() image ptr upon
failure?? */
break;
case GP_FILE_TYPE_PREVIEW:
/* Don't allow to get thumb of nonimage objects! */
if ((oi->ObjectFormat & 0x0800) == 0) return (GP_ERROR_NOT_SUPPORTED);
/* if no thumb, for some reason */
if((size=oi->ThumbCompressedSize)==0) return (GP_ERROR_NOT_SUPPORTED);
fdata=malloc(size+PTP_REQ_HDR_LEN);
CPR_free (context, ptp_getthumb(&camera->pl->params,
camera->pl->params.handles.handler[object_id],
size, fdata), fdata);
image=malloc(size);
memcpy(image, fdata->data, size);
free(fdata);
CR (gp_file_set_data_and_size (file, image, size));
/* XXX does gp_file_set_data_and_size free() image ptr upon
failure?? */
break;
default:
return (GP_ERROR_NOT_SUPPORTED);
}
CR (set_mimetype (camera, file, oi->ObjectFormat));
return (GP_OK);
}
static int
put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file,
void *data, GPContext *context)
{
Camera *camera = data;
PTPObjectInfo oi;
const char *filename, *object;
uint32_t parent;
uint32_t storage;
uint32_t handle;
long int intsize;
uint32_t size;
PTPReq *fdata;
((PTPData *) camera->pl->params.data)->context = context;
memset(&oi, 0, sizeof (PTPObjectInfo));
gp_file_get_name (file, &filename);
gp_file_get_data_and_size (file, &object, &intsize);
size=(uint32_t)intsize;
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* get parent folder id omiting storage pseudofolder */
find_folder_handle(folder,storage,parent,data);
/* if you desire to put file to root folder, you have to use
* 0xffffffff instead of 0x00000000 (which means responder decide).
*/
if (parent==PTP_HANDLER_ROOT) parent=PTP_HANDLER_SPECIAL;
oi.Filename=(char *)filename;
oi.ObjectFormat=get_mimetype(camera, file);
oi.ObjectCompressedSize=size;
gp_file_get_mtime(file, &oi.ModificationDate);
CPR (context, ptp_ek_sendfileobjectinfo (&camera->pl->params,
&storage, &parent, &handle, &oi));
fdata=malloc(size+PTP_REQ_HDR_LEN);
memcpy(fdata->data, object, size);
CPR (context, ptp_ek_sendfileobject (&camera->pl->params,
fdata, size));
free(fdata);
return (GP_OK);
}
static int
delete_file_func (CameraFilesystem *fs, const char *folder,
const char *filename, void *data, GPContext *context)
{
Camera *camera = data;
unsigned long object_id;
uint32_t storage;
((PTPData *) camera->pl->params.data)->context = context;
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* Get file number omiting storage pseudofolder */
find_folder_handle(folder, storage, object_id, data);
object_id = find_child(filename, storage, object_id, camera);
if ((object_id=handle_to_n(object_id, camera))==PTP_HANDLER_SPECIAL)
return (GP_ERROR_BAD_PARAMETERS);
CPR (context, ptp_deleteobject(&camera->pl->params,
camera->pl->params.handles.handler[object_id],0));
return (GP_OK);
}
static int
remove_dir_func (CameraFilesystem *fs, const char *folder,
const char *foldername, void *data, GPContext *context)
{
Camera *camera = data;
unsigned long object_id;
uint32_t storage;
((PTPData *) camera->pl->params.data)->context = context;
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* Get file number omiting storage pseudofolder */
find_folder_handle(folder, storage, object_id, data);
object_id = find_child(foldername, storage, object_id, camera);
if ((object_id=handle_to_n(object_id, camera))==PTP_HANDLER_SPECIAL)
return (GP_ERROR_BAD_PARAMETERS);
CPR (context, ptp_deleteobject(&camera->pl->params,
camera->pl->params.handles.handler[object_id],0));
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;
PTPObjectInfo *oi;
uint32_t object_id;
uint32_t storage;
((PTPData *) camera->pl->params.data)->context = context;
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* Get file number omiting storage pseudofolder */
find_folder_handle(folder, storage, object_id, data);
object_id = find_child(filename, storage, object_id, camera);
if ((object_id=handle_to_n(object_id, camera))==PTP_HANDLER_SPECIAL)
return (GP_ERROR_BAD_PARAMETERS);
oi=&camera->pl->params.objectinfo[object_id];
info->file.fields = GP_FILE_INFO_SIZE|GP_FILE_INFO_TYPE|GP_FILE_INFO_MTIME;
info->file.size = oi->ObjectCompressedSize;
strcpy_mime (info->file.type, oi->ObjectFormat);
info->file.mtime = oi->ModificationDate;
/* if object is an image */
if ((oi->ObjectFormat & 0x0800) != 0) {
info->preview.fields = GP_FILE_INFO_SIZE|GP_FILE_INFO_WIDTH
|GP_FILE_INFO_HEIGHT|GP_FILE_INFO_TYPE;
strcpy_mime(info->preview.type, oi->ThumbFormat);
info->preview.size = oi->ThumbCompressedSize;
info->preview.width = oi->ThumbPixWidth;
info->preview.height = oi->ThumbPixHeight;
info->file.fields = info->file.fields |
GP_FILE_INFO_WIDTH |
GP_FILE_INFO_HEIGHT;
info->file.width = oi->ImagePixWidth;
info->file.height = oi->ImagePixHeight;
}
return (GP_OK);
}
static int
make_dir_func (CameraFilesystem *fs, const char *folder, const char *foldername,
void *data, GPContext *context)
{
Camera *camera = data;
PTPObjectInfo oi;
uint32_t parent;
uint32_t storage;
uint32_t handle;
((PTPData *) camera->pl->params.data)->context = context;
memset(&oi, 0, sizeof (PTPObjectInfo));
/* compute storage ID value from folder patch */
folder_to_storage(folder,storage);
/* get parent folder id omiting storage pseudofolder */
find_folder_handle(folder,storage,parent,data);
/* if you desire to make dir in 'root' folder, you have to use
* 0xffffffff instead of 0x00000000 (which means responder decide).
*/
if (parent==PTP_HANDLER_ROOT) parent=PTP_HANDLER_SPECIAL;
oi.Filename=(char *)foldername;
oi.ObjectFormat=PTP_OFC_Association;
oi.ProtectionStatus=PTP_PS_NoProtection;
oi.AssociationType=PTP_AT_GenericFolder;
CPR (context, ptp_ek_sendfileobjectinfo (&camera->pl->params,
&storage, &parent, &handle, &oi));
/* to create folder on kodak camera you don't have to sendfileobject */
#if 0
CPR (context, ptp_ek_sendfileobject (&camera->pl->params,
&oi, 18));
#endif
return (GP_OK);
}
static int
init_ptp_fs (Camera *camera, GPContext *context)
{
int i,id;
((PTPData *) camera->pl->params.data)->context = context;
/* Get file handles array for filesystem */
id = gp_context_progress_start (context, 0, "Initializing Camera");
/* be paranoid!!! */
memset (&camera->pl->params.handles, 0, sizeof(PTPObjectHandles));
/* get objecthandles of all objects from all stores */
CPR (context, ptp_getobjecthandles
(&camera->pl->params, 0xffffffff, 0x000000, 0x000000,
&camera->pl->params.handles));
gp_context_progress_update (context, id, 10);
/* wee need that for fileststem */
camera->pl->params.objectinfo =
(PTPObjectInfo*)malloc(sizeof(PTPObjectInfo)*
camera->pl->params.handles.n);
memset (camera->pl->params.objectinfo,0,sizeof(PTPObjectInfo)
*camera->pl->params.handles.n);
for (i = 0; i < camera->pl->params.handles.n; i++) {
CPR (context, ptp_getobjectinfo(&camera->pl->params,
camera->pl->params.handles.handler[i],
&camera->pl->params.objectinfo[i]));
#if 1
{
PTPObjectInfo *oi;
oi=&camera->pl->params.objectinfo[i];
GP_DEBUG ("ObjectInfo for '%s':", oi->Filename);
GP_DEBUG (" Object ID: 0x%.4x", camera->pl->params.handles.handler[i]);
GP_DEBUG (" StorageID: 0x%.4x", oi->StorageID);
GP_DEBUG (" ObjectFormat: 0x%.4x", oi->ObjectFormat);
GP_DEBUG (" ObjectCompressedSize: %d", oi->ObjectCompressedSize);
GP_DEBUG (" ThumbFormat: 0x%.4x", oi->ThumbFormat);
GP_DEBUG (" ThumbCompressedSize: %d", oi->ThumbCompressedSize);
GP_DEBUG (" ThumbPixWidth: %d", oi->ThumbPixWidth);
GP_DEBUG (" ThumbPixHeight: %d", oi->ThumbPixHeight);
GP_DEBUG (" ImagePixWidth: %d", oi->ImagePixWidth);
GP_DEBUG (" ImagePixHeight: %d", oi->ImagePixHeight);
GP_DEBUG (" ImageBitDepth: %d", oi->ImageBitDepth);
GP_DEBUG (" ParentObject: 0x%.4x", oi->ParentObject);
GP_DEBUG (" AssociationType: 0x%.4x", oi->AssociationType);
GP_DEBUG (" AssociationDesc: 0x%.4x", oi->AssociationDesc);
GP_DEBUG (" SequenceNumber: 0x%.4x", oi->SequenceNumber);
}
#endif
gp_context_progress_update (context, id,
(90*i)/camera->pl->params.handles.n);
}
gp_context_progress_stop (context, id);
/*
add_dir (camera, 0x00000000, 0xff000000, "DIR1");
add_dir (camera, 0x00000000, 0xff000001, "DIR20");
add_dir (camera, 0xff000000, 0xff000002, "subDIR1");
add_dir (camera, 0xff000002, 0xff000003, "subsubDIR1");
move_object_by_number (camera, 0xff000002, 2);
move_object_by_number (camera, 0xff000001, 3);
move_object_by_number (camera, 0xff000002, 4);
// Used for testing with my camera, which does not support subdirs
*/
return (GP_OK);
}
int
camera_init (Camera *camera, GPContext *context)
{
GPPortSettings settings;
short ret,i;
/* Make sure our port is a USB port */
if (camera->port->type != GP_PORT_USB) {
gp_context_error (context, _("PTP is implemented for "
"USB cameras only."));
return (GP_ERROR_UNKNOWN_PORT);
}
camera->functions->about = camera_about;
camera->functions->exit = camera_exit;
camera->functions->capture = camera_capture;
/* We need some data that we pass around */
camera->pl = malloc (sizeof (CameraPrivateLibrary));
if (!camera->pl)
return (GP_ERROR_NO_MEMORY);
camera->pl->params.write_func = ptp_write_func;
camera->pl->params.read_func = ptp_read_func;
camera->pl->params.check_int_func = ptp_check_int;
camera->pl->params.check_int_fast_func = ptp_check_int_fast;
camera->pl->params.debug_func = ptp_debug_func;
camera->pl->params.error_func = ptp_error_func;
camera->pl->params.data = malloc (sizeof (PTPData));
memset (camera->pl->params.data, 0, sizeof (PTPData));
((PTPData *) camera->pl->params.data)->camera = camera;
camera->pl->params.byteorder = PTP_DL_LE;
/* the transaction_id is incremented before sending,
thus 0xffffffff means 0 ;) */
camera->pl->params.transaction_id=0xffffffff;
/* On large fiels (over 50M) deletion takes over 3 seconds,
* waiting for event after capture may take some time also
*/
CR (gp_port_set_timeout (camera->port, 8000));
/* Configure the port */
CR (gp_port_get_settings (camera->port, &settings));
/* Use the defaults the core parsed */
CR (gp_port_set_settings (camera->port, settings));
/* Establish a connection to the camera */
((PTPData *) camera->pl->params.data)->context = context;
ret=ptp_opensession (&camera->pl->params, 1);
while (ret==PTP_RC_InvalidTransactionID) {
camera->pl->params.transaction_id+=10;
ret=ptp_opensession (&camera->pl->params, 1);
}
if (ret!=PTP_RC_SessionAlreadyOpened && ret!=PTP_RC_OK) {
report_result(context, ret);
return (translate_ptp_result(ret));
}
/* Seems HP does not like getdevinfo outside of session */
/* get device info */
CPR(context, ptp_getdeviceinfo(&camera->pl->params,
&camera->pl->params.deviceinfo));
GP_DEBUG ("Device info:");
GP_DEBUG ("Manufacturer: %s",camera->pl->params.deviceinfo.Manufacturer);
GP_DEBUG (" model: %s", camera->pl->params.deviceinfo.Model);
GP_DEBUG (" device version: %s", camera->pl->params.deviceinfo.DeviceVersion);
GP_DEBUG ("Vendor extension description: %s",camera->pl->params.deviceinfo.VendorExtensionDesc);
GP_DEBUG ("Supported operations:");
for (i=0; i<camera->pl->params.deviceinfo.OperationsSupported_len; i++) {
GP_DEBUG (" 0x%.4x",camera->pl->params.deviceinfo.OperationsSupported[i]);
}
/* init internal ptp objectfiles (required for fs implementation) */
init_ptp_fs (camera, context);
/* Configure the CameraFilesystem */
CR (gp_filesystem_set_list_funcs (camera->fs, file_list_func,
folder_list_func, camera));
CR (gp_filesystem_set_info_funcs (camera->fs, get_info_func, NULL,
camera));
CR (gp_filesystem_set_file_funcs (camera->fs, get_file_func,
delete_file_func, camera));
CR (gp_filesystem_set_folder_funcs (camera->fs, put_file_func,
NULL, make_dir_func,
remove_dir_func, camera));
return (GP_OK);
}