blob: b91f2465c7c8f645e7dde05f4e6b8c98b4d8c123 [file] [log] [blame]
/**
* \file libmtp.c
*
* Copyright (C) 2005-2011 Linus Walleij <triad@df.lth.se>
* Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
* Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
* Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
*
* 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.
*
* This file provides an interface "glue" to the underlying
* PTP implementation from libgphoto2. It uses some local
* code to convert from/to UTF-8 (stored in unicode.c/.h)
* and some small utility functions, mainly for debugging
* (stored in util.c/.h).
*
* The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
* plain copied from the libhphoto2 codebase.
*
* The files libusb-glue.c/.h are just what they say: an
* interface to libusb for the actual, physical USB traffic.
*/
#include "config.h"
#include "libmtp.h"
#include "unicode.h"
#include "ptp.h"
#include "libusb-glue.h"
#include "device-flags.h"
#if 0
#include "playlist-spl.h"
#endif
#include "util.h"
#include "mtpz.h"
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#ifdef _MSC_VER // For MSVC++
#define USE_WINDOWS_IO_H
#include <io.h>
#endif
/**
* Global debug level
* We use a flag system to enable a part of logs.
*
* The LIBMTP_DEBUG environment variable sets the debug flags for any binary
* that uses libmtp and calls LIBMTP_Init. The value can be given in decimal
* (must not start with "0" or it will be interpreted in octal), or in
* hexadecimal (must start with "0x").
*
* The value "-1" enables all debug flags.
*
* Some of the utilities in examples/ also take a command-line flag "-d" that
* enables LIBMTP_DEBUG_PTP and LIBMTP_DEBUG_DATA (same as setting
* LIBMTP_DEBUG=9).
*
* Flags (combine by adding the hex values):
* 0x00 [0000 0000] : LIBMTP_DEBUG_NONE : no debug (default)
* 0x01 [0000 0001] : LIBMTP_DEBUG_PTP : PTP debug
* 0x02 [0000 0010] : LIBMTP_DEBUG_PLST : Playlist debug
* 0x04 [0000 0100] : LIBMTP_DEBUG_USB : USB debug
* 0x08 [0000 1000] : LIBMTP_DEBUG_DATA : USB data debug
*
* (Please keep this list in sync with libmtp.h.)
*/
int LIBMTP_debug = LIBMTP_DEBUG_NONE;
/*
* This is a mapping between libmtp internal MTP filetypes and
* the libgphoto2/PTP equivalent defines. We need this because
* otherwise the libmtp.h device has to be dependent on ptp.h
* to be installed too, and we don't want that.
*/
//typedef struct filemap_struct filemap_t;
typedef struct filemap_struct {
char *description; /**< Text description for the file type */
LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
uint16_t ptp_id; /**< PTP ID for the filetype */
struct filemap_struct *next;
} filemap_t;
/*
* This is a mapping between libmtp internal MTP properties and
* the libgphoto2/PTP equivalent defines. We need this because
* otherwise the libmtp.h device has to be dependent on ptp.h
* to be installed too, and we don't want that.
*/
typedef struct propertymap_struct {
char *description; /**< Text description for the property */
LIBMTP_property_t id; /**< LIBMTP internal type for the property */
uint16_t ptp_id; /**< PTP ID for the property */
struct propertymap_struct *next;
} propertymap_t;
// Global variables
// This holds the global filetype mapping table
static filemap_t *filemap = NULL;
// This holds the global property mapping table
static propertymap_t *propertymap = NULL;
/*
* Forward declarations of local (static) functions.
*/
static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
uint16_t const ptp_id);
static void init_filemap();
static int register_property(char const * const description, LIBMTP_property_t const id,
uint16_t const ptp_id);
static void init_propertymap();
static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
LIBMTP_error_number_t errornumber,
char const * const error_text);
static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
uint16_t ptp_error,
char const * const error_text);
static void flush_handles(LIBMTP_mtpdevice_t *device);
static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
PTPParams *params,
uint32_t storageid,
uint32_t parent);
static void free_storage_list(LIBMTP_mtpdevice_t *device);
static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fitsize);
static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
LIBMTP_devicestorage_t *storage,
uint64_t *freespace);
static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
LIBMTP_devicestorage_t *storage,
uint64_t const filesize);
static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty);
static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t intype);
static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
char **unicstring, uint16_t property);
static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd);
static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd);
static char *get_iso8601_stamp(void);
static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id);
static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
uint16_t const attribute_id, uint64_t const value_default);
static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
uint16_t const attribute_id, uint32_t const value_default);
static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint16_t const value_default);
static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint8_t const value_default);
static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, char const * const string);
static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint32_t const value);
static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint16_t const value);
static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint8_t const value);
static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
LIBMTP_track_t *track);
static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent);
static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
char const * const name,
char const * const artist,
char const * const composer,
char const * const genre,
uint32_t const parenthandle,
uint32_t const storageid,
uint16_t const objectformat,
char const * const suffix,
uint32_t * const newid,
uint32_t const * const tracks,
uint32_t const no_tracks);
static int update_abstract_list(LIBMTP_mtpdevice_t *device,
char const * const name,
char const * const artist,
char const * const composer,
char const * const genre,
uint32_t const objecthandle,
uint16_t const objectformat,
uint32_t const * const tracks,
uint32_t const no_tracks);
static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata);
static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
static int set_object_filename(LIBMTP_mtpdevice_t *device,
uint32_t object_id,
uint16_t ptp_type,
const char **newname);
static char *generate_unique_filename(PTPParams* params, char const * const filename);
static int check_filename_exists(PTPParams* params, char const * const filename);
/**
* These are to wrap the get/put handlers to convert from the MTP types to PTP types
* in a reliable way
*/
typedef struct _MTPDataHandler {
MTPDataGetFunc getfunc;
MTPDataPutFunc putfunc;
void *priv;
} MTPDataHandler;
static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen);
static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen);
/**
* Checks if a filename ends with ".ogg". Used in various
* situations when the device has no idea that it support
* OGG but still does.
*
* @param name string to be checked.
* @return 0 if this does not end with ogg, any other
* value means it does.
*/
static int has_ogg_extension(char *name) {
char *ptype;
if (name == NULL)
return 0;
ptype = strrchr(name,'.');
if (ptype == NULL)
return 0;
if (!strcasecmp (ptype, ".ogg"))
return 1;
return 0;
}
/**
* Checks if a filename ends with ".flac". Used in various
* situations when the device has no idea that it support
* FLAC but still does.
*
* @param name string to be checked.
* @return 0 if this does not end with flac, any other
* value means it does.
*/
static int has_flac_extension(char *name) {
char *ptype;
if (name == NULL)
return 0;
ptype = strrchr(name,'.');
if (ptype == NULL)
return 0;
if (!strcasecmp (ptype, ".flac"))
return 1;
return 0;
}
/**
* Create a new file mapping entry
* @return a newly allocated filemapping entry.
*/
static filemap_t *new_filemap_entry()
{
filemap_t *filemap;
filemap = (filemap_t *)malloc(sizeof(filemap_t));
if( filemap != NULL ) {
filemap->description = NULL;
filemap->id = LIBMTP_FILETYPE_UNKNOWN;
filemap->ptp_id = PTP_OFC_Undefined;
filemap->next = NULL;
}
return filemap;
}
/**
* Register an MTP or PTP filetype for data retrieval
*
* @param description Text description of filetype
* @param id libmtp internal filetype id
* @param ptp_id PTP filetype id
* @return 0 for success any other value means error.
*/
static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
uint16_t const ptp_id)
{
filemap_t *new = NULL, *current;
// Has this LIBMTP filetype been registered before ?
current = filemap;
while (current != NULL) {
if(current->id == id) {
break;
}
current = current->next;
}
// Create the entry
if(current == NULL) {
new = new_filemap_entry();
if(new == NULL) {
return 1;
}
new->id = id;
if(description != NULL) {
new->description = strdup(description);
}
new->ptp_id = ptp_id;
// Add the entry to the list
if(filemap == NULL) {
filemap = new;
} else {
current = filemap;
while (current->next != NULL ) current=current->next;
current->next = new;
}
// Update the existing entry
} else {
if (current->description != NULL) {
free(current->description);
}
current->description = NULL;
if(description != NULL) {
current->description = strdup(description);
}
current->ptp_id = ptp_id;
}
return 0;
}
static void init_filemap()
{
register_filetype("Folder", LIBMTP_FILETYPE_FOLDER, PTP_OFC_Association);
register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
register_filetype("MPEG-4 Part 14 Container Format (Audio Emphasis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Emphasis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
register_filetype("Abstract Album file", LIBMTP_FILETYPE_ALBUM, PTP_OFC_MTP_AbstractAudioAlbum);
register_filetype("Abstract Playlist file", LIBMTP_FILETYPE_PLAYLIST, PTP_OFC_MTP_AbstractAudioVideoPlaylist);
register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
}
/**
* Returns the PTP filetype that maps to a certain libmtp internal file type.
* @param intype the MTP library interface type
* @return the PTP (libgphoto2) interface type
*/
static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
{
filemap_t *current;
current = filemap;
while (current != NULL) {
if(current->id == intype) {
return current->ptp_id;
}
current = current->next;
}
// printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
return PTP_OFC_Undefined;
}
/**
* Returns the MTP internal interface type that maps to a certain ptp
* interface type.
* @param intype the PTP (libgphoto2) interface type
* @return the MTP library interface type
*/
static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
{
filemap_t *current;
current = filemap;
while (current != NULL) {
if(current->ptp_id == intype) {
return current->id;
}
current = current->next;
}
// printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
return LIBMTP_FILETYPE_UNKNOWN;
}
/**
* Create a new property mapping entry
* @return a newly allocated propertymapping entry.
*/
static propertymap_t *new_propertymap_entry()
{
propertymap_t *propertymap;
propertymap = (propertymap_t *)malloc(sizeof(propertymap_t));
if( propertymap != NULL ) {
propertymap->description = NULL;
propertymap->id = LIBMTP_PROPERTY_UNKNOWN;
propertymap->ptp_id = 0;
propertymap->next = NULL;
}
return propertymap;
}
/**
* Register an MTP or PTP property for data retrieval
*
* @param description Text description of property
* @param id libmtp internal property id
* @param ptp_id PTP property id
* @return 0 for success any other value means error.
*/
static int register_property(char const * const description, LIBMTP_property_t const id,
uint16_t const ptp_id)
{
propertymap_t *new = NULL, *current;
// Has this LIBMTP propety been registered before ?
current = propertymap;
while (current != NULL) {
if(current->id == id) {
break;
}
current = current->next;
}
// Create the entry
if(current == NULL) {
new = new_propertymap_entry();
if(new == NULL) {
return 1;
}
new->id = id;
if(description != NULL) {
new->description = strdup(description);
}
new->ptp_id = ptp_id;
// Add the entry to the list
if(propertymap == NULL) {
propertymap = new;
} else {
current = propertymap;
while (current->next != NULL ) current=current->next;
current->next = new;
}
// Update the existing entry
} else {
if (current->description != NULL) {
free(current->description);
}
current->description = NULL;
if(description != NULL) {
current->description = strdup(description);
}
current->ptp_id = ptp_id;
}
return 0;
}
static void init_propertymap()
{
register_property("Storage ID", LIBMTP_PROPERTY_StorageID, PTP_OPC_StorageID);
register_property("Object Format", LIBMTP_PROPERTY_ObjectFormat, PTP_OPC_ObjectFormat);
register_property("Protection Status", LIBMTP_PROPERTY_ProtectionStatus, PTP_OPC_ProtectionStatus);
register_property("Object Size", LIBMTP_PROPERTY_ObjectSize, PTP_OPC_ObjectSize);
register_property("Association Type", LIBMTP_PROPERTY_AssociationType, PTP_OPC_AssociationType);
register_property("Association Desc", LIBMTP_PROPERTY_AssociationDesc, PTP_OPC_AssociationDesc);
register_property("Object File Name", LIBMTP_PROPERTY_ObjectFileName, PTP_OPC_ObjectFileName);
register_property("Date Created", LIBMTP_PROPERTY_DateCreated, PTP_OPC_DateCreated);
register_property("Date Modified", LIBMTP_PROPERTY_DateModified, PTP_OPC_DateModified);
register_property("Keywords", LIBMTP_PROPERTY_Keywords, PTP_OPC_Keywords);
register_property("Parent Object", LIBMTP_PROPERTY_ParentObject, PTP_OPC_ParentObject);
register_property("Allowed Folder Contents", LIBMTP_PROPERTY_AllowedFolderContents, PTP_OPC_AllowedFolderContents);
register_property("Hidden", LIBMTP_PROPERTY_Hidden, PTP_OPC_Hidden);
register_property("System Object", LIBMTP_PROPERTY_SystemObject, PTP_OPC_SystemObject);
register_property("Persistant Unique Object Identifier", LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier, PTP_OPC_PersistantUniqueObjectIdentifier);
register_property("Sync ID", LIBMTP_PROPERTY_SyncID, PTP_OPC_SyncID);
register_property("Property Bag", LIBMTP_PROPERTY_PropertyBag, PTP_OPC_PropertyBag);
register_property("Name", LIBMTP_PROPERTY_Name, PTP_OPC_Name);
register_property("Created By", LIBMTP_PROPERTY_CreatedBy, PTP_OPC_CreatedBy);
register_property("Artist", LIBMTP_PROPERTY_Artist, PTP_OPC_Artist);
register_property("Date Authored", LIBMTP_PROPERTY_DateAuthored, PTP_OPC_DateAuthored);
register_property("Description", LIBMTP_PROPERTY_Description, PTP_OPC_Description);
register_property("URL Reference", LIBMTP_PROPERTY_URLReference, PTP_OPC_URLReference);
register_property("Language Locale", LIBMTP_PROPERTY_LanguageLocale, PTP_OPC_LanguageLocale);
register_property("Copyright Information", LIBMTP_PROPERTY_CopyrightInformation, PTP_OPC_CopyrightInformation);
register_property("Source", LIBMTP_PROPERTY_Source, PTP_OPC_Source);
register_property("Origin Location", LIBMTP_PROPERTY_OriginLocation, PTP_OPC_OriginLocation);
register_property("Date Added", LIBMTP_PROPERTY_DateAdded, PTP_OPC_DateAdded);
register_property("Non Consumable", LIBMTP_PROPERTY_NonConsumable, PTP_OPC_NonConsumable);
register_property("Corrupt Or Unplayable", LIBMTP_PROPERTY_CorruptOrUnplayable, PTP_OPC_CorruptOrUnplayable);
register_property("Producer Serial Number", LIBMTP_PROPERTY_ProducerSerialNumber, PTP_OPC_ProducerSerialNumber);
register_property("Representative Sample Format", LIBMTP_PROPERTY_RepresentativeSampleFormat, PTP_OPC_RepresentativeSampleFormat);
register_property("Representative Sample Sise", LIBMTP_PROPERTY_RepresentativeSampleSize, PTP_OPC_RepresentativeSampleSize);
register_property("Representative Sample Height", LIBMTP_PROPERTY_RepresentativeSampleHeight, PTP_OPC_RepresentativeSampleHeight);
register_property("Representative Sample Width", LIBMTP_PROPERTY_RepresentativeSampleWidth, PTP_OPC_RepresentativeSampleWidth);
register_property("Representative Sample Duration", LIBMTP_PROPERTY_RepresentativeSampleDuration, PTP_OPC_RepresentativeSampleDuration);
register_property("Representative Sample Data", LIBMTP_PROPERTY_RepresentativeSampleData, PTP_OPC_RepresentativeSampleData);
register_property("Width", LIBMTP_PROPERTY_Width, PTP_OPC_Width);
register_property("Height", LIBMTP_PROPERTY_Height, PTP_OPC_Height);
register_property("Duration", LIBMTP_PROPERTY_Duration, PTP_OPC_Duration);
register_property("Rating", LIBMTP_PROPERTY_Rating, PTP_OPC_Rating);
register_property("Track", LIBMTP_PROPERTY_Track, PTP_OPC_Track);
register_property("Genre", LIBMTP_PROPERTY_Genre, PTP_OPC_Genre);
register_property("Credits", LIBMTP_PROPERTY_Credits, PTP_OPC_Credits);
register_property("Lyrics", LIBMTP_PROPERTY_Lyrics, PTP_OPC_Lyrics);
register_property("Subscription Content ID", LIBMTP_PROPERTY_SubscriptionContentID, PTP_OPC_SubscriptionContentID);
register_property("Produced By", LIBMTP_PROPERTY_ProducedBy, PTP_OPC_ProducedBy);
register_property("Use Count", LIBMTP_PROPERTY_UseCount, PTP_OPC_UseCount);
register_property("Skip Count", LIBMTP_PROPERTY_SkipCount, PTP_OPC_SkipCount);
register_property("Last Accessed", LIBMTP_PROPERTY_LastAccessed, PTP_OPC_LastAccessed);
register_property("Parental Rating", LIBMTP_PROPERTY_ParentalRating, PTP_OPC_ParentalRating);
register_property("Meta Genre", LIBMTP_PROPERTY_MetaGenre, PTP_OPC_MetaGenre);
register_property("Composer", LIBMTP_PROPERTY_Composer, PTP_OPC_Composer);
register_property("Effective Rating", LIBMTP_PROPERTY_EffectiveRating, PTP_OPC_EffectiveRating);
register_property("Subtitle", LIBMTP_PROPERTY_Subtitle, PTP_OPC_Subtitle);
register_property("Original Release Date", LIBMTP_PROPERTY_OriginalReleaseDate, PTP_OPC_OriginalReleaseDate);
register_property("Album Name", LIBMTP_PROPERTY_AlbumName, PTP_OPC_AlbumName);
register_property("Album Artist", LIBMTP_PROPERTY_AlbumArtist, PTP_OPC_AlbumArtist);
register_property("Mood", LIBMTP_PROPERTY_Mood, PTP_OPC_Mood);
register_property("DRM Status", LIBMTP_PROPERTY_DRMStatus, PTP_OPC_DRMStatus);
register_property("Sub Description", LIBMTP_PROPERTY_SubDescription, PTP_OPC_SubDescription);
register_property("Is Cropped", LIBMTP_PROPERTY_IsCropped, PTP_OPC_IsCropped);
register_property("Is Color Corrected", LIBMTP_PROPERTY_IsColorCorrected, PTP_OPC_IsColorCorrected);
register_property("Image Bit Depth", LIBMTP_PROPERTY_ImageBitDepth, PTP_OPC_ImageBitDepth);
register_property("f Number", LIBMTP_PROPERTY_Fnumber, PTP_OPC_Fnumber);
register_property("Exposure Time", LIBMTP_PROPERTY_ExposureTime, PTP_OPC_ExposureTime);
register_property("Exposure Index", LIBMTP_PROPERTY_ExposureIndex, PTP_OPC_ExposureIndex);
register_property("Display Name", LIBMTP_PROPERTY_DisplayName, PTP_OPC_DisplayName);
register_property("Body Text", LIBMTP_PROPERTY_BodyText, PTP_OPC_BodyText);
register_property("Subject", LIBMTP_PROPERTY_Subject, PTP_OPC_Subject);
register_property("Priority", LIBMTP_PROPERTY_Priority, PTP_OPC_Priority);
register_property("Given Name", LIBMTP_PROPERTY_GivenName, PTP_OPC_GivenName);
register_property("Middle Names", LIBMTP_PROPERTY_MiddleNames, PTP_OPC_MiddleNames);
register_property("Family Name", LIBMTP_PROPERTY_FamilyName, PTP_OPC_FamilyName);
register_property("Prefix", LIBMTP_PROPERTY_Prefix, PTP_OPC_Prefix);
register_property("Suffix", LIBMTP_PROPERTY_Suffix, PTP_OPC_Suffix);
register_property("Phonetic Given Name", LIBMTP_PROPERTY_PhoneticGivenName, PTP_OPC_PhoneticGivenName);
register_property("Phonetic Family Name", LIBMTP_PROPERTY_PhoneticFamilyName, PTP_OPC_PhoneticFamilyName);
register_property("Email: Primary", LIBMTP_PROPERTY_EmailPrimary, PTP_OPC_EmailPrimary);
register_property("Email: Personal 1", LIBMTP_PROPERTY_EmailPersonal1, PTP_OPC_EmailPersonal1);
register_property("Email: Personal 2", LIBMTP_PROPERTY_EmailPersonal2, PTP_OPC_EmailPersonal2);
register_property("Email: Business 1", LIBMTP_PROPERTY_EmailBusiness1, PTP_OPC_EmailBusiness1);
register_property("Email: Business 2", LIBMTP_PROPERTY_EmailBusiness2, PTP_OPC_EmailBusiness2);
register_property("Email: Others", LIBMTP_PROPERTY_EmailOthers, PTP_OPC_EmailOthers);
register_property("Phone Number: Primary", LIBMTP_PROPERTY_PhoneNumberPrimary, PTP_OPC_PhoneNumberPrimary);
register_property("Phone Number: Personal", LIBMTP_PROPERTY_PhoneNumberPersonal, PTP_OPC_PhoneNumberPersonal);
register_property("Phone Number: Personal 2", LIBMTP_PROPERTY_PhoneNumberPersonal2, PTP_OPC_PhoneNumberPersonal2);
register_property("Phone Number: Business", LIBMTP_PROPERTY_PhoneNumberBusiness, PTP_OPC_PhoneNumberBusiness);
register_property("Phone Number: Business 2", LIBMTP_PROPERTY_PhoneNumberBusiness2, PTP_OPC_PhoneNumberBusiness2);
register_property("Phone Number: Mobile", LIBMTP_PROPERTY_PhoneNumberMobile, PTP_OPC_PhoneNumberMobile);
register_property("Phone Number: Mobile 2", LIBMTP_PROPERTY_PhoneNumberMobile2, PTP_OPC_PhoneNumberMobile2);
register_property("Fax Number: Primary", LIBMTP_PROPERTY_FaxNumberPrimary, PTP_OPC_FaxNumberPrimary);
register_property("Fax Number: Personal", LIBMTP_PROPERTY_FaxNumberPersonal, PTP_OPC_FaxNumberPersonal);
register_property("Fax Number: Business", LIBMTP_PROPERTY_FaxNumberBusiness, PTP_OPC_FaxNumberBusiness);
register_property("Pager Number", LIBMTP_PROPERTY_PagerNumber, PTP_OPC_PagerNumber);
register_property("Phone Number: Others", LIBMTP_PROPERTY_PhoneNumberOthers, PTP_OPC_PhoneNumberOthers);
register_property("Primary Web Address", LIBMTP_PROPERTY_PrimaryWebAddress, PTP_OPC_PrimaryWebAddress);
register_property("Personal Web Address", LIBMTP_PROPERTY_PersonalWebAddress, PTP_OPC_PersonalWebAddress);
register_property("Business Web Address", LIBMTP_PROPERTY_BusinessWebAddress, PTP_OPC_BusinessWebAddress);
register_property("Instant Messenger Address 1", LIBMTP_PROPERTY_InstantMessengerAddress, PTP_OPC_InstantMessengerAddress);
register_property("Instant Messenger Address 2", LIBMTP_PROPERTY_InstantMessengerAddress2, PTP_OPC_InstantMessengerAddress2);
register_property("Instant Messenger Address 3", LIBMTP_PROPERTY_InstantMessengerAddress3, PTP_OPC_InstantMessengerAddress3);
register_property("Postal Address: Personal: Full", LIBMTP_PROPERTY_PostalAddressPersonalFull, PTP_OPC_PostalAddressPersonalFull);
register_property("Postal Address: Personal: Line 1", LIBMTP_PROPERTY_PostalAddressPersonalFullLine1, PTP_OPC_PostalAddressPersonalFullLine1);
register_property("Postal Address: Personal: Line 2", LIBMTP_PROPERTY_PostalAddressPersonalFullLine2, PTP_OPC_PostalAddressPersonalFullLine2);
register_property("Postal Address: Personal: City", LIBMTP_PROPERTY_PostalAddressPersonalFullCity, PTP_OPC_PostalAddressPersonalFullCity);
register_property("Postal Address: Personal: Region", LIBMTP_PROPERTY_PostalAddressPersonalFullRegion, PTP_OPC_PostalAddressPersonalFullRegion);
register_property("Postal Address: Personal: Postal Code", LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode, PTP_OPC_PostalAddressPersonalFullPostalCode);
register_property("Postal Address: Personal: Country", LIBMTP_PROPERTY_PostalAddressPersonalFullCountry, PTP_OPC_PostalAddressPersonalFullCountry);
register_property("Postal Address: Business: Full", LIBMTP_PROPERTY_PostalAddressBusinessFull, PTP_OPC_PostalAddressBusinessFull);
register_property("Postal Address: Business: Line 1", LIBMTP_PROPERTY_PostalAddressBusinessLine1, PTP_OPC_PostalAddressBusinessLine1);
register_property("Postal Address: Business: Line 2", LIBMTP_PROPERTY_PostalAddressBusinessLine2, PTP_OPC_PostalAddressBusinessLine2);
register_property("Postal Address: Business: City", LIBMTP_PROPERTY_PostalAddressBusinessCity, PTP_OPC_PostalAddressBusinessCity);
register_property("Postal Address: Business: Region", LIBMTP_PROPERTY_PostalAddressBusinessRegion, PTP_OPC_PostalAddressBusinessRegion);
register_property("Postal Address: Business: Postal Code", LIBMTP_PROPERTY_PostalAddressBusinessPostalCode, PTP_OPC_PostalAddressBusinessPostalCode);
register_property("Postal Address: Business: Country", LIBMTP_PROPERTY_PostalAddressBusinessCountry, PTP_OPC_PostalAddressBusinessCountry);
register_property("Postal Address: Other: Full", LIBMTP_PROPERTY_PostalAddressOtherFull, PTP_OPC_PostalAddressOtherFull);
register_property("Postal Address: Other: Line 1", LIBMTP_PROPERTY_PostalAddressOtherLine1, PTP_OPC_PostalAddressOtherLine1);
register_property("Postal Address: Other: Line 2", LIBMTP_PROPERTY_PostalAddressOtherLine2, PTP_OPC_PostalAddressOtherLine2);
register_property("Postal Address: Other: City", LIBMTP_PROPERTY_PostalAddressOtherCity, PTP_OPC_PostalAddressOtherCity);
register_property("Postal Address: Other: Region", LIBMTP_PROPERTY_PostalAddressOtherRegion, PTP_OPC_PostalAddressOtherRegion);
register_property("Postal Address: Other: Postal Code", LIBMTP_PROPERTY_PostalAddressOtherPostalCode, PTP_OPC_PostalAddressOtherPostalCode);
register_property("Postal Address: Other: Counrtry", LIBMTP_PROPERTY_PostalAddressOtherCountry, PTP_OPC_PostalAddressOtherCountry);
register_property("Organization Name", LIBMTP_PROPERTY_OrganizationName, PTP_OPC_OrganizationName);
register_property("Phonetic Organization Name", LIBMTP_PROPERTY_PhoneticOrganizationName, PTP_OPC_PhoneticOrganizationName);
register_property("Role", LIBMTP_PROPERTY_Role, PTP_OPC_Role);
register_property("Birthdate", LIBMTP_PROPERTY_Birthdate, PTP_OPC_Birthdate);
register_property("Message To", LIBMTP_PROPERTY_MessageTo, PTP_OPC_MessageTo);
register_property("Message CC", LIBMTP_PROPERTY_MessageCC, PTP_OPC_MessageCC);
register_property("Message BCC", LIBMTP_PROPERTY_MessageBCC, PTP_OPC_MessageBCC);
register_property("Message Read", LIBMTP_PROPERTY_MessageRead, PTP_OPC_MessageRead);
register_property("Message Received Time", LIBMTP_PROPERTY_MessageReceivedTime, PTP_OPC_MessageReceivedTime);
register_property("Message Sender", LIBMTP_PROPERTY_MessageSender, PTP_OPC_MessageSender);
register_property("Activity Begin Time", LIBMTP_PROPERTY_ActivityBeginTime, PTP_OPC_ActivityBeginTime);
register_property("Activity End Time", LIBMTP_PROPERTY_ActivityEndTime, PTP_OPC_ActivityEndTime);
register_property("Activity Location", LIBMTP_PROPERTY_ActivityLocation, PTP_OPC_ActivityLocation);
register_property("Activity Required Attendees", LIBMTP_PROPERTY_ActivityRequiredAttendees, PTP_OPC_ActivityRequiredAttendees);
register_property("Optional Attendees", LIBMTP_PROPERTY_ActivityOptionalAttendees, PTP_OPC_ActivityOptionalAttendees);
register_property("Activity Resources", LIBMTP_PROPERTY_ActivityResources, PTP_OPC_ActivityResources);
register_property("Activity Accepted", LIBMTP_PROPERTY_ActivityAccepted, PTP_OPC_ActivityAccepted);
register_property("Owner", LIBMTP_PROPERTY_Owner, PTP_OPC_Owner);
register_property("Editor", LIBMTP_PROPERTY_Editor, PTP_OPC_Editor);
register_property("Webmaster", LIBMTP_PROPERTY_Webmaster, PTP_OPC_Webmaster);
register_property("URL Source", LIBMTP_PROPERTY_URLSource, PTP_OPC_URLSource);
register_property("URL Destination", LIBMTP_PROPERTY_URLDestination, PTP_OPC_URLDestination);
register_property("Time Bookmark", LIBMTP_PROPERTY_TimeBookmark, PTP_OPC_TimeBookmark);
register_property("Object Bookmark", LIBMTP_PROPERTY_ObjectBookmark, PTP_OPC_ObjectBookmark);
register_property("Byte Bookmark", LIBMTP_PROPERTY_ByteBookmark, PTP_OPC_ByteBookmark);
register_property("Last Build Date", LIBMTP_PROPERTY_LastBuildDate, PTP_OPC_LastBuildDate);
register_property("Time To Live", LIBMTP_PROPERTY_TimetoLive, PTP_OPC_TimetoLive);
register_property("Media GUID", LIBMTP_PROPERTY_MediaGUID, PTP_OPC_MediaGUID);
register_property("Total Bit Rate", LIBMTP_PROPERTY_TotalBitRate, PTP_OPC_TotalBitRate);
register_property("Bit Rate Type", LIBMTP_PROPERTY_BitRateType, PTP_OPC_BitRateType);
register_property("Sample Rate", LIBMTP_PROPERTY_SampleRate, PTP_OPC_SampleRate);
register_property("Number Of Channels", LIBMTP_PROPERTY_NumberOfChannels, PTP_OPC_NumberOfChannels);
register_property("Audio Bit Depth", LIBMTP_PROPERTY_AudioBitDepth, PTP_OPC_AudioBitDepth);
register_property("Scan Depth", LIBMTP_PROPERTY_ScanDepth, PTP_OPC_ScanDepth);
register_property("Audio WAVE Codec", LIBMTP_PROPERTY_AudioWAVECodec, PTP_OPC_AudioWAVECodec);
register_property("Audio Bit Rate", LIBMTP_PROPERTY_AudioBitRate, PTP_OPC_AudioBitRate);
register_property("Video Four CC Codec", LIBMTP_PROPERTY_VideoFourCCCodec, PTP_OPC_VideoFourCCCodec);
register_property("Video Bit Rate", LIBMTP_PROPERTY_VideoBitRate, PTP_OPC_VideoBitRate);
register_property("Frames Per Thousand Seconds", LIBMTP_PROPERTY_FramesPerThousandSeconds, PTP_OPC_FramesPerThousandSeconds);
register_property("Key Frame Distance", LIBMTP_PROPERTY_KeyFrameDistance, PTP_OPC_KeyFrameDistance);
register_property("Buffer Size", LIBMTP_PROPERTY_BufferSize, PTP_OPC_BufferSize);
register_property("Encoding Quality", LIBMTP_PROPERTY_EncodingQuality, PTP_OPC_EncodingQuality);
register_property("Encoding Profile", LIBMTP_PROPERTY_EncodingProfile, PTP_OPC_EncodingProfile);
register_property("Buy flag", LIBMTP_PROPERTY_BuyFlag, PTP_OPC_BuyFlag);
register_property("Unknown property", LIBMTP_PROPERTY_UNKNOWN, 0);
}
/**
* Returns the PTP property that maps to a certain libmtp internal property type.
* @param inproperty the MTP library interface property
* @return the PTP (libgphoto2) property type
*/
static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty)
{
propertymap_t *current;
current = propertymap;
while (current != NULL) {
if(current->id == inproperty) {
return current->ptp_id;
}
current = current->next;
}
return 0;
}
/**
* Returns the MTP internal interface property that maps to a certain ptp
* interface property.
* @param inproperty the PTP (libgphoto2) interface property
* @return the MTP library interface property
*/
static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t inproperty)
{
propertymap_t *current;
current = propertymap;
while (current != NULL) {
if(current->ptp_id == inproperty) {
return current->id;
}
current = current->next;
}
// printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
return LIBMTP_PROPERTY_UNKNOWN;
}
/**
* Set the debug level.
*
* By default, the debug level is set to '0' (disable).
*/
void LIBMTP_Set_Debug(int level)
{
if (LIBMTP_debug || level)
LIBMTP_ERROR("LIBMTP_Set_Debug: Setting debugging level to %d (0x%02x) "
"(%s)\n", level, level, level ? "on" : "off");
LIBMTP_debug = level;
}
/**
* Initialize the library. You are only supposed to call this
* one, before using the library for the first time in a program.
* Never re-initialize libmtp!
*
* The only thing this does at the moment is to initialise the
* filetype mapping table, as well as load MTPZ data if necessary.
*/
void LIBMTP_Init(void)
{
const char *env_debug = getenv("LIBMTP_DEBUG");
if (env_debug) {
const long debug_flags = strtol(env_debug, NULL, 0);
if (debug_flags != LONG_MIN && debug_flags != LONG_MAX &&
INT_MIN <= debug_flags && debug_flags <= INT_MAX) {
LIBMTP_Set_Debug(debug_flags);
} else {
fprintf(stderr, "LIBMTP_Init: error setting debug flags from environment "
"value \"%s\"\n", env_debug);
}
}
init_filemap();
init_propertymap();
if (mtpz_loaddata() == -1)
use_mtpz = 0;
else
use_mtpz = 1;
return;
}
/**
* This helper function returns a textual description for a libmtp
* file type to be used in dialog boxes etc.
* @param intype the libmtp internal filetype to get a description for.
* @return a string representing the filetype, this must <b>NOT</b>
* be free():ed by the caller!
*/
char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
{
filemap_t *current;
current = filemap;
while (current != NULL) {
if(current->id == intype) {
return current->description;
}
current = current->next;
}
return "Unknown filetype";
}
/**
* This helper function returns a textual description for a libmtp
* property to be used in dialog boxes etc.
* @param inproperty the libmtp internal property to get a description for.
* @return a string representing the filetype, this must <b>NOT</b>
* be free():ed by the caller!
*/
char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty)
{
propertymap_t *current;
current = propertymap;
while (current != NULL) {
if(current->id == inproperty) {
return current->description;
}
current = current->next;
}
return "Unknown property";
}
/**
* This function will do its best to fit a 16bit
* value into a PTP object property if the property
* is limited in range or step sizes.
*/
static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd)
{
switch (opd->FormFlag) {
case PTP_DPFF_Range:
if (val < opd->FORM.Range.MinimumValue.u16) {
return opd->FORM.Range.MinimumValue.u16;
}
if (val > opd->FORM.Range.MaximumValue.u16) {
return opd->FORM.Range.MaximumValue.u16;
}
// Round down to last step.
if (val % opd->FORM.Range.StepSize.u16 != 0) {
return val - (val % opd->FORM.Range.StepSize.u16);
}
return val;
break;
case PTP_DPFF_Enumeration:
{
int i;
uint16_t bestfit = opd->FORM.Enum.SupportedValue[0].u16;
for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
if (val == opd->FORM.Enum.SupportedValue[i].u16) {
return val;
}
// Rough guess of best fit
if (opd->FORM.Enum.SupportedValue[i].u16 < val) {
bestfit = opd->FORM.Enum.SupportedValue[i].u16;
}
}
// Just some default that'll work.
return bestfit;
}
default:
// Will accept any value
break;
}
return val;
}
/**
* This function will do its best to fit a 32bit
* value into a PTP object property if the property
* is limited in range or step sizes.
*/
static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd)
{
switch (opd->FormFlag) {
case PTP_DPFF_Range:
if (val < opd->FORM.Range.MinimumValue.u32) {
return opd->FORM.Range.MinimumValue.u32;
}
if (val > opd->FORM.Range.MaximumValue.u32) {
return opd->FORM.Range.MaximumValue.u32;
}
// Round down to last step.
if (val % opd->FORM.Range.StepSize.u32 != 0) {
return val - (val % opd->FORM.Range.StepSize.u32);
}
return val;
break;
case PTP_DPFF_Enumeration:
{
int i;
uint32_t bestfit = opd->FORM.Enum.SupportedValue[0].u32;
for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
if (val == opd->FORM.Enum.SupportedValue[i].u32) {
return val;
}
// Rough guess of best fit
if (opd->FORM.Enum.SupportedValue[i].u32 < val) {
bestfit = opd->FORM.Enum.SupportedValue[i].u32;
}
}
// Just some default that'll work.
return bestfit;
}
default:
// Will accept any value
break;
}
return val;
}
/**
* This function returns a newly created ISO 8601 timestamp with the
* current time in as high precision as possible. It even adds
* the time zone if it can.
*/
static char *get_iso8601_stamp(void)
{
time_t curtime;
struct tm *loctime;
char tmp[64];
curtime = time(NULL);
loctime = localtime(&curtime);
strftime (tmp, sizeof(tmp), "%Y%m%dT%H%M%S.0%z", loctime);
return strdup(tmp);
}
/**
* Gets the allowed values (range or enum) for a property
* @param device a pointer to an MTP device
* @param property the property to query
* @param filetype the filetype of the object you want to set values for
* @param allowed_vals pointer to a LIBMTP_allowed_values_t struct to
* receive the allowed values. Call LIBMTP_destroy_allowed_values_t
* on this on successful completion.
* @return 0 on success, any other value means failure
*/
int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
LIBMTP_filetype_t const filetype, LIBMTP_allowed_values_t *allowed_vals)
{
PTPObjectPropDesc opd;
uint16_t ret = 0;
ret = ptp_mtp_getobjectpropdesc(device->params, map_libmtp_property_to_ptp_property(property), map_libmtp_type_to_ptp_type(filetype), &opd);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Allowed_Property_Values(): could not get property description.");
return -1;
}
if (opd.FormFlag == PTP_OPFF_Enumeration) {
int i = 0;
allowed_vals->is_range = 0;
allowed_vals->num_entries = opd.FORM.Enum.NumberOfValues;
switch (opd.DataType)
{
case PTP_DTC_INT8:
allowed_vals->i8vals = malloc(sizeof(int8_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
break;
case PTP_DTC_UINT8:
allowed_vals->u8vals = malloc(sizeof(uint8_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
break;
case PTP_DTC_INT16:
allowed_vals->i16vals = malloc(sizeof(int16_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
break;
case PTP_DTC_UINT16:
allowed_vals->u16vals = malloc(sizeof(uint16_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
break;
case PTP_DTC_INT32:
allowed_vals->i32vals = malloc(sizeof(int32_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
break;
case PTP_DTC_UINT32:
allowed_vals->u32vals = malloc(sizeof(uint32_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
break;
case PTP_DTC_INT64:
allowed_vals->i64vals = malloc(sizeof(int64_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
break;
case PTP_DTC_UINT64:
allowed_vals->u64vals = malloc(sizeof(uint64_t) * opd.FORM.Enum.NumberOfValues);
allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
break;
}
for (i = 0; i < opd.FORM.Enum.NumberOfValues; i++) {
switch (opd.DataType)
{
case PTP_DTC_INT8:
allowed_vals->i8vals[i] = opd.FORM.Enum.SupportedValue[i].i8;
break;
case PTP_DTC_UINT8:
allowed_vals->u8vals[i] = opd.FORM.Enum.SupportedValue[i].u8;
break;
case PTP_DTC_INT16:
allowed_vals->i16vals[i] = opd.FORM.Enum.SupportedValue[i].i16;
break;
case PTP_DTC_UINT16:
allowed_vals->u16vals[i] = opd.FORM.Enum.SupportedValue[i].u16;
break;
case PTP_DTC_INT32:
allowed_vals->i32vals[i] = opd.FORM.Enum.SupportedValue[i].i32;
break;
case PTP_DTC_UINT32:
allowed_vals->u32vals[i] = opd.FORM.Enum.SupportedValue[i].u32;
break;
case PTP_DTC_INT64:
allowed_vals->i64vals[i] = opd.FORM.Enum.SupportedValue[i].i64;
break;
case PTP_DTC_UINT64:
allowed_vals->u64vals[i] = opd.FORM.Enum.SupportedValue[i].u64;
break;
}
}
ptp_free_objectpropdesc(&opd);
return 0;
} else if (opd.FormFlag == PTP_OPFF_Range) {
allowed_vals->is_range = 1;
switch (opd.DataType)
{
case PTP_DTC_INT8:
allowed_vals->i8min = opd.FORM.Range.MinimumValue.i8;
allowed_vals->i8max = opd.FORM.Range.MaximumValue.i8;
allowed_vals->i8step = opd.FORM.Range.StepSize.i8;
allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
break;
case PTP_DTC_UINT8:
allowed_vals->u8min = opd.FORM.Range.MinimumValue.u8;
allowed_vals->u8max = opd.FORM.Range.MaximumValue.u8;
allowed_vals->u8step = opd.FORM.Range.StepSize.u8;
allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
break;
case PTP_DTC_INT16:
allowed_vals->i16min = opd.FORM.Range.MinimumValue.i16;
allowed_vals->i16max = opd.FORM.Range.MaximumValue.i16;
allowed_vals->i16step = opd.FORM.Range.StepSize.i16;
allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
break;
case PTP_DTC_UINT16:
allowed_vals->u16min = opd.FORM.Range.MinimumValue.u16;
allowed_vals->u16max = opd.FORM.Range.MaximumValue.u16;
allowed_vals->u16step = opd.FORM.Range.StepSize.u16;
allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
break;
case PTP_DTC_INT32:
allowed_vals->i32min = opd.FORM.Range.MinimumValue.i32;
allowed_vals->i32max = opd.FORM.Range.MaximumValue.i32;
allowed_vals->i32step = opd.FORM.Range.StepSize.i32;
allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
break;
case PTP_DTC_UINT32:
allowed_vals->u32min = opd.FORM.Range.MinimumValue.u32;
allowed_vals->u32max = opd.FORM.Range.MaximumValue.u32;
allowed_vals->u32step = opd.FORM.Range.StepSize.u32;
allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
break;
case PTP_DTC_INT64:
allowed_vals->i64min = opd.FORM.Range.MinimumValue.i64;
allowed_vals->i64max = opd.FORM.Range.MaximumValue.i64;
allowed_vals->i64step = opd.FORM.Range.StepSize.i64;
allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
break;
case PTP_DTC_UINT64:
allowed_vals->u64min = opd.FORM.Range.MinimumValue.u64;
allowed_vals->u64max = opd.FORM.Range.MaximumValue.u64;
allowed_vals->u64step = opd.FORM.Range.StepSize.u64;
allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
break;
}
return 0;
} else
return -1;
}
/**
* Destroys a LIBMTP_allowed_values_t struct
* @param allowed_vals the struct to destroy
*/
void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t *allowed_vals)
{
if (!allowed_vals->is_range)
{
switch (allowed_vals->datatype)
{
case LIBMTP_DATATYPE_INT8:
if (allowed_vals->i8vals)
free(allowed_vals->i8vals);
break;
case LIBMTP_DATATYPE_UINT8:
if (allowed_vals->u8vals)
free(allowed_vals->u8vals);
break;
case LIBMTP_DATATYPE_INT16:
if (allowed_vals->i16vals)
free(allowed_vals->i16vals);
break;
case LIBMTP_DATATYPE_UINT16:
if (allowed_vals->u16vals)
free(allowed_vals->u16vals);
break;
case LIBMTP_DATATYPE_INT32:
if (allowed_vals->i32vals)
free(allowed_vals->i32vals);
break;
case LIBMTP_DATATYPE_UINT32:
if (allowed_vals->u32vals)
free(allowed_vals->u32vals);
break;
case LIBMTP_DATATYPE_INT64:
if (allowed_vals->i64vals)
free(allowed_vals->i64vals);
break;
case LIBMTP_DATATYPE_UINT64:
if (allowed_vals->u64vals)
free(allowed_vals->u64vals);
break;
}
}
}
/**
* Determine if a property is supported for a given file type
* @param device a pointer to an MTP device
* @param property the property to query
* @param filetype the filetype of the object you want to set values for
* @return 0 if not supported, positive if supported, negative on error
*/
int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
LIBMTP_filetype_t const filetype)
{
uint16_t *props = NULL;
uint32_t propcnt = 0;
uint16_t ret = 0;
int i = 0;
int supported = 0;
uint16_t ptp_prop = map_libmtp_property_to_ptp_property(property);
if (!ptp_operation_issupported(device->params, PTP_OC_MTP_GetObjectPropsSupported))
return 0;
ret = ptp_mtp_getobjectpropssupported(device->params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "LIBMTP_Is_Property_Supported(): could not get properties supported.");
return -1;
}
for (i = 0; i < propcnt; i++) {
if (props[i] == ptp_prop) {
supported = 1;
break;
}
}
free(props);
return supported;
}
/**
* Retrieves a string from an object
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @return valid string or NULL on failure. The returned string
* must bee <code>free()</code>:ed by the caller after
* use.
*/
char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id)
{
return get_string_from_object(device, object_id, attribute_id);
}
/**
* Retrieves an unsigned 64-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value_default Default value to return on failure
* @return the value
*/
uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint64_t const value_default)
{
return get_u64_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
}
/**
* Retrieves an unsigned 32-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value_default Default value to return on failure
* @return the value
*/
uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint32_t const value_default)
{
return get_u32_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
}
/**
* Retrieves an unsigned 16-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value_default Default value to return on failure
* @return a value
*/
uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint16_t const value_default)
{
return get_u16_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
}
/**
* Retrieves an unsigned 8-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value_default Default value to return on failure
* @return a value
*/
uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint8_t const value_default)
{
return get_u8_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
}
/**
* Sets an object attribute from a string
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param string string value to set
* @return 0 on success, any other value means failure
*/
int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, char const * const string)
{
return set_object_string(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), string);
}
/**
* Sets an object attribute from an unsigned 32-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value 32-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint32_t const value)
{
return set_object_u32(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
}
/**
* Sets an object attribute from an unsigned 16-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value 16-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint16_t const value)
{
return set_object_u16(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
}
/**
* Sets an object attribute from an unsigned 8-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id MTP attribute ID
* @param value 8-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
LIBMTP_property_t const attribute_id, uint8_t const value)
{
return set_object_u8(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
}
/**
* Retrieves a string from an object
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @return valid string or NULL on failure. The returned string
* must bee <code>free()</code>:ed by the caller after
* use.
*/
static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id)
{
PTPPropertyValue propval;
char *retstring = NULL;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
MTPProperties *prop;
if ( device == NULL || object_id == 0) {
return NULL;
}
prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
if (prop) {
if (prop->propval.str != NULL)
return strdup(prop->propval.str);
else
return NULL;
}
ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
if (ret == PTP_RC_OK) {
if (propval.str != NULL) {
retstring = (char *) strdup(propval.str);
free(propval.str);
}
} else {
add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
}
return retstring;
}
/**
* Retrieves an unsigned 64-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value_default Default value to return on failure
* @return the value
*/
static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
uint16_t const attribute_id, uint64_t const value_default)
{
PTPPropertyValue propval;
uint64_t retval = value_default;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
MTPProperties *prop;
if ( device == NULL ) {
return value_default;
}
prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
if (prop)
return prop->propval.u64;
ret = ptp_mtp_getobjectpropvalue(params, object_id,
attribute_id,
&propval,
PTP_DTC_UINT64);
if (ret == PTP_RC_OK) {
retval = propval.u64;
} else {
add_ptp_error_to_errorstack(device, ret, "get_u64_from_object(): could not get unsigned 64bit integer from object.");
}
return retval;
}
/**
* Retrieves an unsigned 32-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value_default Default value to return on failure
* @return the value
*/
static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
uint16_t const attribute_id, uint32_t const value_default)
{
PTPPropertyValue propval;
uint32_t retval = value_default;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
MTPProperties *prop;
if ( device == NULL ) {
return value_default;
}
prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
if (prop)
return prop->propval.u32;
ret = ptp_mtp_getobjectpropvalue(params, object_id,
attribute_id,
&propval,
PTP_DTC_UINT32);
if (ret == PTP_RC_OK) {
retval = propval.u32;
} else {
add_ptp_error_to_errorstack(device, ret, "get_u32_from_object(): could not get unsigned 32bit integer from object.");
}
return retval;
}
/**
* Retrieves an unsigned 16-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value_default Default value to return on failure
* @return a value
*/
static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint16_t const value_default)
{
PTPPropertyValue propval;
uint16_t retval = value_default;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
MTPProperties *prop;
if ( device == NULL ) {
return value_default;
}
// This O(n) search should not be used so often, since code
// using the cached properties don't usually call this function.
prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
if (prop)
return prop->propval.u16;
ret = ptp_mtp_getobjectpropvalue(params, object_id,
attribute_id,
&propval,
PTP_DTC_UINT16);
if (ret == PTP_RC_OK) {
retval = propval.u16;
} else {
add_ptp_error_to_errorstack(device, ret, "get_u16_from_object(): could not get unsigned 16bit integer from object.");
}
return retval;
}
/**
* Retrieves an unsigned 8-bit integer from an object attribute
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value_default Default value to return on failure
* @return a value
*/
static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint8_t const value_default)
{
PTPPropertyValue propval;
uint8_t retval = value_default;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
MTPProperties *prop;
if ( device == NULL ) {
return value_default;
}
// This O(n) search should not be used so often, since code
// using the cached properties don't usually call this function.
prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
if (prop)
return prop->propval.u8;
ret = ptp_mtp_getobjectpropvalue(params, object_id,
attribute_id,
&propval,
PTP_DTC_UINT8);
if (ret == PTP_RC_OK) {
retval = propval.u8;
} else {
add_ptp_error_to_errorstack(device, ret, "get_u8_from_object(): could not get unsigned 8bit integer from object.");
}
return retval;
}
/**
* Sets an object attribute from a string
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param string string value to set
* @return 0 on success, any other value means failure
*/
static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, char const * const string)
{
PTPPropertyValue propval;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
if (device == NULL || string == NULL) {
return -1;
}
if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
"PTP_OC_MTP_SetObjectPropValue not supported.");
return -1;
}
propval.str = (char *) string;
ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
return -1;
}
return 0;
}
/**
* Sets an object attribute from an unsigned 32-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value 32-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint32_t const value)
{
PTPPropertyValue propval;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
if (device == NULL) {
return -1;
}
if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
"PTP_OC_MTP_SetObjectPropValue not supported.");
return -1;
}
propval.u32 = value;
ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
return -1;
}
return 0;
}
/**
* Sets an object attribute from an unsigned 16-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value 16-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint16_t const value)
{
PTPPropertyValue propval;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
if (device == NULL) {
return 1;
}
if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
"PTP_OC_MTP_SetObjectPropValue not supported.");
return -1;
}
propval.u16 = value;
ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
return 1;
}
return 0;
}
/**
* Sets an object attribute from an unsigned 8-bit integer
*
* @param device a pointer to an MTP device.
* @param object_id Object reference
* @param attribute_id PTP attribute ID
* @param value 8-bit unsigned integer to set
* @return 0 on success, any other value means failure
*/
static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
uint16_t const attribute_id, uint8_t const value)
{
PTPPropertyValue propval;
PTPParams *params = (PTPParams *) device->params;
uint16_t ret;
if (device == NULL) {
return 1;
}
if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
"PTP_OC_MTP_SetObjectPropValue not supported.");
return -1;
}
propval.u8 = value;
ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
return 1;
}
return 0;
}
/**
* Get the first (as in "first in the list of") connected MTP device.
* @return a device pointer.
* @see LIBMTP_Get_Connected_Devices()
*/
LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
{
LIBMTP_mtpdevice_t *first_device = NULL;
LIBMTP_raw_device_t *devices;
int numdevs;
LIBMTP_error_number_t ret;
ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
if (ret != LIBMTP_ERROR_NONE) {
return NULL;
}
if (devices == NULL || numdevs == 0) {
return NULL;
}
first_device = LIBMTP_Open_Raw_Device(&devices[0]);
free(devices);
return first_device;
}
/**
* Overriding debug function.
* This way we can disable debug prints.
*/
static void
#ifdef __GNUC__
__attribute__((__format__(printf,2,0)))
#endif
LIBMTP_ptp_debug(void *data, const char *format, va_list args)
{
if ((LIBMTP_debug & LIBMTP_DEBUG_PTP) != 0) {
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
fflush (stderr);
}
}
/**
* Overriding error function.
* This way we can capture all error etc to our errorstack.
*/
static void
#ifdef __GNUC__
__attribute__((__format__(printf,2,0)))
#endif
LIBMTP_ptp_error(void *data, const char *format, va_list args)
{
// if (data == NULL) {
vfprintf (stderr, format, args);
fflush (stderr);
/*
FIXME: find out how we shall get the device here.
} else {
PTP_USB *ptp_usb = data;
LIBMTP_mtpdevice_t *device = ...;
char buf[2048];
vsnprintf (buf, sizeof (buf), format, args);
add_error_to_errorstack(device,
LIBMTP_ERROR_PTP_LAYER,
buf);
}
*/
}
/**
* Parses the extension descriptor, there may be stuff in
* this that we want to know about.
*/
static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
char *desc)
{
int start = 0;
int end = 0;
/* NULL on Canon A70 */
if (!desc)
return;
/* descriptors are divided by semicolons */
while (end < strlen(desc)) {
/* Skip past initial whitespace */
while (desc[start] == ' ' && end < strlen(desc)) {
start++;
end++;
}
/* Detect extension */
while (desc[end] != ';' && end < strlen(desc))
end++;
if (end < strlen(desc)) {
char *element = strndup(desc + start, end-start);
if (element) {
int i = 0;
// printf(" Element: \"%s\"\n", element);
/* Parse for an extension */
while (element[i] != ':' && i < strlen(element))
i++;
if (i < strlen(element)) {
char *name = strndup(element, i);
int majstart = i+1;
// printf(" Extension: \"%s\"\n", name);
/* Parse for minor/major punctuation mark for this extension */
while (element[i] != '.' && i < strlen(element))
i++;
if (i > majstart && i < strlen(element)) {
LIBMTP_device_extension_t *extension;
int major = 0;
int minor = 0;
char *majorstr = strndup(element + majstart, i - majstart);
char *minorstr = strndup(element + i + 1, strlen(element) - i - 1);
major = atoi(majorstr);
minor = atoi(minorstr);
free(majorstr);
free(minorstr);
extension = malloc(sizeof(LIBMTP_device_extension_t));
extension->name = name;
extension->major = major;
extension->minor = minor;
extension->next = NULL;
if (mtpdevice->extensions == NULL) {
mtpdevice->extensions = extension;
} else {
LIBMTP_device_extension_t *tmp = mtpdevice->extensions;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = extension;
}
// printf(" Major: \"%s\" (parsed %d) Minor: \"%s\" (parsed %d)\n",
// majorstr, major, minorstr, minor);
} else {
LIBMTP_ERROR("LIBMTP ERROR: couldnt parse extension %s\n",
element);
}
}
free(element);
}
}
end++;
start = end;
}
}
/**
* This function opens a device from a raw device. It is the
* preferred way to access devices in the new interface where
* several devices can come and go as the library is working
* on a certain device.
* @param rawdevice the raw device to open a "real" device for.
* @return an open device.
*/
LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevice)
{
LIBMTP_mtpdevice_t *mtp_device;
uint8_t bs = 0;
PTPParams *current_params;
PTP_USB *ptp_usb;
LIBMTP_error_number_t err;
int i;
/* Allocate dynamic space for our device */
mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
/* Check if there was a memory allocation error */
if(mtp_device == NULL) {
/* There has been an memory allocation error. We are going to ignore this
device and attempt to continue */
/* TODO: This error statement could probably be a bit more robust */
LIBMTP_ERROR("LIBMTP PANIC: connect_usb_devices encountered a memory "
"allocation error with device %d on bus %d, trying to continue",
rawdevice->devnum, rawdevice->bus_location);
return NULL;
}
// Non-cached by default
mtp_device->cached = 0;
/* Create PTP params */
current_params = (PTPParams *) malloc(sizeof(PTPParams));
if (current_params == NULL) {
free(mtp_device);
return NULL;
}
memset(current_params, 0, sizeof(PTPParams));
current_params->device_flags = rawdevice->device_entry.device_flags;
current_params->nrofobjects = 0;
current_params->objects = NULL;
current_params->response_packet_size = 0;
current_params->response_packet = NULL;
/* This will be a pointer to PTP_USB later */
current_params->data = NULL;
/* Set upp local debug and error functions */
current_params->debug_func = LIBMTP_ptp_debug;
current_params->error_func = LIBMTP_ptp_error;
/* TODO: Will this always be little endian? */
current_params->byteorder = PTP_DL_LE;
current_params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8");
current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UCS-2LE");
if(current_params->cd_locale_to_ucs2 == (iconv_t) -1 ||
current_params->cd_ucs2_to_locale == (iconv_t) -1) {
LIBMTP_ERROR("LIBMTP PANIC: Cannot open iconv() converters to/from UCS-2!\n"
"Too old stdlibc, glibc and libiconv?\n");
free(current_params);
free(mtp_device);
return NULL;
}
mtp_device->params = current_params;
/* Create usbinfo, this also opens the session */
err = configure_usb_device(rawdevice,
current_params,
&mtp_device->usbinfo);
if (err != LIBMTP_ERROR_NONE) {
free(current_params);
free(mtp_device);
return NULL;
}
ptp_usb = (PTP_USB*) mtp_device->usbinfo;
/* Set pointer back to params */
ptp_usb->params = current_params;
/* Cache the device information for later use */
if (ptp_getdeviceinfo(current_params,
&current_params->deviceinfo) != PTP_RC_OK) {
LIBMTP_ERROR("LIBMTP PANIC: Unable to read device information on device "
"%d on bus %d, trying to continue",
rawdevice->devnum, rawdevice->bus_location);
/* Prevent memory leaks for this device */
free(mtp_device->usbinfo);
free(mtp_device->params);
current_params = NULL;
free(mtp_device);
return NULL;
}
/* Check: if this is a PTP device, is it really tagged as MTP? */
if (current_params->deviceinfo.VendorExtensionID != 0x00000006) {
LIBMTP_ERROR("LIBMTP WARNING: no MTP vendor extension on device "
"%d on bus %d\n",
rawdevice->devnum, rawdevice->bus_location);
LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionID: %08x\n",
current_params->deviceinfo.VendorExtensionID);
LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionDesc: %s\n",
current_params->deviceinfo.VendorExtensionDesc);
LIBMTP_ERROR("LIBMTP WARNING: this typically means the device is PTP "
"(i.e. a camera) but not an MTP device at all. "
"Trying to continue anyway.\n");
}
parse_extension_descriptor(mtp_device,
current_params->deviceinfo.VendorExtensionDesc);
/*
* Android has a number of bugs, force-assign these bug flags
* if Android is encountered. Same thing for devices we detect
* as SONY NWZ Walkmen. I have no clue what "sony.net/WMFU" means
* I just know only NWZs have it.
*/
{
LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
int is_microsoft_com_wpdna = 0;
int is_android = 0;
int is_sony_net_wmfu = 0;
int is_sonyericsson_com_se = 0;
/* Loop over extensions and set flags */
while (tmpext != NULL) {
if (!strcmp(tmpext->name, "microsoft.com/WPDNA"))
is_microsoft_com_wpdna = 1;
if (!strcmp(tmpext->name, "android.com"))
is_android = 1;
if (!strcmp(tmpext->name, "sony.net/WMFU"))
is_sony_net_wmfu = 1;
if (!strcmp(tmpext->name, "sonyericsson.com/SE"))
is_sonyericsson_com_se = 1;
tmpext = tmpext->next;
}
/* Check for specific stacks */
if (is_microsoft_com_wpdna && is_sonyericsson_com_se && !is_android) {
/*
* The Aricent stack seems to be detected by providing WPDNA, the SonyEricsson
* extension and NO Android extension.
*/
ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ARICENT_BUGS;
LIBMTP_INFO("Aricent MTP stack device detected, assigning default bug flags\n");
}
else if (is_android) {
/*
* If bugs are fixed in later versions, test on tmpext->major, tmpext->minor
*/
ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ANDROID_BUGS;
LIBMTP_INFO("Android device detected, assigning default bug flags\n");
}
else if (is_sony_net_wmfu) {
ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_SONY_NWZ_BUGS;
LIBMTP_INFO("SONY NWZ device detected, assigning default bug flags\n");
}
}
/*
* If the OGG or FLAC filetypes are flagged as "unknown", check
* if the firmware has been updated to actually support it.
*/
if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_OGG) {
/* This is not unknown anymore, unflag it */
ptp_usb->rawdevice.device_entry.device_flags &=
~DEVICE_FLAG_OGG_IS_UNKNOWN;
break;
}
}
}
if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_FLAC) {
/* This is not unknown anymore, unflag it */
ptp_usb->rawdevice.device_entry.device_flags &=
~DEVICE_FLAG_FLAC_IS_UNKNOWN;
break;
}
}
}
/* Determine if the object size supported is 32 or 64 bit wide */
if (ptp_operation_issupported(current_params,PTP_OC_MTP_GetObjectPropsSupported)) {
for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
PTPObjectPropDesc opd;
if (ptp_mtp_getobjectpropdesc(current_params,
PTP_OPC_ObjectSize,
current_params->deviceinfo.ImageFormats[i],
&opd) != PTP_RC_OK) {
LIBMTP_ERROR("LIBMTP PANIC: "
"could not inspect object property descriptions!\n");
} else {
if (opd.DataType == PTP_DTC_UINT32) {
if (bs == 0) {
bs = 32;
} else if (bs != 32) {
LIBMTP_ERROR("LIBMTP PANIC: "
"different objects support different object sizes!\n");
bs = 0;
break;
}
} else if (opd.DataType == PTP_DTC_UINT64) {
if (bs == 0) {
bs = 64;
} else if (bs != 64) {
LIBMTP_ERROR("LIBMTP PANIC: "
"different objects support different object sizes!\n");
bs = 0;
break;
}
} else {
// Ignore if other size.
LIBMTP_ERROR("LIBMTP PANIC: "
"awkward object size data type: %04x\n", opd.DataType);
bs = 0;
break;
}
}
}
}
if (bs == 0) {
// Could not detect object bitsize, assume 32 bits
bs = 32;
}
mtp_device->object_bitsize = bs;
/* No Errors yet for this device */
mtp_device->errorstack = NULL;
/* Default Max Battery Level, we will adjust this if possible */
mtp_device->maximum_battery_level = 100;
/* Check if device supports reading maximum battery level */
if(!FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) &&
ptp_property_issupported( current_params, PTP_DPC_BatteryLevel)) {
PTPDevicePropDesc dpd;
/* Try to read maximum battery level */
if(ptp_getdevicepropdesc(current_params,
PTP_DPC_BatteryLevel,
&dpd) != PTP_RC_OK) {
add_error_to_errorstack(mtp_device,
LIBMTP_ERROR_CONNECTING,
"Unable to read Maximum Battery Level for this "
"device even though the device supposedly "
"supports this functionality");
}
/* TODO: is this appropriate? */
/* If max battery level is 0 then leave the default, otherwise assign */
if (dpd.FORM.Range.MaximumValue.u8 != 0) {
mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
}
ptp_free_devicepropdesc(&dpd);
}
/* Set all default folders to 0xffffffffU (root directory) */
mtp_device->default_music_folder = 0xffffffffU;
mtp_device->default_playlist_folder = 0xffffffffU;
mtp_device->default_picture_folder = 0xffffffffU;
mtp_device->default_video_folder = 0xffffffffU;
mtp_device->default_organizer_folder = 0xffffffffU;
mtp_device->default_zencast_folder = 0xffffffffU;
mtp_device->default_album_folder = 0xffffffffU;
mtp_device->default_text_folder = 0xffffffffU;
/* Set initial storage information */
mtp_device->storage = NULL;
if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) {
add_error_to_errorstack(mtp_device,
LIBMTP_ERROR_GENERAL,
"Get Storage information failed.");
mtp_device->storage = NULL;
}
return mtp_device;
}
LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
{
LIBMTP_mtpdevice_t *mtp_device = LIBMTP_Open_Raw_Device_Uncached(rawdevice);
if (mtp_device == NULL)
return NULL;
/* Check for MTPZ devices. */
if (use_mtpz) {
LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
while (tmpext != NULL) {
if (!strcmp(tmpext->name, "microsoft.com/MTPZ")) {
LIBMTP_INFO("MTPZ device detected. Authenticating...\n");
if (PTP_RC_OK == ptp_mtpz_handshake(mtp_device->params)) {
LIBMTP_INFO ("(MTPZ) Successfully authenticated with device.\n");
} else {
LIBMTP_INFO ("(MTPZ) Failure - could not authenticate with device.\n");
}
break;
}
tmpext = tmpext->next;
}
}
// Set up this device as cached
mtp_device->cached = 1;
/*
* Then get the handles and try to locate the default folders.
* This has the desired side effect of caching all handles from
* the device which speeds up later operations.
*/
flush_handles(mtp_device);
return mtp_device;
}
/**
* To read events sent by the device, repeatedly call this function from a secondary
* thread until the return value is < 0.
*
* @param device a pointer to the MTP device to poll for events.
* @param event contains a pointer to be filled in with the event retrieved if the call
* is successful.
* @param out1 contains the param1 value from the raw event.
* @return 0 on success, any other value means the polling loop shall be
* terminated immediately for this session.
*/
int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_t *out1)
{
/*
* FIXME: Potential race-condition here, if client deallocs device
* while we're *not* waiting for input. As we'll be waiting for
* input most of the time, it's unlikely but still worth considering
* for improvement. Also we cannot affect the state of the cache etc
* unless we know we are the sole user on the device. A spinlock or
* mutex in the LIBMTP_mtpdevice_t is needed for this to work.
*/
PTPParams *params = (PTPParams *) device->params;
PTPContainer ptp_event;
uint16_t ret = ptp_usb_event_wait(params, &ptp_event);
uint16_t code;
uint32_t session_id;
uint32_t transaction_id;
uint32_t param1;
uint32_t param2;
uint32_t param3;
if (ret != PTP_RC_OK) {
/* Device is closing down or other fatal stuff, exit thread */
return -1;
}
*event = LIBMTP_EVENT_NONE;
/* Process the event */
code = ptp_event.Code;
session_id = ptp_event.SessionID;
transaction_id = ptp_event.Transaction_ID;
param1 = ptp_event.Param1;
param2 = ptp_event.Param2;
param3 = ptp_event.Param3;
switch(code) {
case PTP_EC_Undefined:
LIBMTP_INFO("Received event PTP_EC_Undefined in session %u\n", session_id);
break;
case PTP_EC_CancelTransaction:
LIBMTP_INFO("Received event PTP_EC_CancelTransaction in session %u\n", session_id);
break;
case PTP_EC_ObjectAdded:
LIBMTP_INFO("Received event PTP_EC_ObjectAdded in session %u\n", session_id);
break;
case PTP_EC_ObjectRemoved:
LIBMTP_INFO("Received event PTP_EC_ObjectRemoved in session %u\n", session_id);
break;
case PTP_EC_StoreAdded:
LIBMTP_INFO("Received event PTP_EC_StoreAdded in session %u\n", session_id);
/* TODO: rescan storages */
*event = LIBMTP_EVENT_STORE_ADDED;
*out1 = param1;
break;
case PTP_EC_StoreRemoved:
LIBMTP_INFO("Received event PTP_EC_StoreRemoved in session %u\n", session_id);
/* TODO: rescan storages */
break;
case PTP_EC_DevicePropChanged:
LIBMTP_INFO("Received event PTP_EC_DevicePropChanged in session %u\n", session_id);
/* TODO: update device properties */
break;
case PTP_EC_ObjectInfoChanged:
LIBMTP_INFO("Received event PTP_EC_ObjectInfoChanged in session %u\n", session_id);
/* TODO: rescan object cache or just for this one object */
break;
case PTP_EC_DeviceInfoChanged:
LIBMTP_INFO("Received event PTP_EC_DeviceInfoChanged in session %u\n", session_id);
/* TODO: update device info */
break;
case PTP_EC_RequestObjectTransfer:
LIBMTP_INFO("Received event PTP_EC_RequestObjectTransfer in session %u\n", session_id);
break;
case PTP_EC_StoreFull:
LIBMTP_INFO("Received event PTP_EC_StoreFull in session %u\n", session_id);
break;
case PTP_EC_DeviceReset:
LIBMTP_INFO("Received event PTP_EC_DeviceReset in session %u\n", session_id);
break;
case PTP_EC_StorageInfoChanged :
LIBMTP_INFO( "Received event PTP_EC_StorageInfoChanged in session %u\n", session_id);
/* TODO: update storage info */
break;
case PTP_EC_CaptureComplete :
LIBMTP_INFO( "Received event PTP_EC_CaptureComplete in session %u\n", session_id);
break;
case PTP_EC_UnreportedStatus :
LIBMTP_INFO( "Received event PTP_EC_UnreportedStatus in session %u\n", session_id);
break;
default :
LIBMTP_INFO( "Received unknown event in session %u\n", session_id);
break;
}
return 0;
}
/**
* Recursive function that adds MTP devices to a linked list
* @param devices a list of raw devices to have real devices created for.
* @return a device pointer to a newly created mtpdevice (used in linked
* list creation).
*/
static LIBMTP_mtpdevice_t * create_usb_mtp_devices(LIBMTP_raw_device_t *devices, int numdevs)
{
uint8_t i;
LIBMTP_mtpdevice_t *mtp_device_list = NULL;
LIBMTP_mtpdevice_t *current_device = NULL;
for (i=0; i < numdevs; i++) {
LIBMTP_mtpdevice_t *mtp_device;
mtp_device = LIBMTP_Open_Raw_Device(&devices[i]);
/* On error, try next device */
if (mtp_device == NULL)
continue;
/* Add the device to the list */
mtp_device->next = NULL;
if (mtp_device_list == NULL) {
mtp_device_list = current_device = mtp_device;
} else {
current_device->next = mtp_device;
current_device = mtp_device;
}
}
return mtp_device_list;
}
/**
* Get the number of devices that are available in the listed device list
* @param device_list Pointer to a linked list of devices
* @return Number of devices in the device list device_list
* @see LIBMTP_Get_Connected_Devices()
*/
uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *device_list)
{
uint32_t numdevices = 0;
LIBMTP_mtpdevice_t *iter;
for(iter = device_list; iter != NULL; iter = iter->next)
numdevices++;
return numdevices;
}
/**
* Get the first connected MTP device node in the linked list of devices.
* Currently this only provides access to USB devices
* @param device_list A list of devices ready to be used by the caller. You
* need to know how many there are.
* @return Any error information gathered from device connections
* @see LIBMTP_Number_Devices_In_List()
*/
LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_list)
{
LIBMTP_raw_device_t *devices;
int numdevs;
LIBMTP_error_number_t ret;
ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
if (ret != LIBMTP_ERROR_NONE) {
*device_list = NULL;
return ret;
}
/* Assign linked list of devices */
if (devices == NULL || numdevs == 0) {
*device_list = NULL;
return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
}
*device_list = create_usb_mtp_devices(devices, numdevs);
free(devices);
/* TODO: Add wifi device access here */
/* We have found some devices but create failed */
if (*device_list == NULL)
return LIBMTP_ERROR_CONNECTING;
return LIBMTP_ERROR_NONE;
}
/**
* This closes and releases an allocated MTP device.
* @param device a pointer to the MTP device to release.
*/
void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
{
if(device != NULL)
{
if(device->next != NULL)
{
LIBMTP_Release_Device_List(device->next);
}
LIBMTP_Release_Device(device);
}
}
/**
* This closes and releases an allocated MTP device.
* @param device a pointer to the MTP device to release.
*/
void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
{
PTPParams *params = (PTPParams *) device->params;
PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
close_device(ptp_usb, params);
// Clear error stack
LIBMTP_Clear_Errorstack(device);
// Free iconv() converters...
iconv_close(params->cd_locale_to_ucs2);
iconv_close(params->cd_ucs2_to_locale);
free(ptp_usb);
ptp_free_params(params);
free(params);
free_storage_list(device);
// Free extension list...
if (device->extensions != NULL) {
LIBMTP_device_extension_t *tmp = device->extensions;
while (tmp != NULL) {
LIBMTP_device_extension_t *next = tmp->next;
if (tmp->name)
free(tmp->name);
free(tmp);
tmp = next;
}
}
free(device);
}
/**
* This can be used by any libmtp-intrinsic code that
* need to stack up an error on the stack. You are only
* supposed to add errors to the error stack using this
* function, do not create and reference error entries
* directly.
*/
static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
LIBMTP_error_number_t errornumber,
char const * const error_text)
{
LIBMTP_error_t *newerror;
if (device == NULL) {
LIBMTP_ERROR("LIBMTP PANIC: Trying to add error to a NULL device!\n");
return;
}
newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
newerror->errornumber = errornumber;
newerror->error_text = strdup(error_text);
newerror->next = NULL;
if (device->errorstack == NULL) {
device->errorstack = newerror;
} else {
LIBMTP_error_t *tmp = device->errorstack;
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = newerror;
}
}
/**
* Adds an error from the PTP layer to the error stack.
*/
static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
uint16_t ptp_error,
char const * const error_text)
{
if (device == NULL) {
LIBMTP_ERROR("LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
return;
} else {
char outstr[256];
snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
outstr[sizeof(outstr)-1] = '\0';
add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
snprintf(outstr, sizeof(outstr), "Error %04x: %s", ptp_error, ptp_strerror(ptp_error));
outstr[sizeof(outstr)-1] = '\0';
add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
}
}
/**
* This returns the error stack for a device in case you
* need to either reference the error numbers (e.g. when
* creating multilingual apps with multiple-language text
* representations for each error number) or when you need
* to build a multi-line error text widget or something like
* that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
* to clear it when you're finished with it.
* @param device a pointer to the MTP device to get the error
* stack for.
* @return the error stack or NULL if there are no errors
* on the stack.
* @see LIBMTP_Clear_Errorstack()
* @see LIBMTP_Dump_Errorstack()
*/
LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
{
if (device == NULL) {
LIBMTP_ERROR("LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
return NULL;
}
return device->errorstack;
}
/**
* This function clears the error stack of a device and frees
* any memory used by it. Call this when you're finished with
* using the errors.
* @param device a pointer to the MTP device to clear the error
* stack for.
*/
void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
{
if (device == NULL) {
LIBMTP_ERROR("LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
} else {
LIBMTP_error_t *tmp = device->errorstack;
while (tmp != NULL) {
LIBMTP_error_t *tmp2;
if (tmp->error_text != NULL) {
free(tmp->error_text);
}
tmp2 = tmp;
tmp = tmp->next;
free(tmp2);
}
device->errorstack = NULL;
}
}
/**
* This function dumps the error stack to <code>stderr</code>.
* (You still have to clear the stack though.)
* @param device a pointer to the MTP device to dump the error
* stack for.
*/
void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
{
if (device == NULL) {
LIBMTP_ERROR("LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
} else {
LIBMTP_error_t *tmp = device->errorstack;
while (tmp != NULL) {
if (tmp->error_text != NULL) {
LIBMTP_ERROR("Error %d: %s\n", tmp->errornumber, tmp->error_text);
} else {
LIBMTP_ERROR("Error %d: (unknown)\n", tmp->errornumber);
}
tmp = tmp->next;
}
}
}
/**
* This command gets all handles and stuff by FAST directory retrieveal
* which is available by getting all metadata for object
* <code>0xffffffff</code> which simply means "all metadata for all objects".
* This works on the vast majority of MTP devices (there ARE exceptions!)
* and is quite quick. Check the error stack to see if there were
* problems getting the metadata.
* @return 0 if all was OK, -1 on failure.
*/
static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
{
PTPParams *params = (PTPParams *) device->params;
int cnt = 0;
int i, j, nrofprops;
uint32_t lasthandle = 0xffffffff;
MTPProperties *props = NULL;
MTPProperties *prop;
uint16_t ret;
int oldtimeout;
PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
/*
* The follow request causes the device to generate
* a list of every file on the device and return it
* in a single response.
*
* Some slow devices as well as devices with very
* large file systems can easily take longer then
* the standard timeout value before it is able
* to return a response.
*
* Temporarly set timeout to allow working with
* widest range of devices.
*/
get_usb_device_timeout(ptp_usb, &oldtimeout);
set_usb_device_timeout(ptp_usb, 60000);
ret = ptp_mtp_getobjectproplist(params, 0xffffffff, &props, &nrofprops);
set_usb_device_timeout(ptp_usb, oldtimeout);
if (ret == PTP_RC_MTP_Specification_By_Group_Unsupported) {
// What's the point in the device implementing this command if
// you cannot use it to get all props for AT LEAST one object?
// Well, whatever...
add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
"cannot retrieve all metadata for an object on this device.");
return -1;
}
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
"could not get proplist of all objects.");
return -1;
}
if (props == NULL && nrofprops != 0) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
"get_all_metadata_fast(): "
"call to ptp_mtp_getobjectproplist() returned "
"inconsistent results.");
return -1;
}
/*
* We count the number of objects by counting the ObjectHandle
* references, whenever it changes we get a new object, when it's
* the same, it is just different properties of the same object.
*/
prop = props;
for (i=0;i<nrofprops;i++) {
if (lasthandle != prop->ObjectHandle) {
cnt++;
lasthandle = prop->ObjectHandle;
}
prop++;
}
lasthandle = 0xffffffff;
params->objects = calloc (sizeof(PTPObject),cnt);
prop = props;
i = -1;
for (j=0;j<nrofprops;j++) {
if (lasthandle != prop->ObjectHandle) {
if (i >= 0) {
params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
if (!params->objects[i].oi.Filename) {
/* I have one such file on my Creative (Marcus) */
params->objects[i].oi.Filename = strdup("<null>");
}
}
i++;
lasthandle = prop->ObjectHandle;
params->objects[i].oid = prop->ObjectHandle;
}
switch (prop->property) {
case PTP_OPC_ParentObject:
params->objects[i].oi.ParentObject = prop->propval.u32;
params->objects[i].flags |= PTPOBJECT_PARENTOBJECT_LOADED;
break;
case PTP_OPC_ObjectFormat:
params->objects[i].oi.ObjectFormat = prop->propval.u16;
break;
case PTP_OPC_ObjectSize:
// We loose precision here, up to 32 bits! However the commands that
// retrieve metadata for files and tracks will make sure that the
// PTP_OPC_ObjectSize is read in and duplicated again.
if (device->object_bitsize == 64) {
params->objects[i].oi.ObjectCompressedSize = (uint32_t) prop->propval.u64;
} else {
params->objects[i].oi.ObjectCompressedSize = prop->propval.u32;
}
break;
case PTP_OPC_StorageID:
params->objects[i].oi.StorageID = prop->propval.u32;
params->objects[i].flags |= PTPOBJECT_STORAGEID_LOADED;
break;
case PTP_OPC_ObjectFileName:
if (prop->propval.str != NULL)
params->objects[i].oi.Filename = strdup(prop->propval.str);
break;
default: {
MTPProperties *newprops;
/* Copy all of the other MTP oprierties into the per-object proplist */
if (params->objects[i].nrofmtpprops) {
newprops = realloc(params->objects[i].mtpprops,
(params->objects[i].nrofmtpprops+1)*sizeof(MTPProperties));
} else {
newprops = calloc(sizeof(MTPProperties),1);
}
if (!newprops) return 0; /* FIXME: error handling? */
params->objects[i].mtpprops = newprops;
memcpy(&params->objects[i].mtpprops[params->objects[i].nrofmtpprops],
&props[j],sizeof(props[j]));
params->objects[i].nrofmtpprops++;
params->objects[i].flags |= PTPOBJECT_MTPPROPLIST_LOADED;
break;
}
}
prop++;
}
/* mark last entry also */
params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
params->nrofobjects = i+1;
/* The device might not give the list in linear ascending order */
ptp_objects_sort (params);
return 0;
}
/**
* This function will recurse through all the directories on the device,
* starting at the root directory, gathering metadata as it moves along.
* It works better on some devices that will only return data for a
* certain directory and does not respect the option to get all metadata
* for all objects.
*/
static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
PTPParams *params,
uint32_t storageid,
uint32_t parent)
{
PTPObjectHandles currentHandles;
int i = 0;
uint16_t ret = ptp_getobjecthandles(params,
storageid,
PTP_GOH_ALL_FORMATS,
parent,
&currentHandles);
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "get_handles_recursively(): could not get object handles.");
return;
}
if (currentHandles.Handler == NULL || currentHandles.n == 0)
return;
// Now descend into any subdirectories found
for (i = 0; i < currentHandles.n; i++) {
PTPObject *ob;
ret = ptp_object_want(params,currentHandles.Handler[i],
PTPOBJECT_OBJECTINFO_LOADED, &ob);
if (ret == PTP_RC_OK) {
if (ob->oi.ObjectFormat == PTP_OFC_Association)
get_handles_recursively(device, params,
storageid, currentHandles.Handler[i]);
} else {