blob: 3eaf5bc1846ccb351f3c2eb3a9e00c3617ff13ae [file] [log] [blame]
Linus Walleij10c58422006-06-02 08:51:53 +00001/**
2 * \file libmtp.c
Linus Walleij2f45d222007-02-02 22:47:39 +00003 *
4 * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
5 * Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
Linus Walleij2d3f7b82007-02-14 09:24:20 +00006 * Copyright (C) 2007 Ted Bullock
Linus Walleij2f45d222007-02-02 22:47:39 +00007 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
Linus Walleij10c58422006-06-02 08:51:53 +000023 * This file provides an interface "glue" to the underlying
24 * PTP implementation from libgphoto2. It uses some local
25 * code to convert from/to UTF-8 (stored in unicode.c/.h)
26 * and some small utility functions, mainly for debugging
27 * (stored in util.c/.h).
28 *
mopoke96143402006-10-30 04:37:26 +000029 * The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
Linus Walleij10c58422006-06-02 08:51:53 +000030 * plain copied from the libhphoto2 codebase.
31 *
32 * The files libusb-glue.c/.h are just what they say: an
33 * interface to libusb for the actual, physical USB traffic.
34 */
Linus Walleij2f45d222007-02-02 22:47:39 +000035
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000036#include "libmtp.h"
Linus Walleijb9256fd2006-02-15 09:40:43 +000037#include "unicode.h"
Linus Walleij394bbbe2006-02-22 16:10:53 +000038#include "ptp.h"
Linus Walleij15e344f2006-03-06 15:15:00 +000039#include "libusb-glue.h"
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000040
Linus Walleij7beba572006-11-29 08:56:12 +000041#include <string.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <fcntl.h>
45#ifdef _MSC_VER // For MSVC++
46#define USE_WINDOWS_IO_H
47#include <io.h>
48#endif
49
Linus Walleij3fcfea52006-11-13 07:07:36 +000050/* Enable enhanced MTP commands */
51#define ENABLE_MTP_ENHANCED
52
Linus Walleijc86afbd2006-05-04 19:05:24 +000053/*
54 * This is a mapping between libmtp internal MTP filetypes and
55 * the libgphoto2/PTP equivalent defines. We need this because
56 * otherwise the libmtp.h device has to be dependent on ptp.h
57 * to be installed too, and we don't want that.
58 */
Linus Walleijcf42f452006-11-28 08:32:49 +000059//typedef struct filemap_struct filemap_t;
60typedef struct filemap_struct {
raveloxd9a28642006-05-26 23:42:22 +000061 char *description; /**< Text description for the file type */
62 LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
63 uint16_t ptp_id; /**< PTP ID for the filetype */
Linus Walleijcf42f452006-11-28 08:32:49 +000064 struct filemap_struct *next;
65} filemap_t;
Linus Walleijc86afbd2006-05-04 19:05:24 +000066
Linus Walleijf67bca92006-05-29 09:33:39 +000067// Global variables
Linus Walleij438bd7f2006-06-08 11:35:44 +000068// This holds the global filetype mapping table
Linus Walleijcf42f452006-11-28 08:32:49 +000069static filemap_t *filemap = NULL;
Linus Walleijf67bca92006-05-29 09:33:39 +000070
Linus Walleijcf42f452006-11-28 08:32:49 +000071/*
72 * Forward declarations of local (static) functions.
73 */
74static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
75 uint16_t const ptp_id);
Linus Walleij7beba572006-11-29 08:56:12 +000076static void init_filemap();
Linus Walleij2715c442007-01-20 22:35:29 +000077static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
78 LIBMTP_error_number_t errornumber,
79 char const * const error_text);
Linus Walleij070e9b42007-01-22 08:49:28 +000080static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
81 uint16_t ptp_error,
82 char const * const error_text);
Linus Walleij438bd7f2006-06-08 11:35:44 +000083static void flush_handles(LIBMTP_mtpdevice_t *device);
Linus Walleij9e1b0812006-12-12 19:22:02 +000084static void free_storage_list(LIBMTP_mtpdevice_t *device);
85static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
86static uint32_t get_first_storageid(LIBMTP_mtpdevice_t *device);
87static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device,uint64_t *freespace);
Linus Walleijf5fcda32006-12-03 22:31:02 +000088static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize);
Linus Walleijf67bca92006-05-29 09:33:39 +000089static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
90static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
mopoke96143402006-10-30 04:37:26 +000091static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +000092 char **unicstring, uint16_t property);
Linus Walleij9901e222006-11-30 12:28:19 +000093static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
94 uint16_t const attribute_id);
95static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
96 uint16_t const attribute_id, uint32_t const value_default);
97static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
98 uint16_t const attribute_id, uint16_t const value_default);
99static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
100 uint16_t const attribute_id, uint8_t const value_default);
101static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
102 uint16_t const attribute_id, char const * const string);
103static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
104 uint16_t const attribute_id, uint32_t const value);
105static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
106 uint16_t const attribute_id, uint16_t const value);
107static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
108 uint16_t const attribute_id, uint8_t const value);
Linus Walleij8ab54262006-06-21 07:12:28 +0000109static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
110 LIBMTP_track_t *track);
Linus Walleij7beba572006-11-29 08:56:12 +0000111static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
112 char const * const name,
113 uint32_t const parenthandle,
114 uint16_t const objectformat,
115 char const * const suffix,
116 uint32_t * const newid,
117 uint32_t const * const tracks,
118 uint32_t const no_tracks);
Linus Walleij3fcfea52006-11-13 07:07:36 +0000119static MTPPropList *New_MTP_Prop_Entry();
120static void Destroy_MTP_Prop_Entry(MTPPropList *prop);
raveloxd9a28642006-05-26 23:42:22 +0000121
Linus Walleijcf42f452006-11-28 08:32:49 +0000122/**
123 * Create a new file mapping entry
124 * @return a newly allocated filemapping entry.
125 */
126static filemap_t *new_filemap_entry()
raveloxd9a28642006-05-26 23:42:22 +0000127{
Linus Walleijcf42f452006-11-28 08:32:49 +0000128 filemap_t *filemap;
mopoke96143402006-10-30 04:37:26 +0000129
Linus Walleijcf42f452006-11-28 08:32:49 +0000130 filemap = (filemap_t *)malloc(sizeof(filemap_t));
mopoke96143402006-10-30 04:37:26 +0000131
Linus Walleijf67bca92006-05-29 09:33:39 +0000132 if( filemap != NULL ) {
133 filemap->description = NULL;
134 filemap->id = LIBMTP_FILETYPE_UNKNOWN;
135 filemap->ptp_id = PTP_OFC_Undefined;
Linus Walleijf67bca92006-05-29 09:33:39 +0000136 filemap->next = NULL;
137 }
mopoke96143402006-10-30 04:37:26 +0000138
Linus Walleijf67bca92006-05-29 09:33:39 +0000139 return filemap;
raveloxd9a28642006-05-26 23:42:22 +0000140}
141
raveloxd9a28642006-05-26 23:42:22 +0000142/**
143 * Register an MTP or PTP filetype for data retrieval
144 *
145 * @param description Text description of filetype
Linus Walleijf0f3d482006-05-29 14:10:21 +0000146 * @param id libmtp internal filetype id
raveloxd9a28642006-05-26 23:42:22 +0000147 * @param ptp_id PTP filetype id
Linus Walleijf0f3d482006-05-29 14:10:21 +0000148 * @return 0 for success any other value means error.
raveloxd9a28642006-05-26 23:42:22 +0000149*/
Linus Walleijcf42f452006-11-28 08:32:49 +0000150static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
151 uint16_t const ptp_id)
raveloxd9a28642006-05-26 23:42:22 +0000152{
Linus Walleijcf42f452006-11-28 08:32:49 +0000153 filemap_t *new = NULL, *current;
raveloxd9a28642006-05-26 23:42:22 +0000154
155 // Has this LIBMTP filetype been registered before ?
156 current = filemap;
157 while (current != NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000158 if(current->id == id) {
159 break;
160 }
Linus Walleijf0f3d482006-05-29 14:10:21 +0000161 current = current->next;
raveloxd9a28642006-05-26 23:42:22 +0000162 }
mopoke96143402006-10-30 04:37:26 +0000163
raveloxd9a28642006-05-26 23:42:22 +0000164 // Create the entry
165 if(current == NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000166 new = new_filemap_entry();
Linus Walleijf0f3d482006-05-29 14:10:21 +0000167 if(new == NULL) {
168 return 1;
169 }
mopoke96143402006-10-30 04:37:26 +0000170
Linus Walleijf67bca92006-05-29 09:33:39 +0000171 new->id = id;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000172 if(description != NULL) {
173 new->description = strdup(description);
174 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000175 new->ptp_id = ptp_id;
mopoke96143402006-10-30 04:37:26 +0000176
Linus Walleijf67bca92006-05-29 09:33:39 +0000177 // Add the entry to the list
178 if(filemap == NULL) {
179 filemap = new;
180 } else {
181 current = filemap;
182 while (current->next != NULL ) current=current->next;
183 current->next = new;
184 }
185 // Update the existing entry
raveloxd9a28642006-05-26 23:42:22 +0000186 } else {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000187 if (current->description != NULL) {
188 free(current->description);
189 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000190 current->description = NULL;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000191 if(description != NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000192 current->description = strdup(description);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000193 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000194 current->ptp_id = ptp_id;
raveloxd9a28642006-05-26 23:42:22 +0000195 }
196
Linus Walleijf0f3d482006-05-29 14:10:21 +0000197 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000198}
199
raveloxd9a28642006-05-26 23:42:22 +0000200static void init_filemap()
201{
Linus Walleij5fb47132006-12-30 15:35:48 +0000202 register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
Linus Walleijcf42f452006-11-28 08:32:49 +0000203 register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
Linus Walleij5fb47132006-12-30 15:35:48 +0000204 register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
205 register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
Linus Walleijcf42f452006-11-28 08:32:49 +0000206 register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
207 register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000208 register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
209 register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
210 register_filetype("MPEG-4 Part 14 Container Format (Audio Empahsis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
211 register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Empahsis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
Linus Walleijcf42f452006-11-28 08:32:49 +0000212 register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
Linus Walleijcf42f452006-11-28 08:32:49 +0000213 register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
214 register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
215 register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
216 register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
217 register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
218 register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
219 register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
220 register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000221 register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
222 register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
Linus Walleijcf42f452006-11-28 08:32:49 +0000223 register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
224 register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
225 register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
226 register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
227 register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
228 register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
229 register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
230 register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
231 register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
232 register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
233 register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
234 register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
235 register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
236 register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
Linus Walleij5fb47132006-12-30 15:35:48 +0000237 register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
238 register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
239 register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
240 register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
241 register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
Linus Walleija05b9802006-12-08 08:50:30 +0000242 register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
Linus Walleijcf42f452006-11-28 08:32:49 +0000243 register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
raveloxd9a28642006-05-26 23:42:22 +0000244}
Linus Walleij16c51f02006-05-04 13:20:22 +0000245
Linus Walleij16c51f02006-05-04 13:20:22 +0000246/**
247 * Returns the PTP filetype that maps to a certain libmtp internal file type.
248 * @param intype the MTP library interface type
249 * @return the PTP (libgphoto2) interface type
250 */
251static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
252{
Linus Walleijcf42f452006-11-28 08:32:49 +0000253 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000254
raveloxd9a28642006-05-26 23:42:22 +0000255 current = filemap;
256
257 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000258 if(current->id == intype) {
259 return current->ptp_id;
260 }
261 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000262 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000263 // printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000264 return PTP_OFC_Undefined;
265}
266
267
268/**
Linus Walleij438bd7f2006-06-08 11:35:44 +0000269 * Returns the PTP internal filetype that maps to a certain libmtp
270 * interface file type.
mopoke96143402006-10-30 04:37:26 +0000271 * @param intype the PTP (libgphoto2) interface type
Linus Walleij8ab54262006-06-21 07:12:28 +0000272 * @return the MTP library interface type
Linus Walleij16c51f02006-05-04 13:20:22 +0000273 */
274static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
275{
Linus Walleijcf42f452006-11-28 08:32:49 +0000276 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000277
raveloxd9a28642006-05-26 23:42:22 +0000278 current = filemap;
279
280 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000281 if(current->ptp_id == intype) {
282 return current->id;
283 }
284 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000285 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000286 // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000287 return LIBMTP_FILETYPE_UNKNOWN;
288}
289
Linus Walleijfa1374c2006-02-27 07:41:46 +0000290
Linus Walleij6946ac52006-03-21 06:51:22 +0000291/**
Linus Walleijf0f3d482006-05-29 14:10:21 +0000292 * Initialize the library. You are only supposed to call this
293 * one, before using the library for the first time in a program.
294 * Never re-initialize libmtp!
295 *
raveloxd9a28642006-05-26 23:42:22 +0000296 * The only thing this does at the moment is to initialise the
297 * filetype mapping table.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000298 */
299void LIBMTP_Init(void)
300{
raveloxd9a28642006-05-26 23:42:22 +0000301 init_filemap();
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000302 return;
303}
304
Linus Walleijcf42f452006-11-28 08:32:49 +0000305
306/**
307 * This helper function returns a textual description for a libmtp
308 * file type to be used in dialog boxes etc.
309 * @param intype the libmtp internal filetype to get a description for.
310 * @return a string representing the filetype, this must <b>NOT</b>
311 * be free():ed by the caller!
312 */
313char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
314{
315 filemap_t *current;
316
317 current = filemap;
318
319 while (current != NULL) {
320 if(current->id == intype) {
321 return current->description;
322 }
323 current = current->next;
324 }
325
326 return "Unknown filetype";
327}
328
329
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000330/**
raveloxd9a28642006-05-26 23:42:22 +0000331 * Retrieves a string from an object
332 *
333 * @param device a pointer to an MTP device.
334 * @param object_id Object reference
335 * @param attribute_id PTP attribute ID
Linus Walleij438bd7f2006-06-08 11:35:44 +0000336 * @return valid string or NULL on failure. The returned string
337 * must bee <code>free()</code>:ed by the caller after
338 * use.
raveloxd9a28642006-05-26 23:42:22 +0000339 */
Linus Walleij9901e222006-11-30 12:28:19 +0000340static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000341 uint16_t const attribute_id)
raveloxd9a28642006-05-26 23:42:22 +0000342{
343 PTPPropertyValue propval;
344 char *retstring = NULL;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000345 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000346 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +0000347
Linus Walleij438bd7f2006-06-08 11:35:44 +0000348 if ( device == NULL || object_id == 0) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000349 return NULL;
350 }
mopoke96143402006-10-30 04:37:26 +0000351
Linus Walleija823a702006-08-27 21:27:46 +0000352 ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
raveloxd9a28642006-05-26 23:42:22 +0000353 if (ret == PTP_RC_OK) {
Linus Walleija823a702006-08-27 21:27:46 +0000354 if (propval.str != NULL) {
355 retstring = (char *) strdup(propval.str);
356 free(propval.str);
raveloxd9a28642006-05-26 23:42:22 +0000357 }
Linus Walleij070e9b42007-01-22 08:49:28 +0000358 } else {
359 add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
raveloxd9a28642006-05-26 23:42:22 +0000360 }
mopoke96143402006-10-30 04:37:26 +0000361
raveloxd9a28642006-05-26 23:42:22 +0000362 return retstring;
363}
364
365/**
366 * Retrieves an unsigned 32-bit integer from an object attribute
367 *
368 * @param device a pointer to an MTP device.
369 * @param object_id Object reference
370 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +0000371 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +0000372 * @return the value
raveloxd9a28642006-05-26 23:42:22 +0000373 */
Linus Walleij9901e222006-11-30 12:28:19 +0000374static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000375 uint16_t const attribute_id, uint32_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +0000376{
377 PTPPropertyValue propval;
378 uint32_t retval = value_default;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000379 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000380 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +0000381
Linus Walleijf0f3d482006-05-29 14:10:21 +0000382 if ( device == NULL ) {
383 return value_default;
384 }
raveloxd9a28642006-05-26 23:42:22 +0000385
raveloxd9a28642006-05-26 23:42:22 +0000386 ret = ptp_mtp_getobjectpropvalue(params, object_id,
387 attribute_id,
388 &propval,
389 PTP_DTC_UINT32);
390 if (ret == PTP_RC_OK) {
391 retval = propval.u32;
Linus Walleij070e9b42007-01-22 08:49:28 +0000392 } else {
393 add_ptp_error_to_errorstack(device, ret, "get_u32_from_object(): could not get unsigned 32bit integer from object.");
raveloxd9a28642006-05-26 23:42:22 +0000394 }
395
396 return retval;
397}
398
399/**
400 * Retrieves an unsigned 16-bit integer from an object attribute
401 *
402 * @param device a pointer to an MTP device.
403 * @param object_id Object reference
404 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +0000405 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +0000406 * @return a value
raveloxd9a28642006-05-26 23:42:22 +0000407 */
Linus Walleij9901e222006-11-30 12:28:19 +0000408static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000409 uint16_t const attribute_id, uint16_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +0000410{
411 PTPPropertyValue propval;
412 uint16_t retval = value_default;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000413 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000414 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000415
Linus Walleijf0f3d482006-05-29 14:10:21 +0000416 if ( device == NULL ) {
417 return value_default;
418 }
raveloxd9a28642006-05-26 23:42:22 +0000419
raveloxd9a28642006-05-26 23:42:22 +0000420 ret = ptp_mtp_getobjectpropvalue(params, object_id,
421 attribute_id,
422 &propval,
423 PTP_DTC_UINT16);
424 if (ret == PTP_RC_OK) {
425 retval = propval.u16;
Linus Walleij070e9b42007-01-22 08:49:28 +0000426 } else {
427 add_ptp_error_to_errorstack(device, ret, "get_u16_from_object(): could not get unsigned 16bit integer from object.");
raveloxd9a28642006-05-26 23:42:22 +0000428 }
mopoke96143402006-10-30 04:37:26 +0000429
raveloxd9a28642006-05-26 23:42:22 +0000430 return retval;
431}
432
433/**
Linus Walleij99310d42006-11-01 08:29:39 +0000434 * Retrieves an unsigned 8-bit integer from an object attribute
435 *
436 * @param device a pointer to an MTP device.
437 * @param object_id Object reference
438 * @param attribute_id PTP attribute ID
439 * @param value_default Default value to return on failure
440 * @return a value
441 */
Linus Walleij9901e222006-11-30 12:28:19 +0000442static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij99310d42006-11-01 08:29:39 +0000443 uint16_t const attribute_id, uint8_t const value_default)
444{
445 PTPPropertyValue propval;
446 uint8_t retval = value_default;
447 PTPParams *params = (PTPParams *) device->params;
448 uint16_t ret;
449
450 if ( device == NULL ) {
451 return value_default;
452 }
453
454 ret = ptp_mtp_getobjectpropvalue(params, object_id,
455 attribute_id,
456 &propval,
457 PTP_DTC_UINT8);
458 if (ret == PTP_RC_OK) {
459 retval = propval.u8;
Linus Walleij070e9b42007-01-22 08:49:28 +0000460 } else {
461 add_ptp_error_to_errorstack(device, ret, "get_u8_from_object(): could not get unsigned 8bit integer from object.");
Linus Walleij99310d42006-11-01 08:29:39 +0000462 }
463
464 return retval;
465}
466
467/**
raveloxd9a28642006-05-26 23:42:22 +0000468 * Sets an object attribute from a string
469 *
470 * @param device a pointer to an MTP device.
471 * @param object_id Object reference
472 * @param attribute_id PTP attribute ID
473 * @param string string value to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000474 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000475 */
Linus Walleij9901e222006-11-30 12:28:19 +0000476static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000477 uint16_t const attribute_id, char const * const string)
raveloxd9a28642006-05-26 23:42:22 +0000478{
479 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000480 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000481 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000482
Linus Walleijf0f3d482006-05-29 14:10:21 +0000483 if (device == NULL || string == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000484 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000485 }
raveloxd9a28642006-05-26 23:42:22 +0000486
Linus Walleija823a702006-08-27 21:27:46 +0000487 propval.str = (char *) string;
488 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000489 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000490 add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
Linus Walleij438bd7f2006-06-08 11:35:44 +0000491 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000492 }
mopoke96143402006-10-30 04:37:26 +0000493
Linus Walleijf0f3d482006-05-29 14:10:21 +0000494 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000495}
496
497/**
498 * Sets an object attribute from an unsigned 32-bit integer
499 *
500 * @param device a pointer to an MTP device.
501 * @param object_id Object reference
502 * @param attribute_id PTP attribute ID
503 * @param value 32-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000504 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000505 */
Linus Walleij9901e222006-11-30 12:28:19 +0000506static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000507 uint16_t const attribute_id, uint32_t const value)
raveloxd9a28642006-05-26 23:42:22 +0000508{
509 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000510 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000511 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000512
Linus Walleijf0f3d482006-05-29 14:10:21 +0000513 if (device == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000514 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000515 }
raveloxd9a28642006-05-26 23:42:22 +0000516
517 propval.u32 = value;
518 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000519 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000520 add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
Linus Walleij438bd7f2006-06-08 11:35:44 +0000521 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000522 }
mopoke96143402006-10-30 04:37:26 +0000523
Linus Walleijf0f3d482006-05-29 14:10:21 +0000524 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000525}
526
527/**
528 * Sets an object attribute from an unsigned 16-bit integer
529 *
530 * @param device a pointer to an MTP device.
531 * @param object_id Object reference
532 * @param attribute_id PTP attribute ID
533 * @param value 16-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000534 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000535 */
Linus Walleij9901e222006-11-30 12:28:19 +0000536static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000537 uint16_t const attribute_id, uint16_t const value)
raveloxd9a28642006-05-26 23:42:22 +0000538{
539 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000540 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000541 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000542
Linus Walleijf0f3d482006-05-29 14:10:21 +0000543 if (device == NULL) {
544 return 1;
545 }
raveloxd9a28642006-05-26 23:42:22 +0000546
547 propval.u16 = value;
548 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000549 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000550 add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +0000551 return 1;
552 }
raveloxd9a28642006-05-26 23:42:22 +0000553
Linus Walleijf0f3d482006-05-29 14:10:21 +0000554 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000555}
556
557/**
Linus Walleij99310d42006-11-01 08:29:39 +0000558 * Sets an object attribute from an unsigned 8-bit integer
559 *
560 * @param device a pointer to an MTP device.
561 * @param object_id Object reference
562 * @param attribute_id PTP attribute ID
563 * @param value 8-bit unsigned integer to set
564 * @return 0 on success, any other value means failure
565 */
Linus Walleij9901e222006-11-30 12:28:19 +0000566static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
567 uint16_t const attribute_id, uint8_t const value)
Linus Walleij99310d42006-11-01 08:29:39 +0000568{
569 PTPPropertyValue propval;
570 PTPParams *params = (PTPParams *) device->params;
571 uint16_t ret;
572
573 if (device == NULL) {
574 return 1;
575 }
576
577 propval.u8 = value;
578 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
579 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000580 add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +0000581 return 1;
raveloxd9a28642006-05-26 23:42:22 +0000582 }
mopoke96143402006-10-30 04:37:26 +0000583
Linus Walleijf0f3d482006-05-29 14:10:21 +0000584 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000585}
586
raveloxd9a28642006-05-26 23:42:22 +0000587/**
Linus Walleij039d1dd2007-02-23 22:34:07 +0000588 * Get the first (as in "first in the list of") connected MTP device.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000589 * @return a device pointer.
Linus Walleij039d1dd2007-02-23 22:34:07 +0000590 * @see LIBMTP_Get_Connected_Devices()
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000591 */
Linus Walleijb9256fd2006-02-15 09:40:43 +0000592LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000593{
tedbullocke7713642007-02-18 23:10:09 +0000594 LIBMTP_mtpdevice_t *first_device = NULL;
595
tedbullocke7713642007-02-18 23:10:09 +0000596 switch(LIBMTP_Get_Connected_Devices(&first_device))
597 {
598 /* Specific Errors or Messages that connect_mtp_devices should return */
tedbullock433d2172007-02-23 22:39:12 +0000599 case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
tedbullocke7713642007-02-18 23:10:09 +0000600 fprintf(stderr, "LIBMTP_Get_First_Device: No Devices Attached\n");
Richard Low0f794762007-02-23 22:06:27 +0000601 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000602
603 case LIBMTP_ERROR_CONNECTING:
604 fprintf(stderr, "LIBMTP_Get_First_Device: Error Connecting\n");
Richard Low0f794762007-02-23 22:06:27 +0000605 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000606
607 case LIBMTP_ERROR_MEMORY_ALLOCATION:
608 fprintf(stderr, "LIBMTP_Get_First_Device: Memory Alloc Error\n");
Richard Low0f794762007-02-23 22:06:27 +0000609 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000610
611 /* Unknown general errors - This should never execute */
612 case LIBMTP_ERROR_GENERAL:
613 default:
614 fprintf(stderr, "LIBMTP_Get_First_Device: Unknown Connection Error\n");
Richard Low0f794762007-02-23 22:06:27 +0000615 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000616
617 /* Successfully connect at least one device, so continue */
618 case LIBMTP_ERROR_NONE:
tedbullocke7713642007-02-18 23:10:09 +0000619 break;
Linus Walleija8a19cc2007-02-02 22:13:17 +0000620 }
tedbullock44b0ff72007-02-22 22:20:28 +0000621
622 /* Only return the first device, release the rest */
623 if(first_device->next != NULL)
624 {
625 LIBMTP_Release_Device_List(first_device->next);
626 first_device->next = NULL;
627 }
tedbullocke7713642007-02-18 23:10:09 +0000628
629 return first_device;
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000630}
631
632/**
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000633 * Recursive function that adds MTP devices to a linked list
634 * @param The number of detected USB devices
635 * @param Dynamic array of interface numbers
636 * @param Dynamic array of PTP parameters
637 * @param Dynamic array of USB PTP devices
638 * @param The device number currently being created in relation to numdevices
639 * @return a device pointer to a newly created mtpdevice (used in linked
640 * list creation
641 */
642static LIBMTP_mtpdevice_t * create_usb_mtp_devices(uint8_t numdevices,
tedbullock0f033cb2007-02-14 20:56:54 +0000643 uint8_t interface_number[],
tedbullock69a445b2007-02-15 07:41:04 +0000644 PTPParams *params[],
645 PTP_USB *ptp_usb[],
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000646 uint8_t current_device)
647{
648 /* Check if there are devices left to connect */
649 if(current_device < numdevices)
Linus Walleij68b19c02007-02-15 11:50:37 +0000650 {
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000651 LIBMTP_mtpdevice_t *mtp_device;
652 uint32_t i;
653
654 /* Clear any handlers */
655 params[current_device]->handles.Handler = NULL;
656
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000657 /* Allocate dynamic space for our device */
658 mtp_device = (LIBMTP_mtpdevice_t *)malloc(sizeof(LIBMTP_mtpdevice_t));
659
660 /* Check if there was a memory allocation error */
661 if(mtp_device == NULL)
662 {
663 /* There has been an memory allocation error. We are going to ignore this
664 device and attempt to continue */
665
666 /* TODO: This error statement could probably be a bit more robust */
Linus Walleij68b19c02007-02-15 11:50:37 +0000667 fprintf(stderr, "LIBMTP PANIC: connect_usb_devices encountered a memory "
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000668 "allocation error with device %u, trying to continue",
669 current_device);
670
671 /* Prevent memory leaks for this device */
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000672 free(ptp_usb[current_device]);
673 ptp_usb[current_device] = NULL;
674
675 free(params[current_device]);
676 params[current_device] = NULL;
677
678 /* We have freed a bit of memory so try again with the next device */
679 return create_usb_mtp_devices(numdevices,
680 interface_number,
681 params,
682 ptp_usb,
683 current_device + 1);
684 }
685
tedbullock69a445b2007-02-15 07:41:04 +0000686 /* Copy device information to mtp_device structure */
687 mtp_device->interface_number = interface_number[current_device];
688 mtp_device->params = params[current_device];
689 mtp_device->usbinfo = ptp_usb[current_device];
690
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000691 /* Cache the device information for later use */
692 if (ptp_getdeviceinfo(params[current_device],
693 &params[current_device]->deviceinfo) != PTP_RC_OK)
694 {
Linus Walleij68b19c02007-02-15 11:50:37 +0000695 fprintf(stderr, "LIBMTP PANIC: Unable to read device information on device "
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000696 "number %u, trying to continue", current_device);
697
698 /* Prevent memory leaks for this device */
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000699 free(ptp_usb[current_device]);
700 ptp_usb[current_device] = NULL;
701
702 free(params[current_device]);
703 params[current_device] = NULL;
tedbullock69a445b2007-02-15 07:41:04 +0000704 free(mtp_device);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000705
706 /* try again with the next device */
707 return create_usb_mtp_devices(numdevices,
708 interface_number,
709 params,
710 ptp_usb,
711 current_device + 1);
712 }
713
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000714 /* No Errors yet for this device */
715 mtp_device->errorstack = NULL;
716
tedbullock0f033cb2007-02-14 20:56:54 +0000717 /* Cache the device information */
718 if (ptp_getdeviceinfo(params[current_device],
719 &params[current_device]->deviceinfo) != PTP_RC_OK)
720 {
721 add_error_to_errorstack(mtp_device,
Linus Walleij68b19c02007-02-15 11:50:37 +0000722 LIBMTP_ERROR_CONNECTING,
723 "Unable to read device information. Recommend "
724 "disconnecting this device\n");
tedbullock0f033cb2007-02-14 20:56:54 +0000725 }
726
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000727 /* Default Max Battery Level, we will adjust this if possible */
728 mtp_device->maximum_battery_level = 100;
729
730 /* Check if device supports reading maximum battery level */
731 if(ptp_property_issupported( params[current_device],
732 PTP_DPC_BatteryLevel))
733 {
734 PTPDevicePropDesc dpd;
735
736 /* Try to read maximum battery level */
737 if(ptp_getdevicepropdesc( params[current_device],
738 PTP_DPC_BatteryLevel,
739 &dpd) != PTP_RC_OK)
740 {
741 add_error_to_errorstack(mtp_device,
742 LIBMTP_ERROR_CONNECTING,
743 "Unable to read Maximum Battery Level for this "
744 "device even though the device supposedly "
745 "supports this functionality");
746 }
747
748 /* TODO: is this appropriate? */
749 /* If max battery level is 0 then leave the default, otherwise assign */
750 if (dpd.FORM.Range.MaximumValue.u8 != 0)
751 {
752 mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
753 }
754
755 ptp_free_devicepropdesc(&dpd);
756 }
757
758 /* Set all default folders to 0 (root directory) */
759 mtp_device->default_music_folder = 0;
760 mtp_device->default_playlist_folder = 0;
761 mtp_device->default_picture_folder = 0;
762 mtp_device->default_video_folder = 0;
763 mtp_device->default_organizer_folder = 0;
764 mtp_device->default_zencast_folder = 0;
765 mtp_device->default_album_folder = 0;
766 mtp_device->default_text_folder = 0;
767
768 /*
769 * Then get the handles and try to locate the default folders.
770 * This has the desired side effect of caching all handles from
771 * the device which speeds up later operations.
772 */
773 flush_handles(mtp_device);
774
775 /*
776 * Remaining directories to get the handles to.
777 * We can stop when done this to save time
778 */
779 for(i = 0; i < params[current_device]->handles.n; i++)
780 {
781 PTPObjectInfo oi;
782 uint16_t ret;
783
784 ret = ptp_getobjectinfo( params[current_device],
785 params[current_device]->handles.Handler[i],
786 &oi);
787
788 if (ret != PTP_RC_OK)
789 {
790 add_error_to_errorstack(mtp_device,
791 LIBMTP_ERROR_CONNECTING,
792 "Found a bad handle, trying to ignore it.");
793 continue;
794 }
795
796 /* Ignore handles that point to non-folders */
797 if(oi.ObjectFormat != PTP_OFC_Association)
798 continue;
799 if ( oi.Filename == NULL)
800 continue;
801
802 /* Is this the Music Folder */
803 if (!strcmp(oi.Filename, "My Music") ||
804 !strcmp(oi.Filename, "Music"))
805 {
806 mtp_device->default_music_folder =
807 params[current_device]->handles.Handler[i];
808 }
809 else if (!strcmp(oi.Filename, "My Playlists") ||
810 !strcmp(oi.Filename, "Playlists"))
811 {
812 mtp_device->default_playlist_folder =
813 params[current_device]->handles.Handler[i];
814 }
815 else if (!strcmp(oi.Filename, "My Pictures") ||
816 !strcmp(oi.Filename, "Pictures"))
817 {
818 mtp_device->default_picture_folder =
819 params[current_device]->handles.Handler[i];
820 }
821 else if (!strcmp(oi.Filename, "My Video") ||
822 !strcmp(oi.Filename, "Video"))
823 {
824 mtp_device->default_video_folder =
825 params[current_device]->handles.Handler[i];
826 }
827 else if (!strcmp(oi.Filename, "My Organizer"))
828 {
829 mtp_device->default_organizer_folder =
830 params[current_device]->handles.Handler[i];
831 }
832 else if (!strcmp(oi.Filename, "ZENcast"))
833 {
834 mtp_device->default_zencast_folder =
835 params[current_device]->handles.Handler[i];
836 }
837 else if (!strcmp(oi.Filename, "My Albums") ||
838 !strcmp(oi.Filename, "Albums"))
839 {
840 mtp_device->default_album_folder =
841 params[current_device]->handles.Handler[i];
842 }
843 else if (!strcmp(oi.Filename, "Text"))
844 {
845 mtp_device->default_text_folder =
846 params[current_device]->handles.Handler[i];
847 }
848 }
849
850 /* Set initial storage information */
851 mtp_device->storage = NULL;
852 if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1)
853 {
854 add_error_to_errorstack(mtp_device,
855 LIBMTP_ERROR_GENERAL,
856 "Get Storage information failed.");
857 }
858
859 mtp_device->next = create_usb_mtp_devices(numdevices,
860 interface_number,
861 params,
862 ptp_usb,
863 current_device + 1);
Linus Walleij68b19c02007-02-15 11:50:37 +0000864
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000865 return mtp_device;
866 }
867 /* No more devices, end recursive function */
868 else
869 return NULL;
870}
871
872/**
873 * Get the first connected MTP device node in the linked list of devices.
874 * Currently this only provides access to USB devices
875 * @param Pointer to first device (if possible), filled after function executes
876 * @return Any error information gathered from device connections
877 */
878LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **DevList)
879{
tedbullock0f033cb2007-02-14 20:56:54 +0000880 uint8_t interface_number[256];
881 uint8_t numdevices = 0;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000882 /* Dynamically allocated PTP and USB information - be sure to call free()*/
tedbullock69a445b2007-02-15 07:41:04 +0000883 PTPParams **params;
884 PTP_USB **ptp_usb;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000885
tedbullock0f033cb2007-02-14 20:56:54 +0000886 switch(find_usb_devices(&params, &ptp_usb, interface_number, &numdevices))
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000887 {
888 /* Specific Errors or Messages that connect_mtp_devices should return */
tedbullock433d2172007-02-23 22:39:12 +0000889 case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000890 *DevList = NULL;
tedbullock433d2172007-02-23 22:39:12 +0000891 return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000892 case LIBMTP_ERROR_CONNECTING:
893 *DevList = NULL;
894 return LIBMTP_ERROR_CONNECTING;
895 case LIBMTP_ERROR_MEMORY_ALLOCATION:
896 *DevList = NULL;
897 return LIBMTP_ERROR_MEMORY_ALLOCATION;
898
899 /* Unknown general errors - This should never execute */
900 case LIBMTP_ERROR_GENERAL:
901 default:
902 *DevList = NULL;
903 return LIBMTP_ERROR_GENERAL;
904
905 /* Successfully connect at least one device, so continue */
906 case LIBMTP_ERROR_NONE:;
907 }
908
909 /* Assign linked list of devices */
910 *DevList = create_usb_mtp_devices(numdevices,
tedbullock0f033cb2007-02-14 20:56:54 +0000911 interface_number,
tedbullock69a445b2007-02-15 07:41:04 +0000912 params,
913 ptp_usb,
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000914 0);
915
916 /* TODO: Add wifi device access here */
tedbullock69a445b2007-02-15 07:41:04 +0000917
918 free(params);
919 free(ptp_usb);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000920
921 return LIBMTP_ERROR_NONE;
922}
923
924/**
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000925 * This closes and releases an allocated MTP device.
Linus Walleijb9256fd2006-02-15 09:40:43 +0000926 * @param device a pointer to the MTP device to release.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000927 */
tedbullock0f033cb2007-02-14 20:56:54 +0000928void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
929{
930 if(device != NULL)
931 {
932 if(device->next != NULL)
933 {
934 LIBMTP_Release_Device_List(device->next);
935 }
936
937 LIBMTP_Release_Device(device);
938 }
939}
940
941/**
942 * This closes and releases an allocated MTP device.
943 * @param device a pointer to the MTP device to release.
944 */
Linus Walleijb9256fd2006-02-15 09:40:43 +0000945void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000946{
Linus Walleij9b28da32006-03-16 13:47:58 +0000947 PTPParams *params = (PTPParams *) device->params;
Linus Walleij2d411db2006-03-22 12:13:09 +0000948 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij9b28da32006-03-16 13:47:58 +0000949
Linus Walleij2d411db2006-03-22 12:13:09 +0000950 close_device(ptp_usb, params, device->interface_number);
Linus Walleij2715c442007-01-20 22:35:29 +0000951 // Clear error stack
952 LIBMTP_Clear_Errorstack(device);
Linus Walleij3ec86312006-08-21 13:25:24 +0000953 // Free iconv() converters...
Linus Walleija823a702006-08-27 21:27:46 +0000954 iconv_close(params->cd_locale_to_ucs2);
955 iconv_close(params->cd_ucs2_to_locale);
tedbullock69a445b2007-02-15 07:41:04 +0000956 free(ptp_usb);
Linus Walleij073c4172007-02-02 22:26:33 +0000957 ptp_free_params(params);
Linus Walleij9e1b0812006-12-12 19:22:02 +0000958 free_storage_list(device);
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000959 free(device);
960}
Linus Walleijb9256fd2006-02-15 09:40:43 +0000961
962/**
Linus Walleij2715c442007-01-20 22:35:29 +0000963 * This can be used by any libmtp-intrinsic code that
Linus Walleij68b19c02007-02-15 11:50:37 +0000964 * need to stack up an error on the stack. You are only
965 * supposed to add errors to the error stack using this
966 * function, do not create and reference error entries
967 * directly.
Linus Walleij2715c442007-01-20 22:35:29 +0000968 */
969static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
970 LIBMTP_error_number_t errornumber,
971 char const * const error_text)
972{
973 LIBMTP_error_t *newerror;
974
Linus Walleij68b19c02007-02-15 11:50:37 +0000975 if (device == NULL) {
976 fprintf(stderr, "LIBMTP PANIC: Trying to add error to a NULL device!\n");
977 return;
978 }
Linus Walleij2715c442007-01-20 22:35:29 +0000979 newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
980 newerror->errornumber = errornumber;
981 newerror->error_text = strdup(error_text);
rreardon774503c2007-02-15 15:52:49 +0000982 newerror->next = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +0000983 if (device->errorstack == NULL) {
984 device->errorstack = newerror;
985 } else {
986 LIBMTP_error_t *tmp = device->errorstack;
987
988 while (tmp->next != NULL) {
989 tmp = tmp->next;
990 }
991 tmp->next = newerror;
992 }
993}
994
995/**
Linus Walleij070e9b42007-01-22 08:49:28 +0000996 * Adds an error from the PTP layer to the error stack.
997 */
998static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
999 uint16_t ptp_error,
1000 char const * const error_text)
1001{
Linus Walleij68b19c02007-02-15 11:50:37 +00001002 if (device == NULL) {
1003 fprintf(stderr, "LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
1004 return;
1005 } else {
1006 char outstr[256];
1007 snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
1008 outstr[sizeof(outstr)-1] = '\0';
1009 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
1010 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, "(Look this up in ptp.h for an explanation.)");
1011 }
Linus Walleij070e9b42007-01-22 08:49:28 +00001012}
1013
1014/**
Linus Walleij2715c442007-01-20 22:35:29 +00001015 * This returns the error stack for a device in case you
1016 * need to either reference the error numbers (e.g. when
1017 * creating multilingual apps with multiple-language text
1018 * representations for each error number) or when you need
1019 * to build a multi-line error text widget or something like
1020 * that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
1021 * to clear it when you're finished with it.
1022 * @param device a pointer to the MTP device to get the error
1023 * stack for.
1024 * @return the error stack or NULL if there are no errors
1025 * on the stack.
1026 * @see LIBMTP_Clear_Errorstack()
1027 * @see LIBMTP_Dump_Errorstack()
1028 */
1029LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
1030{
Linus Walleij68b19c02007-02-15 11:50:37 +00001031 if (device == NULL) {
1032 fprintf(stderr, "LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
1033 }
Linus Walleij2715c442007-01-20 22:35:29 +00001034 return device->errorstack;
1035}
1036
1037/**
1038 * This function clears the error stack of a device and frees
1039 * any memory used by it. Call this when you're finished with
1040 * using the errors.
1041 * @param device a pointer to the MTP device to clear the error
1042 * stack for.
1043 */
1044void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
1045{
Linus Walleij68b19c02007-02-15 11:50:37 +00001046 if (device == NULL) {
1047 fprintf(stderr, "LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
1048 } else {
1049 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2715c442007-01-20 22:35:29 +00001050
Linus Walleij68b19c02007-02-15 11:50:37 +00001051 while (tmp != NULL) {
1052 LIBMTP_error_t *tmp2;
1053
1054 if (tmp->error_text != NULL) {
1055 free(tmp->error_text);
1056 }
1057 tmp2 = tmp;
1058 tmp = tmp->next;
1059 free(tmp2);
Linus Walleij2715c442007-01-20 22:35:29 +00001060 }
Linus Walleij68b19c02007-02-15 11:50:37 +00001061 device->errorstack = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +00001062 }
Linus Walleij2715c442007-01-20 22:35:29 +00001063}
1064
1065/**
1066 * This function dumps the error stack to <code>stderr</code>.
1067 * (You still have to clear the stack though.)
1068 * @param device a pointer to the MTP device to dump the error
1069 * stack for.
1070 */
1071void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
1072{
Linus Walleij68b19c02007-02-15 11:50:37 +00001073 if (device == NULL) {
1074 fprintf(stderr, "LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
1075 } else {
1076 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2715c442007-01-20 22:35:29 +00001077
Linus Walleij68b19c02007-02-15 11:50:37 +00001078 while (tmp != NULL) {
1079 if (tmp->error_text != NULL) {
1080 fprintf(stderr, "Error %d: %s\n", tmp->errornumber, tmp->error_text);
1081 } else {
1082 fprintf(stderr, "Error %d: (unknown)\n", tmp->errornumber);
1083 }
1084 tmp = tmp->next;
Linus Walleij2715c442007-01-20 22:35:29 +00001085 }
Linus Walleij2715c442007-01-20 22:35:29 +00001086 }
1087}
1088
1089/**
Linus Walleij438bd7f2006-06-08 11:35:44 +00001090 * This function refresh the internal handle list whenever
1091 * the items stored inside the device is altered. On operations
1092 * that do not add or remove objects, this is typically not
1093 * called.
1094 * @param device a pointer to the MTP device to flush handles for.
1095 */
1096static void flush_handles(LIBMTP_mtpdevice_t *device)
1097{
1098 PTPParams *params = (PTPParams *) device->params;
1099 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00001100
Linus Walleij438bd7f2006-06-08 11:35:44 +00001101 if (params->handles.Handler != NULL) {
1102 free(params->handles.Handler);
1103 }
1104
1105 // Get all the handles if we haven't already done that
1106 ret = ptp_getobjecthandles(params,
mopoke96143402006-10-30 04:37:26 +00001107 PTP_GOH_ALL_STORAGE,
1108 PTP_GOH_ALL_FORMATS,
1109 PTP_GOH_ALL_ASSOCS,
Linus Walleij438bd7f2006-06-08 11:35:44 +00001110 &params->handles);
1111 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001112 add_ptp_error_to_errorstack(device, ret, "flush_handles(): could not get object handles.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00001113 }
1114
1115 return;
1116}
1117
1118/**
Linus Walleij9e1b0812006-12-12 19:22:02 +00001119 * This function traverses a devices storage list freeing up the
1120 * strings and the structs.
1121 * @param device a pointer to the MTP device to free the storage
1122 * list for.
1123 */
1124static void free_storage_list(LIBMTP_mtpdevice_t *device)
1125{
Linus Walleije1ac07e2006-12-14 19:38:59 +00001126 LIBMTP_devicestorage_t *storage;
1127 LIBMTP_devicestorage_t *tmp;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001128
Linus Walleije1ac07e2006-12-14 19:38:59 +00001129 storage = device->storage;
1130 while(storage != NULL) {
1131 if (storage->StorageDescription != NULL) {
1132 free(storage->StorageDescription);
1133 }
1134 if (storage->VolumeIdentifier != NULL) {
1135 free(storage->VolumeIdentifier);
1136 }
1137 tmp = storage;
1138 storage = storage->next;
1139 free(tmp);
Linus Walleij9e1b0812006-12-12 19:22:02 +00001140 }
1141 device->storage = NULL;
1142
1143 return;
1144}
1145
1146/**
1147 * This function traverses a devices storage list freeing up the
1148 * strings and the structs.
1149 * @param device a pointer to the MTP device to free the storage
1150 * list for.
1151 */
1152static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
1153{
1154 LIBMTP_devicestorage_t *oldhead, *ptr1, *ptr2, *newlist;
1155
1156 if (device->storage == NULL)
1157 return -1;
1158 if (sortby == LIBMTP_STORAGE_SORTBY_NOTSORTED)
1159 return 0;
1160
1161 oldhead = ptr1 = ptr2 = device->storage;
1162
1163 newlist = NULL;
1164
1165 while(oldhead != NULL) {
1166 ptr1 = ptr2 = oldhead;
1167 while(ptr1 != NULL) {
1168
1169 if (sortby == LIBMTP_STORAGE_SORTBY_FREESPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
1170 ptr2 = ptr1;
1171 if (sortby == LIBMTP_STORAGE_SORTBY_MAXSPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
1172 ptr2 = ptr1;
1173
1174 ptr1 = ptr1->next;
1175 }
1176
1177 // Make our previous entries next point to our next
1178 if(ptr2->prev != NULL) {
1179 ptr1 = ptr2->prev;
1180 ptr1->next = ptr2->next;
1181 } else {
1182 oldhead = ptr2->next;
1183 if(oldhead != NULL)
1184 oldhead->prev = NULL;
1185 }
1186
1187 // Make our next entries previous point to our previous
1188 ptr1 = ptr2->next;
1189 if(ptr1 != NULL) {
1190 ptr1->prev = ptr2->prev;
1191 } else {
1192 ptr1 = ptr2->prev;
1193 if(ptr1 != NULL)
1194 ptr1->next = NULL;
1195 }
1196
1197 if(newlist == NULL) {
1198 newlist = ptr2;
1199 newlist->prev = NULL;
1200 } else {
1201 ptr2->prev = newlist;
1202 newlist->next = ptr2;
1203 newlist = newlist->next;
1204 }
1205 }
1206
1207 newlist->next = NULL;
1208 while(newlist->prev != NULL)
1209 newlist = newlist->prev;
1210
1211 device->storage = newlist;
1212
1213 return 0;
1214}
1215
1216/**
1217 * This function grabs the first storageid from the device storage
1218 * list.
1219 * @param device a pointer to the MTP device to free the storage
1220 * list for.
1221 */
1222static uint32_t get_first_storageid(LIBMTP_mtpdevice_t *device)
1223{
1224 LIBMTP_devicestorage_t *storage = device->storage;
1225 uint32_t store = 0;
1226
1227 if(storage != NULL)
1228 store = storage->id;
1229
1230 return store;
1231}
1232
1233/**
1234 * This function grabs the freespace from the first storage in
1235 * device storage list.
1236 * @param device a pointer to the MTP device to free the storage
1237 * list for.
1238 */
Linus Walleij070e9b42007-01-22 08:49:28 +00001239static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device, uint64_t *freespace)
Linus Walleij9e1b0812006-12-12 19:22:02 +00001240{
1241 LIBMTP_devicestorage_t *storage = device->storage;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001242 PTPParams *params = (PTPParams *) device->params;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001243
Linus Walleije1ac07e2006-12-14 19:38:59 +00001244 if(storage == NULL) {
Linus Walleij9e1b0812006-12-12 19:22:02 +00001245 return -1;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001246 }
1247 // Always query the device about this, since some models explicitly
1248 // needs that.
1249 if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
1250 PTPStorageInfo storageInfo;
Linus Walleij070e9b42007-01-22 08:49:28 +00001251 uint16_t ret;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001252
Linus Walleij070e9b42007-01-22 08:49:28 +00001253 ret = ptp_getstorageinfo(params, storage->id, &storageInfo);
1254 if (ret != PTP_RC_OK) {
1255 add_ptp_error_to_errorstack(device, ret, "get_first_storage_freespace(): could not get storage info.");
Linus Walleije1ac07e2006-12-14 19:38:59 +00001256 return -1;
1257 }
1258 if (storage->StorageDescription != NULL) {
1259 free(storage->StorageDescription);
1260 }
1261 if (storage->VolumeIdentifier != NULL) {
1262 free(storage->VolumeIdentifier);
1263 }
1264 storage->StorageType = storageInfo.StorageType;
1265 storage->FilesystemType = storageInfo.FilesystemType;
1266 storage->AccessCapability = storageInfo.AccessCapability;
1267 storage->MaxCapacity = storageInfo.MaxCapability;
1268 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
1269 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
1270 storage->StorageDescription = storageInfo.StorageDescription;
1271 storage->VolumeIdentifier = storageInfo.VolumeLabel;
1272 }
1273 if(storage->FreeSpaceInBytes == (uint64_t) -1)
Linus Walleij9e1b0812006-12-12 19:22:02 +00001274 return -1;
1275 *freespace = storage->FreeSpaceInBytes;
1276 return 0;
1277}
1278
1279/**
Linus Walleij8c45b292006-04-26 14:12:44 +00001280 * This function dumps out a large chunk of textual information
1281 * provided from the PTP protocol and additionally some extra
1282 * MTP-specific information where applicable.
1283 * @param device a pointer to the MTP device to report info from.
1284 */
1285void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
1286{
1287 int i;
1288 PTPParams *params = (PTPParams *) device->params;
Linus Walleijc6210fb2006-05-08 11:11:41 +00001289 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001290 LIBMTP_devicestorage_t *storage = device->storage;
mopoke96143402006-10-30 04:37:26 +00001291
Linus Walleijc6210fb2006-05-08 11:11:41 +00001292 printf("USB low-level info:\n");
1293 dump_usbinfo(ptp_usb);
Linus Walleij8c45b292006-04-26 14:12:44 +00001294 /* Print out some verbose information */
1295 printf("Device info:\n");
1296 printf(" Manufacturer: %s\n", params->deviceinfo.Manufacturer);
1297 printf(" Model: %s\n", params->deviceinfo.Model);
1298 printf(" Device version: %s\n", params->deviceinfo.DeviceVersion);
1299 printf(" Serial number: %s\n", params->deviceinfo.SerialNumber);
1300 printf(" Vendor extension ID: 0x%08x\n", params->deviceinfo.VendorExtensionID);
1301 printf(" Vendor extension description: %s\n", params->deviceinfo.VendorExtensionDesc);
1302 printf("Supported operations:\n");
1303 for (i=0;i<params->deviceinfo.OperationsSupported_len;i++) {
Linus Walleij4f40d112006-09-21 07:44:53 +00001304 char txt[256];
1305
1306 (void) ptp_render_opcode (params, params->deviceinfo.OperationsSupported[i], sizeof(txt), txt);
1307 printf(" %04x: %s\n", params->deviceinfo.OperationsSupported[i], txt);
Linus Walleij8c45b292006-04-26 14:12:44 +00001308 }
1309 printf("Events supported:\n");
1310 if (params->deviceinfo.EventsSupported_len == 0) {
1311 printf(" None.\n");
1312 } else {
1313 for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
1314 printf(" 0x%04x\n", params->deviceinfo.EventsSupported[i]);
1315 }
1316 }
1317 printf("Device Properties Supported:\n");
1318 for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
Linus Walleij16c51f02006-05-04 13:20:22 +00001319 char const *propdesc = ptp_get_property_description(params, params->deviceinfo.DevicePropertiesSupported[i]);
mopoke96143402006-10-30 04:37:26 +00001320
Linus Walleij545c7792006-06-13 15:22:30 +00001321 if (propdesc != NULL) {
1322 printf(" 0x%04x: %s\n", params->deviceinfo.DevicePropertiesSupported[i], propdesc);
1323 } else {
1324 uint16_t prop = params->deviceinfo.DevicePropertiesSupported[i];
Linus Walleijcf223e62006-06-19 09:31:53 +00001325 printf(" 0x%04x: Unknown property\n", prop);
Linus Walleij545c7792006-06-13 15:22:30 +00001326 }
Linus Walleij8c45b292006-04-26 14:12:44 +00001327 }
Linus Walleij0af979a2006-06-19 11:49:10 +00001328
1329 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
1330 printf("Playable File (Object) Types and Object Properties Supported:\n");
1331 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
1332 char txt[256];
1333 uint16_t ret;
1334 uint16_t *props = NULL;
1335 uint32_t propcnt = 0;
1336 int j;
mopoke96143402006-10-30 04:37:26 +00001337
Linus Walleij0af979a2006-06-19 11:49:10 +00001338 (void) ptp_render_ofc (params, params->deviceinfo.ImageFormats[i], sizeof(txt), txt);
1339 printf(" %04x: %s\n", params->deviceinfo.ImageFormats[i], txt);
mopoke96143402006-10-30 04:37:26 +00001340
Linus Walleij0af979a2006-06-19 11:49:10 +00001341 ret = ptp_mtp_getobjectpropssupported (params, params->deviceinfo.ImageFormats[i], &propcnt, &props);
1342 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001343 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Dump_Device_Info(): error on query for object properties.");
Linus Walleij0af979a2006-06-19 11:49:10 +00001344 } else {
1345 for (j=0;j<propcnt;j++) {
1346 (void) ptp_render_mtp_propname(props[j],sizeof(txt),txt);
1347 printf(" %04x: %s\n", props[j], txt);
1348 }
1349 free(props);
1350 }
1351 }
1352 }
mopoke96143402006-10-30 04:37:26 +00001353
Linus Walleije1ac07e2006-12-14 19:38:59 +00001354 if(storage != NULL && ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
Linus Walleij9e1b0812006-12-12 19:22:02 +00001355 printf("Storage Devices:\n");
Linus Walleije1ac07e2006-12-14 19:38:59 +00001356 while(storage != NULL) {
1357 printf(" StorageID: 0x%08x\n",storage->id);
1358 printf(" StorageType: 0x%04x\n",storage->StorageType);
1359 printf(" FilesystemType: 0x%04x\n",storage->FilesystemType);
1360 printf(" AccessCapability: 0x%04x\n",storage->AccessCapability);
1361 printf(" MaxCapacity: %lld\n",storage->MaxCapacity);
1362 printf(" FreeSpaceInBytes: %lld\n",storage->FreeSpaceInBytes);
1363 printf(" FreeSpaceInObjects: %lld\n",storage->FreeSpaceInObjects);
1364 printf(" StorageDescription: %s\n",storage->StorageDescription);
1365 printf(" VolumeIdentifier: %s\n",storage->VolumeIdentifier);
1366 storage = storage->next;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001367 }
1368 }
1369
Linus Walleij545c7792006-06-13 15:22:30 +00001370 printf("Special directories:\n");
1371 printf(" Default music folder: 0x%08x\n", device->default_music_folder);
1372 printf(" Default playlist folder: 0x%08x\n", device->default_playlist_folder);
1373 printf(" Default picture folder: 0x%08x\n", device->default_picture_folder);
1374 printf(" Default video folder: 0x%08x\n", device->default_video_folder);
1375 printf(" Default organizer folder: 0x%08x\n", device->default_organizer_folder);
1376 printf(" Default zencast folder: 0x%08x\n", device->default_zencast_folder);
Linus Walleijccf28ce2006-11-16 16:06:38 +00001377 printf(" Default album folder: 0x%08x\n", device->default_album_folder);
Linus Walleij9316e652006-12-07 09:55:21 +00001378 printf(" Default text folder: 0x%08x\n", device->default_text_folder);
Linus Walleij8c45b292006-04-26 14:12:44 +00001379}
1380
1381/**
mopoke96143402006-10-30 04:37:26 +00001382 * This retrieves the model name (often equal to product name)
Linus Walleij80124062006-03-15 10:26:09 +00001383 * of an MTP device.
1384 * @param device a pointer to the device to get the model name for.
1385 * @return a newly allocated UTF-8 string representing the model name.
1386 * The string must be freed by the caller after use. If the call
1387 * was unsuccessful this will contain NULL.
1388 */
1389char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t *device)
1390{
1391 char *retmodel = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001392 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001393
Linus Walleij9b28da32006-03-16 13:47:58 +00001394 if (params->deviceinfo.Model != NULL) {
1395 retmodel = strdup(params->deviceinfo.Model);
Linus Walleij80124062006-03-15 10:26:09 +00001396 }
1397 return retmodel;
1398}
1399
1400/**
1401 * This retrieves the serial number of an MTP device.
1402 * @param device a pointer to the device to get the serial number for.
1403 * @return a newly allocated UTF-8 string representing the serial number.
1404 * The string must be freed by the caller after use. If the call
1405 * was unsuccessful this will contain NULL.
1406 */
1407char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t *device)
1408{
1409 char *retnumber = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001410 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001411
Linus Walleij9b28da32006-03-16 13:47:58 +00001412 if (params->deviceinfo.SerialNumber != NULL) {
1413 retnumber = strdup(params->deviceinfo.SerialNumber);
Linus Walleij80124062006-03-15 10:26:09 +00001414 }
1415 return retnumber;
1416}
1417
1418/**
mopoke96143402006-10-30 04:37:26 +00001419 * This retrieves the device version (hardware and firmware version) of an
Linus Walleij80124062006-03-15 10:26:09 +00001420 * MTP device.
1421 * @param device a pointer to the device to get the device version for.
1422 * @return a newly allocated UTF-8 string representing the device version.
1423 * The string must be freed by the caller after use. If the call
1424 * was unsuccessful this will contain NULL.
1425 */
1426char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t *device)
1427{
1428 char *retversion = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001429 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001430
Linus Walleij9b28da32006-03-16 13:47:58 +00001431 if (params->deviceinfo.DeviceVersion != NULL) {
1432 retversion = strdup(params->deviceinfo.DeviceVersion);
Linus Walleij80124062006-03-15 10:26:09 +00001433 }
1434 return retversion;
1435}
1436
1437
1438/**
Linus Walleijfae27482006-08-19 20:13:25 +00001439 * This retrieves the "friendly name" of an MTP device. Usually
1440 * this is simply the name of the owner or something like
Linus Walleij30658792006-08-19 22:18:55 +00001441 * "John Doe's Digital Audio Player". This property should be supported
Linus Walleijfae27482006-08-19 20:13:25 +00001442 * by all MTP devices.
1443 * @param device a pointer to the device to get the friendly name for.
mopoke96143402006-10-30 04:37:26 +00001444 * @return a newly allocated UTF-8 string representing the friendly name.
Linus Walleijb9256fd2006-02-15 09:40:43 +00001445 * The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00001446 * @see LIBMTP_Set_Friendlyname()
Linus Walleijb9256fd2006-02-15 09:40:43 +00001447 */
Linus Walleij30658792006-08-19 22:18:55 +00001448char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t *device)
Linus Walleijb9256fd2006-02-15 09:40:43 +00001449{
Linus Walleijb02a0662006-04-25 08:05:09 +00001450 PTPPropertyValue propval;
Linus Walleijb9256fd2006-02-15 09:40:43 +00001451 char *retstring = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001452 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001453 uint16_t ret;
Linus Walleijb9256fd2006-02-15 09:40:43 +00001454
Linus Walleijcf223e62006-06-19 09:31:53 +00001455 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
1456 return NULL;
1457 }
1458
Linus Walleij070e9b42007-01-22 08:49:28 +00001459 ret = ptp_getdevicepropvalue(params,
1460 PTP_DPC_MTP_DeviceFriendlyName,
1461 &propval,
1462 PTP_DTC_STR);
1463 if (ret != PTP_RC_OK) {
1464 add_ptp_error_to_errorstack(device, ret, "Error getting friendlyname.");
Linus Walleijb9256fd2006-02-15 09:40:43 +00001465 return NULL;
1466 }
Linus Walleija823a702006-08-27 21:27:46 +00001467 if (propval.str != NULL) {
1468 retstring = strdup(propval.str);
1469 free(propval.str);
1470 }
Linus Walleijfae27482006-08-19 20:13:25 +00001471 return retstring;
1472}
1473
1474/**
Linus Walleij30658792006-08-19 22:18:55 +00001475 * Sets the "friendly name" of an MTP device.
1476 * @param device a pointer to the device to set the friendly name for.
1477 * @param friendlyname the new friendly name for the device.
1478 * @return 0 on success, any other value means failure.
1479 * @see LIBMTP_Get_Ownername()
1480 */
1481int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t *device,
1482 char const * const friendlyname)
1483{
1484 PTPPropertyValue propval;
1485 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001486 uint16_t ret;
Linus Walleij30658792006-08-19 22:18:55 +00001487
1488 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
1489 return -1;
1490 }
Linus Walleija823a702006-08-27 21:27:46 +00001491 propval.str = (char *) friendlyname;
Linus Walleij070e9b42007-01-22 08:49:28 +00001492 ret = ptp_setdevicepropvalue(params,
1493 PTP_DPC_MTP_DeviceFriendlyName,
1494 &propval,
1495 PTP_DTC_STR);
1496 if (ret != PTP_RC_OK) {
1497 add_ptp_error_to_errorstack(device, ret, "Error setting friendlyname.");
Linus Walleij30658792006-08-19 22:18:55 +00001498 return -1;
1499 }
Linus Walleij30658792006-08-19 22:18:55 +00001500 return 0;
1501}
1502
1503/**
Linus Walleijfae27482006-08-19 20:13:25 +00001504 * This retrieves the syncronization partner of an MTP device. This
1505 * property should be supported by all MTP devices.
1506 * @param device a pointer to the device to get the sync partner for.
1507 * @return a newly allocated UTF-8 string representing the synchronization
1508 * partner. The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00001509 * @see LIBMTP_Set_Syncpartner()
Linus Walleijfae27482006-08-19 20:13:25 +00001510 */
1511char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t *device)
1512{
1513 PTPPropertyValue propval;
1514 char *retstring = NULL;
1515 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001516 uint16_t ret;
Linus Walleijfae27482006-08-19 20:13:25 +00001517
1518 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
1519 return NULL;
1520 }
1521
Linus Walleij070e9b42007-01-22 08:49:28 +00001522 ret = ptp_getdevicepropvalue(params,
1523 PTP_DPC_MTP_SynchronizationPartner,
1524 &propval,
1525 PTP_DTC_STR);
1526 if (ret != PTP_RC_OK) {
1527 add_ptp_error_to_errorstack(device, ret, "Error getting syncpartner.");
Linus Walleijfae27482006-08-19 20:13:25 +00001528 return NULL;
1529 }
Linus Walleija823a702006-08-27 21:27:46 +00001530 if (propval.str != NULL) {
1531 retstring = strdup(propval.str);
1532 free(propval.str);
1533 }
Linus Walleijb9256fd2006-02-15 09:40:43 +00001534 return retstring;
1535}
1536
Linus Walleij30658792006-08-19 22:18:55 +00001537
1538/**
1539 * Sets the synchronization partner of an MTP device. Note that
1540 * we have no idea what the effect of setting this to "foobar"
1541 * may be. But the general idea seems to be to tell which program
1542 * shall synchronize with this device and tell others to leave
1543 * it alone.
1544 * @param device a pointer to the device to set the sync partner for.
1545 * @param syncpartner the new synchronization partner for the device.
1546 * @return 0 on success, any other value means failure.
1547 * @see LIBMTP_Get_Syncpartner()
1548 */
1549int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t *device,
1550 char const * const syncpartner)
1551{
1552 PTPPropertyValue propval;
1553 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001554 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00001555
Linus Walleij30658792006-08-19 22:18:55 +00001556 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
1557 return -1;
1558 }
Linus Walleija823a702006-08-27 21:27:46 +00001559 propval.str = (char *) syncpartner;
Linus Walleij070e9b42007-01-22 08:49:28 +00001560 ret = ptp_setdevicepropvalue(params,
1561 PTP_DPC_MTP_SynchronizationPartner,
1562 &propval,
1563 PTP_DTC_STR);
1564 if (ret != PTP_RC_OK) {
1565 add_ptp_error_to_errorstack(device, ret, "Error setting syncpartner.");
Linus Walleij30658792006-08-19 22:18:55 +00001566 return -1;
1567 }
Linus Walleij30658792006-08-19 22:18:55 +00001568 return 0;
1569}
1570
Linus Walleij394bbbe2006-02-22 16:10:53 +00001571/**
Linus Walleijf5fcda32006-12-03 22:31:02 +00001572 * Checks if the device can stora a file of this size or
1573 * if it's too big.
1574 * @param device a pointer to the device.
1575 * @param filesize the size of the file to check whether it will fit.
1576 * @return 0 if the file fits, any other value means failure.
1577 */
1578static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize) {
1579 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf5fcda32006-12-03 22:31:02 +00001580 uint64_t freebytes;
Linus Walleijf5fcda32006-12-03 22:31:02 +00001581 int ret;
1582
1583 // If we cannot check the storage, no big deal.
1584 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
1585 return 0;
1586 }
1587
Linus Walleij9e1b0812006-12-12 19:22:02 +00001588 ret = get_first_storage_freespace(device,&freebytes);
Linus Walleijf5fcda32006-12-03 22:31:02 +00001589 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001590 add_ptp_error_to_errorstack(device, ret, "check_if_file_fits(): error checking free storage.");
Linus Walleijf5fcda32006-12-03 22:31:02 +00001591 return -1;
1592 } else {
1593 if (filesize > freebytes) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001594 add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL, "check_if_file_fits(): device storage is full.");
Linus Walleijf5fcda32006-12-03 22:31:02 +00001595 return -1;
1596 }
1597 }
1598 return 0;
1599}
1600
1601
Linus Walleijf5fcda32006-12-03 22:31:02 +00001602/**
Linus Walleijfa1374c2006-02-27 07:41:46 +00001603 * This function retrieves the current battery level on the device.
1604 * @param device a pointer to the device to get the battery level for.
mopoke96143402006-10-30 04:37:26 +00001605 * @param maximum_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00001606 * maximum level of the battery if the call was successful.
mopoke96143402006-10-30 04:37:26 +00001607 * @param current_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00001608 * current level of the battery if the call was successful.
Linus Walleij545c7792006-06-13 15:22:30 +00001609 * A value of 0 means that the device is on external power.
Linus Walleijfa1374c2006-02-27 07:41:46 +00001610 * @return 0 if the storage info was successfully retrieved, any other
Linus Walleij80439342006-09-12 10:42:26 +00001611 * means failure. A typical cause of failure is that
Linus Walleij545c7792006-06-13 15:22:30 +00001612 * the device does not support the battery level property.
Linus Walleijfa1374c2006-02-27 07:41:46 +00001613 */
mopoke96143402006-10-30 04:37:26 +00001614int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *device,
1615 uint8_t * const maximum_level,
Linus Walleijfa1374c2006-02-27 07:41:46 +00001616 uint8_t * const current_level)
1617{
Linus Walleijb02a0662006-04-25 08:05:09 +00001618 PTPPropertyValue propval;
Linus Walleijfa1374c2006-02-27 07:41:46 +00001619 uint16_t ret;
Linus Walleij9b28da32006-03-16 13:47:58 +00001620 PTPParams *params = (PTPParams *) device->params;
Linus Walleijfa1374c2006-02-27 07:41:46 +00001621
Linus Walleij545c7792006-06-13 15:22:30 +00001622 *maximum_level = 0;
1623 *current_level = 0;
1624
1625 if (!ptp_property_issupported(params, PTP_DPC_BatteryLevel)) {
1626 return -1;
1627 }
mopoke96143402006-10-30 04:37:26 +00001628
Linus Walleijb02a0662006-04-25 08:05:09 +00001629 ret = ptp_getdevicepropvalue(params, PTP_DPC_BatteryLevel, &propval, PTP_DTC_UINT8);
1630 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001631 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Batterylevel(): could not get device property value.");
Linus Walleijfa1374c2006-02-27 07:41:46 +00001632 return -1;
1633 }
mopoke96143402006-10-30 04:37:26 +00001634
Linus Walleijfa1374c2006-02-27 07:41:46 +00001635 *maximum_level = device->maximum_battery_level;
Linus Walleijb02a0662006-04-25 08:05:09 +00001636 *current_level = propval.u8;
mopoke96143402006-10-30 04:37:26 +00001637
Linus Walleijfa1374c2006-02-27 07:41:46 +00001638 return 0;
1639}
1640
Linus Walleij13374a42006-09-13 11:55:30 +00001641
1642/**
1643 * Formats device storage (if the device supports the operation).
1644 * WARNING: This WILL delete all data from the device. Make sure you've
1645 * got confirmation from the user BEFORE you call this function.
1646 *
Linus Walleijf8491912006-12-15 10:23:30 +00001647 * @param device a pointer to the device containing the storage to format.
1648 * @param storage the actual storage to format.
Linus Walleij13374a42006-09-13 11:55:30 +00001649 * @return 0 on success, any other value means failure.
1650 */
Linus Walleijf8491912006-12-15 10:23:30 +00001651int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *device, LIBMTP_devicestorage_t *storage)
Linus Walleij13374a42006-09-13 11:55:30 +00001652{
1653 uint16_t ret;
1654 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001655
Linus Walleij13374a42006-09-13 11:55:30 +00001656 if (!ptp_operation_issupported(params,PTP_OC_FormatStore)) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001657 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Format_Storage(): device cannot format storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00001658 return -1;
1659 }
Linus Walleijf8491912006-12-15 10:23:30 +00001660 ret = ptp_formatstore(params, storage->id);
Linus Walleij13374a42006-09-13 11:55:30 +00001661 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001662 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Format_Storage(): failed to format storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00001663 return -1;
1664 }
1665 return 0;
1666}
1667
Linus Walleijfa1374c2006-02-27 07:41:46 +00001668/**
Linus Walleij545c7792006-06-13 15:22:30 +00001669 * Helper function to extract a unicode property off a device.
Linus Walleije46f12e2006-06-22 17:53:25 +00001670 * This is the standard way of retrieveing unicode device
1671 * properties as described by the PTP spec.
Linus Walleijcf223e62006-06-19 09:31:53 +00001672 * @param device a pointer to the device to get the property from.
mopoke96143402006-10-30 04:37:26 +00001673 * @param unicstring a pointer to a pointer that will hold the
Linus Walleijcf223e62006-06-19 09:31:53 +00001674 * property after this call is completed.
1675 * @param property the property to retrieve.
1676 * @return 0 on success, any other value means failure.
Linus Walleij545c7792006-06-13 15:22:30 +00001677 */
mopoke96143402006-10-30 04:37:26 +00001678static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +00001679 char **unicstring, uint16_t property)
Linus Walleij545c7792006-06-13 15:22:30 +00001680{
1681 PTPPropertyValue propval;
1682 PTPParams *params = (PTPParams *) device->params;
Linus Walleij16571dc2006-08-17 20:27:46 +00001683 uint16_t *tmp;
Linus Walleij070e9b42007-01-22 08:49:28 +00001684 uint16_t ret;
Linus Walleij545c7792006-06-13 15:22:30 +00001685 int i;
1686
1687 if (!ptp_property_issupported(params, property)) {
1688 return -1;
1689 }
1690
Linus Walleijcf223e62006-06-19 09:31:53 +00001691 // Unicode strings are 16bit unsigned integer arrays.
Linus Walleij070e9b42007-01-22 08:49:28 +00001692 ret = ptp_getdevicepropvalue(params,
1693 property,
1694 &propval,
1695 PTP_DTC_AUINT16);
1696 if (ret != PTP_RC_OK) {
1697 // TODO: add a note on WHICH property that we failed to get.
tedbullock4e51cb92007-02-15 11:48:34 +00001698 *unicstring = NULL;
Linus Walleij070e9b42007-01-22 08:49:28 +00001699 add_ptp_error_to_errorstack(device, ret, "get_device_unicode_property(): failed to get unicode property.");
Linus Walleij545c7792006-06-13 15:22:30 +00001700 return -1;
1701 }
1702
1703 // Extract the actual array.
Linus Walleij16571dc2006-08-17 20:27:46 +00001704 // printf("Array of %d elements\n", propval.a.count);
1705 tmp = malloc((propval.a.count + 1)*sizeof(uint16_t));
Linus Walleij545c7792006-06-13 15:22:30 +00001706 for (i = 0; i < propval.a.count; i++) {
Linus Walleij16571dc2006-08-17 20:27:46 +00001707 tmp[i] = propval.a.v[i].u16;
1708 // printf("%04x ", tmp[i]);
Linus Walleij545c7792006-06-13 15:22:30 +00001709 }
Linus Walleij16571dc2006-08-17 20:27:46 +00001710 tmp[propval.a.count] = 0x0000U;
Linus Walleij545c7792006-06-13 15:22:30 +00001711 free(propval.a.v);
1712
Linus Walleij3ec86312006-08-21 13:25:24 +00001713 *unicstring = utf16_to_utf8(device, tmp);
Linus Walleij16571dc2006-08-17 20:27:46 +00001714
Linus Walleij545c7792006-06-13 15:22:30 +00001715 free(tmp);
1716
1717 return 0;
1718}
1719
1720/**
1721 * This function returns the secure time as an XML document string from
1722 * the device.
1723 * @param device a pointer to the device to get the secure time for.
1724 * @param sectime the secure time string as an XML document or NULL if the call
1725 * failed or the secure time property is not supported. This string
1726 * must be <code>free()</code>:ed by the caller after use.
1727 * @return 0 on success, any other value means failure.
1728 */
Linus Walleij8ab54262006-06-21 07:12:28 +00001729int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *device, char ** const sectime)
Linus Walleij545c7792006-06-13 15:22:30 +00001730{
1731 return get_device_unicode_property(device, sectime, PTP_DPC_MTP_SecureTime);
1732}
1733
1734/**
mopoke96143402006-10-30 04:37:26 +00001735 * This function returns the device (public key) certificate as an
Linus Walleij545c7792006-06-13 15:22:30 +00001736 * XML document string from the device.
1737 * @param device a pointer to the device to get the device certificate for.
1738 * @param devcert the device certificate as an XML string or NULL if the call
1739 * failed or the device certificate property is not supported. This
1740 * string must be <code>free()</code>:ed by the caller after use.
1741 * @return 0 on success, any other value means failure.
1742 */
Linus Walleij8ab54262006-06-21 07:12:28 +00001743int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *device, char ** const devcert)
Linus Walleij545c7792006-06-13 15:22:30 +00001744{
1745 return get_device_unicode_property(device, devcert, PTP_DPC_MTP_DeviceCertificate);
1746}
1747
1748/**
Linus Walleij8ab54262006-06-21 07:12:28 +00001749 * This function retrieves a list of supported file types, i.e. the file
1750 * types that this device claims it supports, e.g. audio file types that
1751 * the device can play etc. This list is mitigated to
1752 * inlcude the file types that libmtp can handle, i.e. it will not list
1753 * filetypes that libmtp will handle internally like playlists and folders.
1754 * @param device a pointer to the device to get the filetype capabilities for.
1755 * @param filetypes a pointer to a pointer that will hold the list of
1756 * supported filetypes if the call was successful. This list must
1757 * be <code>free()</code>:ed by the caller after use.
1758 * @param length a pointer to a variable that will hold the length of the
1759 * list of supported filetypes if the call was successful.
1760 * @return 0 on success, any other value means failure.
1761 * @see LIBMTP_Get_Filetype_Description()
1762 */
mopoke96143402006-10-30 04:37:26 +00001763int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const filetypes,
Linus Walleij8ab54262006-06-21 07:12:28 +00001764 uint16_t * const length)
1765{
1766 PTPParams *params = (PTPParams *) device->params;
1767 uint16_t *localtypes;
1768 uint16_t localtypelen;
1769 uint32_t i;
mopoke96143402006-10-30 04:37:26 +00001770
Linus Walleij8ab54262006-06-21 07:12:28 +00001771 // This is more memory than needed if there are unknown types, but what the heck.
1772 localtypes = (uint16_t *) malloc(params->deviceinfo.ImageFormats_len * sizeof(uint16_t));
1773 localtypelen = 0;
mopoke96143402006-10-30 04:37:26 +00001774
Linus Walleij8ab54262006-06-21 07:12:28 +00001775 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
1776 uint16_t localtype = map_ptp_type_to_libmtp_type(params->deviceinfo.ImageFormats[i]);
1777 if (localtype != LIBMTP_FILETYPE_UNKNOWN) {
1778 localtypes[localtypelen] = localtype;
1779 localtypelen++;
1780 }
1781 }
1782
1783 *filetypes = localtypes;
1784 *length = localtypelen;
1785
1786 return 0;
1787}
1788
raveloxd9a28642006-05-26 23:42:22 +00001789/**
Linus Walleij9e1b0812006-12-12 19:22:02 +00001790 * This function retrieves all the storage id's of a device and there
1791 * properties. Then creates a linked list and puts the list head into
Linus Walleijf8491912006-12-15 10:23:30 +00001792 * the device struct. It also optionally sorts this list. If you want
1793 * to display storage information in your application you should call
1794 * this function, then dereference the device struct
1795 * (<code>device->storage</code>) to get out information on the storage.
1796 *
Linus Walleij9e1b0812006-12-12 19:22:02 +00001797 * @param device a pointer to the device to get the filetype capabilities for.
1798 * @param sortby an integer that determines the sorting of the storage list.
1799 * Valid sort methods are defined in libmtp.h with beginning with
1800 * LIBMTP_STORAGE_SORTBY_. 0 or LIBMTP_STORAGE_SORTBY_NOTSORTED to not
1801 * sort.
1802 * @return 0 on success, 1 success but only with storage id's, storage
1803 * properities could not be retrieved and -1 means failure.
1804 * @see LIBMTP_Get_Filetype_Description()
1805 */
1806int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *device, int const sortby)
1807{
1808 uint32_t i = 0;
1809 PTPStorageInfo storageInfo;
1810 PTPParams *params = (PTPParams *) device->params;
1811 PTPStorageIDs storageIDs;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001812 LIBMTP_devicestorage_t *storage = NULL;
1813 LIBMTP_devicestorage_t *storageprev = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001814
1815 if (device->storage != NULL)
1816 free_storage_list(device);
1817
1818 // if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs))
1819 // return -1;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001820 if (!ptp_getstorageids (params, &storageIDs) == PTP_RC_OK)
Linus Walleij9e1b0812006-12-12 19:22:02 +00001821 return -1;
1822 if (storageIDs.n < 1)
1823 return -1;
1824
1825 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
1826 for (i = 0; i < storageIDs.n; i++) {
1827
Linus Walleije1ac07e2006-12-14 19:38:59 +00001828 storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
1829 storage->prev = storageprev;
1830 if (storageprev != NULL)
1831 storageprev->next = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001832 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00001833 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001834
Linus Walleije1ac07e2006-12-14 19:38:59 +00001835 storage->id = storageIDs.Storage[i];
1836 storage->StorageType = PTP_ST_Undefined;
1837 storage->FilesystemType = PTP_FST_Undefined;
1838 storage->AccessCapability = PTP_AC_ReadWrite;
1839 storage->MaxCapacity = (uint64_t) -1;
1840 storage->FreeSpaceInBytes = (uint64_t) -1;
1841 storage->FreeSpaceInObjects = (uint64_t) -1;
1842 storage->StorageDescription = strdup("Unknown storage");
1843 storage->VolumeIdentifier = strdup("Unknown volume");
1844 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001845
Linus Walleije1ac07e2006-12-14 19:38:59 +00001846 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001847 }
1848 free(storageIDs.Storage);
1849 return 1;
1850 } else {
1851 for (i = 0; i < storageIDs.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001852 uint16_t ret;
1853 ret = ptp_getstorageinfo(params, storageIDs.Storage[i], &storageInfo);
1854 if (ret != PTP_RC_OK) {
1855 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Storage(): Could not get storage info.");
Linus Walleij9e1b0812006-12-12 19:22:02 +00001856 if (device->storage != NULL) {
1857 free_storage_list(device);
1858 }
1859 return -1;
1860 }
1861
Linus Walleije1ac07e2006-12-14 19:38:59 +00001862 storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
1863 storage->prev = storageprev;
1864 if (storageprev != NULL)
1865 storageprev->next = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001866 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00001867 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001868
Linus Walleije1ac07e2006-12-14 19:38:59 +00001869 storage->id = storageIDs.Storage[i];
1870 storage->StorageType = storageInfo.StorageType;
1871 storage->FilesystemType = storageInfo.FilesystemType;
1872 storage->AccessCapability = storageInfo.AccessCapability;
1873 storage->MaxCapacity = storageInfo.MaxCapability;
1874 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
1875 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
1876 storage->StorageDescription = storageInfo.StorageDescription;
1877 storage->VolumeIdentifier = storageInfo.VolumeLabel;
1878 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001879
Linus Walleije1ac07e2006-12-14 19:38:59 +00001880 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001881 }
1882
Linus Walleije1ac07e2006-12-14 19:38:59 +00001883 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001884
1885 sort_storage_by(device,sortby);
1886 free(storageIDs.Storage);
1887 return 0;
1888 }
1889}
1890
1891/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00001892 * This creates a new file metadata structure and allocates memory
1893 * for it. Notice that if you add strings to this structure they
1894 * will be freed by the corresponding <code>LIBMTP_destroy_file_t</code>
mopoke96143402006-10-30 04:37:26 +00001895 * operation later, so be careful of using strdup() when assigning
Linus Walleijf6bc1782006-03-24 15:12:47 +00001896 * strings, e.g.:
1897 *
1898 * <pre>
1899 * LIBMTP_file_t *file = LIBMTP_new_file_t();
1900 * file->filename = strdup(namestr);
1901 * ....
1902 * LIBMTP_destroy_file_t(file);
1903 * </pre>
1904 *
1905 * @return a pointer to the newly allocated metadata structure.
1906 * @see LIBMTP_destroy_file_t()
1907 */
1908LIBMTP_file_t *LIBMTP_new_file_t(void)
1909{
1910 LIBMTP_file_t *new = (LIBMTP_file_t *) malloc(sizeof(LIBMTP_file_t));
1911 if (new == NULL) {
1912 return NULL;
1913 }
1914 new->filename = NULL;
1915 new->filesize = 0;
1916 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
1917 new->next = NULL;
1918 return new;
1919}
1920
1921/**
1922 * This destroys a file metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00001923 * used by it, including any strings. Never use a file metadata
Linus Walleijf6bc1782006-03-24 15:12:47 +00001924 * structure again after calling this function on it.
1925 * @param file the file metadata to destroy.
1926 * @see LIBMTP_new_file_t()
1927 */
1928void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
1929{
1930 if (file == NULL) {
1931 return;
1932 }
1933 if (file->filename != NULL)
1934 free(file->filename);
1935 free(file);
1936 return;
1937}
1938
1939/**
mopoke31364442006-11-20 04:53:04 +00001940* THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Richard Lowdc0b6c72006-11-13 09:22:23 +00001941 * NOT TO USE IT.
1942 * @see LIBMTP_Get_Filelisting_With_Callback()
1943 */
1944LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *device)
1945{
1946 printf("WARNING: LIBMTP_Get_Filelisting() is deprecated.\n");
1947 printf("WARNING: please update your code to use LIBMTP_Get_Filelisting_With_Callback()\n");
1948 return LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
1949}
1950
1951/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00001952 * This returns a long list of all files available
1953 * on the current MTP device. Typical usage:
1954 *
1955 * <pre>
1956 * LIBMTP_file_t *filelist;
1957 *
Richard Lowdc0b6c72006-11-13 09:22:23 +00001958 * filelist = LIBMTP_Get_Filelisting_With_Callback(device, callback, data);
Linus Walleijf6bc1782006-03-24 15:12:47 +00001959 * while (filelist != NULL) {
1960 * LIBMTP_file_t *tmp;
1961 *
1962 * // Do something on each element in the list here...
1963 * tmp = filelist;
1964 * filelist = filelist->next;
1965 * LIBMTP_destroy_file_t(tmp);
1966 * }
1967 * </pre>
1968 *
1969 * @param device a pointer to the device to get the file listing for.
Richard Lowdc0b6c72006-11-13 09:22:23 +00001970 * @param callback a function to be called during the tracklisting retrieveal
mopoke31364442006-11-20 04:53:04 +00001971 * for displaying progress bars etc, or NULL if you don't want
Richard Lowdc0b6c72006-11-13 09:22:23 +00001972 * any callbacks.
1973 * @param data a user-defined pointer that is passed along to
1974 * the <code>progress</code> function in order to
1975 * pass along some user defined data to the progress
1976 * updates. If not used, set this to NULL.
Linus Walleijf6bc1782006-03-24 15:12:47 +00001977 * @return a list of files that can be followed using the <code>next</code>
1978 * field of the <code>LIBMTP_file_t</code> data structure.
1979 * Each of the metadata tags must be freed after use, and may
1980 * contain only partial metadata information, i.e. one or several
1981 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00001982 * @see LIBMTP_Get_Filemetadata()
Linus Walleijf6bc1782006-03-24 15:12:47 +00001983 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00001984LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
1985 LIBMTP_progressfunc_t const callback,
1986 void const * const data)
Linus Walleijf6bc1782006-03-24 15:12:47 +00001987{
1988 uint32_t i = 0;
1989 LIBMTP_file_t *retfiles = NULL;
1990 LIBMTP_file_t *curfile = NULL;
1991 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001992
mopoke96143402006-10-30 04:37:26 +00001993 // Get all the handles if we haven't already done that
Linus Walleijf6bc1782006-03-24 15:12:47 +00001994 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00001995 flush_handles(device);
Linus Walleijf6bc1782006-03-24 15:12:47 +00001996 }
mopoke96143402006-10-30 04:37:26 +00001997
Linus Walleijf6bc1782006-03-24 15:12:47 +00001998 for (i = 0; i < params->handles.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001999 LIBMTP_file_t *file;
2000 PTPObjectInfo oi;
2001 uint16_t ret;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002002
Richard Lowdc0b6c72006-11-13 09:22:23 +00002003 if (callback != NULL)
2004 callback(i, params->handles.n, data);
mopoke31364442006-11-20 04:53:04 +00002005
Linus Walleij070e9b42007-01-22 08:49:28 +00002006 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
Linus Walleijf6bc1782006-03-24 15:12:47 +00002007
Linus Walleij070e9b42007-01-22 08:49:28 +00002008 if (ret == PTP_RC_OK) {
Linus Walleij16c51f02006-05-04 13:20:22 +00002009
Linus Walleij91405592006-05-05 14:22:51 +00002010 if (oi.ObjectFormat == PTP_OFC_Association) {
Linus Walleijf0f3d482006-05-29 14:10:21 +00002011 // MTP use thesis object format for folders which means
Linus Walleij16c51f02006-05-04 13:20:22 +00002012 // these "files" will turn up on a folder listing instead.
2013 continue;
2014 }
2015
Linus Walleijf6bc1782006-03-24 15:12:47 +00002016 // Allocate a new file type
2017 file = LIBMTP_new_file_t();
Linus Walleijb02a0662006-04-25 08:05:09 +00002018
Linus Walleijd208f9c2006-04-27 14:16:06 +00002019 file->parent_id = oi.ParentObject;
Linus Walleij16c51f02006-05-04 13:20:22 +00002020
2021 // Set the filetype
2022 file->filetype = map_ptp_type_to_libmtp_type(oi.ObjectFormat);
Linus Walleijf6bc1782006-03-24 15:12:47 +00002023
2024 // Original file-specific properties
2025 file->filesize = oi.ObjectCompressedSize;
2026 if (oi.Filename != NULL) {
2027 file->filename = strdup(oi.Filename);
2028 }
2029
2030 // This is some sort of unique ID so we can keep track of the track.
2031 file->item_id = params->handles.Handler[i];
mopoke96143402006-10-30 04:37:26 +00002032
Linus Walleijf6bc1782006-03-24 15:12:47 +00002033 // Add track to a list that will be returned afterwards.
2034 if (retfiles == NULL) {
2035 retfiles = file;
2036 curfile = file;
2037 } else {
2038 curfile->next = file;
2039 curfile = file;
2040 }
mopoke96143402006-10-30 04:37:26 +00002041
Linus Walleijf6bc1782006-03-24 15:12:47 +00002042 // Call listing callback
2043 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
2044
2045 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00002046 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filelisting_With_Callback(): Found a bad handle, trying to ignore it.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002047 }
2048
2049 } // Handle counting loop
2050 return retfiles;
2051}
2052
2053/**
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002054 * This function retrieves the metadata for a single file off
2055 * the device.
2056 *
2057 * Do not call this function repeatedly! The file handles are linearly
2058 * searched O(n) and the call may involve (slow) USB traffic, so use
2059 * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
2060 * as an efficient data structure such as a hash list.
2061 *
2062 * @param device a pointer to the device to get the file metadata from.
2063 * @param fileid the object ID of the file that you want the metadata for.
2064 * @return a metadata entry on success or NULL on failure.
2065 * @see LIBMTP_Get_Filelisting()
2066 */
2067LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
2068{
2069 uint32_t i = 0;
2070 PTPParams *params = (PTPParams *) device->params;
2071
mopoke96143402006-10-30 04:37:26 +00002072 // Get all the handles if we haven't already done that
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002073 if (params->handles.Handler == NULL) {
2074 flush_handles(device);
2075 }
2076
2077 for (i = 0; i < params->handles.n; i++) {
2078 LIBMTP_file_t *file;
2079 PTPObjectInfo oi;
Linus Walleij070e9b42007-01-22 08:49:28 +00002080 uint16_t ret;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002081
2082 // Is this the file we're looking for?
2083 if (params->handles.Handler[i] != fileid) {
2084 continue;
2085 }
2086
Linus Walleij070e9b42007-01-22 08:49:28 +00002087 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
2088
2089 if (ret == PTP_RC_OK) {
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002090
2091 if (oi.ObjectFormat == PTP_OFC_Association) {
2092 // MTP use thesis object format for folders which means
2093 // these "files" will turn up on a folder listing instead.
2094 return NULL;
2095 }
2096
2097 // Allocate a new file type
2098 file = LIBMTP_new_file_t();
mopoke96143402006-10-30 04:37:26 +00002099
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002100 file->parent_id = oi.ParentObject;
mopoke96143402006-10-30 04:37:26 +00002101
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002102 // Set the filetype
2103 file->filetype = map_ptp_type_to_libmtp_type(oi.ObjectFormat);
mopoke96143402006-10-30 04:37:26 +00002104
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002105 // Original file-specific properties
2106 file->filesize = oi.ObjectCompressedSize;
2107 if (oi.Filename != NULL) {
2108 file->filename = strdup(oi.Filename);
2109 }
mopoke96143402006-10-30 04:37:26 +00002110
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002111 // This is some sort of unique ID so we can keep track of the track.
2112 file->item_id = params->handles.Handler[i];
mopoke96143402006-10-30 04:37:26 +00002113
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002114 return file;
2115 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00002116 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filemetadata(): Could not get object info.");
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002117 return NULL;
2118 }
2119
2120 }
2121 return NULL;
2122}
2123
2124/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00002125 * This creates a new track metadata structure and allocates memory
2126 * for it. Notice that if you add strings to this structure they
2127 * will be freed by the corresponding <code>LIBMTP_destroy_track_t</code>
mopoke96143402006-10-30 04:37:26 +00002128 * operation later, so be careful of using strdup() when assigning
Linus Walleij394bbbe2006-02-22 16:10:53 +00002129 * strings, e.g.:
2130 *
Linus Walleij17e39f72006-02-23 15:54:28 +00002131 * <pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00002132 * LIBMTP_track_t *track = LIBMTP_new_track_t();
2133 * track->title = strdup(titlestr);
2134 * ....
2135 * LIBMTP_destroy_track_t(track);
Linus Walleij17e39f72006-02-23 15:54:28 +00002136 * </pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00002137 *
2138 * @return a pointer to the newly allocated metadata structure.
2139 * @see LIBMTP_destroy_track_t()
2140 */
2141LIBMTP_track_t *LIBMTP_new_track_t(void)
Linus Walleijb9256fd2006-02-15 09:40:43 +00002142{
2143 LIBMTP_track_t *new = (LIBMTP_track_t *) malloc(sizeof(LIBMTP_track_t));
2144 if (new == NULL) {
2145 return NULL;
2146 }
2147 new->title = NULL;
2148 new->artist = NULL;
2149 new->album = NULL;
2150 new->genre = NULL;
2151 new->date = NULL;
2152 new->filename = NULL;
2153 new->duration = 0;
2154 new->tracknumber = 0;
2155 new->filesize = 0;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002156 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
Linus Walleijcf223e62006-06-19 09:31:53 +00002157 new->samplerate = 0;
2158 new->nochannels = 0;
2159 new->wavecodec = 0;
2160 new->bitrate = 0;
2161 new->bitratetype = 0;
2162 new->rating = 0;
2163 new->usecount = 0;
Linus Walleijb9256fd2006-02-15 09:40:43 +00002164 new->next = NULL;
2165 return new;
2166}
2167
Linus Walleij394bbbe2006-02-22 16:10:53 +00002168/**
2169 * This destroys a track metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00002170 * used by it, including any strings. Never use a track metadata
Linus Walleij394bbbe2006-02-22 16:10:53 +00002171 * structure again after calling this function on it.
2172 * @param track the track metadata to destroy.
2173 * @see LIBMTP_new_track_t()
2174 */
Linus Walleijb9256fd2006-02-15 09:40:43 +00002175void LIBMTP_destroy_track_t(LIBMTP_track_t *track)
2176{
2177 if (track == NULL) {
2178 return;
2179 }
2180 if (track->title != NULL)
2181 free(track->title);
2182 if (track->artist != NULL)
2183 free(track->artist);
2184 if (track->album != NULL)
2185 free(track->album);
2186 if (track->genre != NULL)
2187 free(track->genre);
2188 if (track->date != NULL)
2189 free(track->date);
2190 if (track->filename != NULL)
2191 free(track->filename);
2192 free(track);
2193 return;
2194}
2195
2196/**
Linus Walleij8ab54262006-06-21 07:12:28 +00002197 * This function retrieves the track metadata for a track
2198 * given by a unique ID.
2199 * @param device a pointer to the device to get the track metadata off.
2200 * @param trackid the unique ID of the track.
2201 * @param objectformat the object format of this track, so we know what it supports.
2202 * @param track a metadata set to fill in.
2203 */
2204static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
2205 LIBMTP_track_t *track)
2206{
Linus Walleij00cf0642006-07-26 20:40:59 +00002207 uint16_t ret;
2208 PTPParams *params = (PTPParams *) device->params;
Linus Walleij1a1b2d12006-11-23 13:32:17 +00002209 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij00cf0642006-07-26 20:40:59 +00002210 uint32_t i;
Linus Walleij00cf0642006-07-26 20:40:59 +00002211
Linus Walleij2f5ed5c2006-11-21 07:40:29 +00002212#ifdef ENABLE_MTP_ENHANCED
Linus Walleij1a1b2d12006-11-23 13:32:17 +00002213 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
2214 && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)) {
Linus Walleij3fcfea52006-11-13 07:07:36 +00002215 MTPPropList *proplist = NULL;
2216 MTPPropList *prop;
2217 MTPPropList *tmpprop;
mopoke31364442006-11-20 04:53:04 +00002218
Linus Walleij1a1b2d12006-11-23 13:32:17 +00002219 /*
2220 * This should retrieve all properties for an object, but on devices
2221 * which are inherently broken it will not, so these need the
2222 * special flag DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST.
2223 */
Linus Walleij3fcfea52006-11-13 07:07:36 +00002224 ret = ptp_mtp_getobjectproplist(params, track->item_id, &proplist);
Linus Walleij070e9b42007-01-22 08:49:28 +00002225 if (ret != PTP_RC_OK) {
2226 add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectproplist() failed.");
2227 return;
2228 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00002229 prop = proplist;
2230 while (prop != NULL) {
2231 switch (prop->property) {
Linus Walleij00cf0642006-07-26 20:40:59 +00002232 case PTP_OPC_Name:
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002233 if (prop->propval.str != NULL)
2234 track->title = strdup(prop->propval.str);
2235 else
2236 track->title = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00002237 break;
2238 case PTP_OPC_Artist:
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002239 if (prop->propval.str != NULL)
2240 track->artist = strdup(prop->propval.str);
2241 else
2242 track->artist = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00002243 break;
2244 case PTP_OPC_Duration:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002245 track->duration = prop->propval.u32;
Linus Walleij00cf0642006-07-26 20:40:59 +00002246 break;
2247 case PTP_OPC_Track:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002248 track->tracknumber = prop->propval.u16;
Linus Walleij00cf0642006-07-26 20:40:59 +00002249 break;
2250 case PTP_OPC_Genre:
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002251 if (prop->propval.str != NULL)
2252 track->genre = strdup(prop->propval.str);
2253 else
2254 track->genre = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00002255 break;
2256 case PTP_OPC_AlbumName:
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002257 if (prop->propval.str != NULL)
2258 track->album = strdup(prop->propval.str);
2259 else
2260 track->album = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00002261 break;
2262 case PTP_OPC_OriginalReleaseDate:
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002263 if (prop->propval.str != NULL)
2264 track->date = strdup(prop->propval.str);
2265 else
2266 track->date = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00002267 break;
2268 // These are, well not so important.
2269 case PTP_OPC_SampleRate:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002270 track->samplerate = prop->propval.u32;
Linus Walleij00cf0642006-07-26 20:40:59 +00002271 break;
2272 case PTP_OPC_NumberOfChannels:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002273 track->nochannels = prop->propval.u16;
Linus Walleij00cf0642006-07-26 20:40:59 +00002274 break;
2275 case PTP_OPC_AudioWAVECodec:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002276 track->wavecodec = prop->propval.u32;
Linus Walleij00cf0642006-07-26 20:40:59 +00002277 break;
2278 case PTP_OPC_AudioBitRate:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002279 track->bitrate = prop->propval.u32;
Linus Walleij00cf0642006-07-26 20:40:59 +00002280 break;
2281 case PTP_OPC_BitRateType:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002282 track->bitratetype = prop->propval.u16;
Linus Walleij00cf0642006-07-26 20:40:59 +00002283 break;
2284 case PTP_OPC_Rating:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002285 track->rating = prop->propval.u16;
Linus Walleij00cf0642006-07-26 20:40:59 +00002286 break;
2287 case PTP_OPC_UseCount:
Linus Walleij3fcfea52006-11-13 07:07:36 +00002288 track->usecount = prop->propval.u32;
Linus Walleij00cf0642006-07-26 20:40:59 +00002289 break;
2290 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00002291 tmpprop = prop;
2292 prop = prop->next;
2293 Destroy_MTP_Prop_Entry(tmpprop);
Linus Walleij00cf0642006-07-26 20:40:59 +00002294 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00002295 } else {
2296#else
2297 {
2298#endif
2299 uint16_t *props = NULL;
2300 uint32_t propcnt = 0;
2301
2302 // First see which properties can be retrieved for this object format
Linus Walleij070e9b42007-01-22 08:49:28 +00002303 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(track->filetype), &propcnt, &props);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002304 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002305 add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
Linus Walleij3fcfea52006-11-13 07:07:36 +00002306 // Just bail out for now, nothing is ever set.
2307 return;
2308 } else {
2309 for (i=0;i<propcnt;i++) {
2310 switch (props[i]) {
2311 case PTP_OPC_Name:
Linus Walleij9901e222006-11-30 12:28:19 +00002312 track->title = get_string_from_object(device, track->item_id, PTP_OPC_Name);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002313 break;
2314 case PTP_OPC_Artist:
Linus Walleij9901e222006-11-30 12:28:19 +00002315 track->artist = get_string_from_object(device, track->item_id, PTP_OPC_Artist);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002316 break;
2317 case PTP_OPC_Duration:
Linus Walleij9901e222006-11-30 12:28:19 +00002318 track->duration = get_u32_from_object(device, track->item_id, PTP_OPC_Duration, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002319 break;
2320 case PTP_OPC_Track:
Linus Walleij9901e222006-11-30 12:28:19 +00002321 track->tracknumber = get_u16_from_object(device, track->item_id, PTP_OPC_Track, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002322 break;
2323 case PTP_OPC_Genre:
Linus Walleij9901e222006-11-30 12:28:19 +00002324 track->genre = get_string_from_object(device, track->item_id, PTP_OPC_Genre);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002325 break;
2326 case PTP_OPC_AlbumName:
Linus Walleij9901e222006-11-30 12:28:19 +00002327 track->album = get_string_from_object(device, track->item_id, PTP_OPC_AlbumName);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002328 break;
2329 case PTP_OPC_OriginalReleaseDate:
Linus Walleij9901e222006-11-30 12:28:19 +00002330 track->date = get_string_from_object(device, track->item_id, PTP_OPC_OriginalReleaseDate);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002331 break;
2332 // These are, well not so important.
2333 case PTP_OPC_SampleRate:
Linus Walleij9901e222006-11-30 12:28:19 +00002334 track->samplerate = get_u32_from_object(device, track->item_id, PTP_OPC_SampleRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002335 break;
2336 case PTP_OPC_NumberOfChannels:
Linus Walleij9901e222006-11-30 12:28:19 +00002337 track->nochannels = get_u16_from_object(device, track->item_id, PTP_OPC_NumberOfChannels, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002338 break;
2339 case PTP_OPC_AudioWAVECodec:
Linus Walleij9901e222006-11-30 12:28:19 +00002340 track->wavecodec = get_u32_from_object(device, track->item_id, PTP_OPC_AudioWAVECodec, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002341 break;
2342 case PTP_OPC_AudioBitRate:
Linus Walleij9901e222006-11-30 12:28:19 +00002343 track->bitrate = get_u32_from_object(device, track->item_id, PTP_OPC_AudioBitRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002344 break;
2345 case PTP_OPC_BitRateType:
Linus Walleij9901e222006-11-30 12:28:19 +00002346 track->bitratetype = get_u16_from_object(device, track->item_id, PTP_OPC_BitRateType, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002347 break;
2348 case PTP_OPC_Rating:
Linus Walleij9901e222006-11-30 12:28:19 +00002349 track->rating = get_u16_from_object(device, track->item_id, PTP_OPC_Rating, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002350 break;
2351 case PTP_OPC_UseCount:
Linus Walleij9901e222006-11-30 12:28:19 +00002352 track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002353 break;
2354 }
2355 }
2356 free(props);
2357 }
Linus Walleij00cf0642006-07-26 20:40:59 +00002358 }
Linus Walleij8ab54262006-06-21 07:12:28 +00002359}
2360
2361/**
mopoke31364442006-11-20 04:53:04 +00002362 * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Linus Walleij3fcfea52006-11-13 07:07:36 +00002363 * NOT TO USE IT.
2364 * @see LIBMTP_Get_Tracklisting_With_Callback()
2365 */
2366LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t *device)
2367{
2368 printf("WARNING: LIBMTP_Get_Tracklisting() is deprecated.\n");
2369 printf("WARNING: please update your code to use LIBMTP_Get_Tracklisting_With_Callback()\n");
Richard Lowdc0b6c72006-11-13 09:22:23 +00002370 return LIBMTP_Get_Tracklisting_With_Callback(device, NULL, NULL);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002371}
2372
2373/**
Linus Walleijb9256fd2006-02-15 09:40:43 +00002374 * This returns a long list of all tracks available
Linus Walleija4982732006-02-24 15:46:02 +00002375 * on the current MTP device. Typical usage:
2376 *
2377 * <pre>
2378 * LIBMTP_track_t *tracklist;
2379 *
Richard Lowdc0b6c72006-11-13 09:22:23 +00002380 * tracklist = LIBMTP_Get_Tracklisting(device, callback, data);
Linus Walleija4982732006-02-24 15:46:02 +00002381 * while (tracklist != NULL) {
2382 * LIBMTP_track_t *tmp;
2383 *
2384 * // Do something on each element in the list here...
2385 * tmp = tracklist;
2386 * tracklist = tracklist->next;
2387 * LIBMTP_destroy_track_t(tmp);
2388 * }
2389 * </pre>
2390 *
Linus Walleijb9256fd2006-02-15 09:40:43 +00002391 * @param device a pointer to the device to get the track listing for.
Linus Walleij3fcfea52006-11-13 07:07:36 +00002392 * @param callback a function to be called during the tracklisting retrieveal
mopoke31364442006-11-20 04:53:04 +00002393 * for displaying progress bars etc, or NULL if you don't want
Linus Walleij3fcfea52006-11-13 07:07:36 +00002394 * any callbacks.
Richard Lowdc0b6c72006-11-13 09:22:23 +00002395 * @param data a user-defined pointer that is passed along to
2396 * the <code>progress</code> function in order to
2397 * pass along some user defined data to the progress
2398 * updates. If not used, set this to NULL.
Linus Walleija4982732006-02-24 15:46:02 +00002399 * @return a list of tracks that can be followed using the <code>next</code>
2400 * field of the <code>LIBMTP_track_t</code> data structure.
2401 * Each of the metadata tags must be freed after use, and may
2402 * contain only partial metadata information, i.e. one or several
2403 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002404 * @see LIBMTP_Get_Trackmetadata()
Linus Walleijb9256fd2006-02-15 09:40:43 +00002405 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00002406LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t *device,
2407 LIBMTP_progressfunc_t const callback,
2408 void const * const data)
Linus Walleijb9256fd2006-02-15 09:40:43 +00002409{
2410 uint32_t i = 0;
2411 LIBMTP_track_t *retracks = NULL;
2412 LIBMTP_track_t *curtrack = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00002413 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00002414
mopoke96143402006-10-30 04:37:26 +00002415 // Get all the handles if we haven't already done that
Linus Walleij9b28da32006-03-16 13:47:58 +00002416 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00002417 flush_handles(device);
Linus Walleijb9256fd2006-02-15 09:40:43 +00002418 }
mopoke96143402006-10-30 04:37:26 +00002419
Linus Walleij9b28da32006-03-16 13:47:58 +00002420 for (i = 0; i < params->handles.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002421 LIBMTP_track_t *track;
2422 PTPObjectInfo oi;
2423 uint16_t ret;
mopoke31364442006-11-20 04:53:04 +00002424
Richard Lowdc0b6c72006-11-13 09:22:23 +00002425 if (callback != NULL)
2426 callback(i, params->handles.n, data);
mopoke31364442006-11-20 04:53:04 +00002427
Linus Walleij070e9b42007-01-22 08:49:28 +00002428 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
2429 if (ret == PTP_RC_OK) {
mopoke96143402006-10-30 04:37:26 +00002430
Linus Walleijb9256fd2006-02-15 09:40:43 +00002431 // Ignore stuff we don't know how to handle...
Linus Walleij8ab54262006-06-21 07:12:28 +00002432 // TODO: get this list as an intersection of the sets
2433 // supported by the device and the from the device and
2434 // all known audio track files?
mopoke96143402006-10-30 04:37:26 +00002435 if ( oi.ObjectFormat != PTP_OFC_WAV &&
2436 oi.ObjectFormat != PTP_OFC_MP3 &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002437 oi.ObjectFormat != PTP_OFC_MTP_MP2 &&
Linus Walleij9c6ca022006-04-21 10:24:15 +00002438 oi.ObjectFormat != PTP_OFC_MTP_WMA &&
mopoke96143402006-10-30 04:37:26 +00002439 oi.ObjectFormat != PTP_OFC_MTP_OGG &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002440 oi.ObjectFormat != PTP_OFC_MTP_FLAC &&
2441 oi.ObjectFormat != PTP_OFC_MTP_AAC &&
2442 oi.ObjectFormat != PTP_OFC_MTP_M4A &&
Linus Walleij9c6ca022006-04-21 10:24:15 +00002443 oi.ObjectFormat != PTP_OFC_MTP_MP4 &&
2444 oi.ObjectFormat != PTP_OFC_MTP_UndefinedAudio ) {
Linus Walleijafe61432006-05-05 13:51:34 +00002445 // printf("Not a music track (format: %d), skipping...\n",oi.ObjectFormat);
Linus Walleijb9256fd2006-02-15 09:40:43 +00002446 continue;
2447 }
mopoke96143402006-10-30 04:37:26 +00002448
Linus Walleijb9256fd2006-02-15 09:40:43 +00002449 // Allocate a new track type
2450 track = LIBMTP_new_track_t();
Linus Walleij8ab54262006-06-21 07:12:28 +00002451
2452 // This is some sort of unique ID so we can keep track of the track.
2453 track->item_id = params->handles.Handler[i];
mopoke31364442006-11-20 04:53:04 +00002454 track->parent_id = oi.ParentObject;
mopoke96143402006-10-30 04:37:26 +00002455
Linus Walleij16c51f02006-05-04 13:20:22 +00002456 track->filetype = map_ptp_type_to_libmtp_type(oi.ObjectFormat);
mopoke96143402006-10-30 04:37:26 +00002457
Linus Walleijb9256fd2006-02-15 09:40:43 +00002458 // Original file-specific properties
2459 track->filesize = oi.ObjectCompressedSize;
2460 if (oi.Filename != NULL) {
2461 track->filename = strdup(oi.Filename);
Linus Walleijb9256fd2006-02-15 09:40:43 +00002462 }
Linus Walleij8ab54262006-06-21 07:12:28 +00002463
2464 get_track_metadata(device, oi.ObjectFormat, track);
mopoke96143402006-10-30 04:37:26 +00002465
Linus Walleijb9256fd2006-02-15 09:40:43 +00002466 // Add track to a list that will be returned afterwards.
2467 if (retracks == NULL) {
2468 retracks = track;
2469 curtrack = track;
2470 } else {
2471 curtrack->next = track;
2472 curtrack = track;
2473 }
mopoke96143402006-10-30 04:37:26 +00002474
Linus Walleijb9256fd2006-02-15 09:40:43 +00002475 // Call listing callback
Linus Walleij9b28da32006-03-16 13:47:58 +00002476 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
Linus Walleijb9256fd2006-02-15 09:40:43 +00002477
2478 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00002479 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Tracklisting_With_Callback(): failed to get object info.");
Linus Walleijb9256fd2006-02-15 09:40:43 +00002480 }
2481
2482 } // Handle counting loop
2483 return retracks;
2484}
Linus Walleijdcde6082006-02-17 16:16:34 +00002485
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002486/**
2487 * This function retrieves the metadata for a single track off
2488 * the device.
2489 *
2490 * Do not call this function repeatedly! The track handles are linearly
2491 * searched O(n) and the call may involve (slow) USB traffic, so use
2492 * <code>LIBMTP_Get_Tracklisting()</code> and cache the tracks, preferably
2493 * as an efficient data structure such as a hash list.
2494 *
2495 * @param device a pointer to the device to get the track metadata from.
2496 * @param trackid the object ID of the track that you want the metadata for.
2497 * @return a track metadata entry on success or NULL on failure.
2498 * @see LIBMTP_Get_Tracklisting()
2499 */
2500LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t *device, uint32_t const trackid)
2501{
2502 uint32_t i = 0;
2503 PTPParams *params = (PTPParams *) device->params;
2504
mopoke96143402006-10-30 04:37:26 +00002505 // Get all the handles if we haven't already done that
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002506 if (params->handles.Handler == NULL) {
2507 flush_handles(device);
2508 }
2509
2510 for (i = 0; i < params->handles.n; i++) {
2511 PTPObjectInfo oi;
mopoke96143402006-10-30 04:37:26 +00002512
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002513 // Skip if this is not the track we want.
2514 if (params->handles.Handler[i] != trackid) {
2515 continue;
2516 }
2517
2518 if (ptp_getobjectinfo(params, params->handles.Handler[i], &oi) == PTP_RC_OK) {
2519 LIBMTP_track_t *track;
mopoke96143402006-10-30 04:37:26 +00002520
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002521 // Ignore stuff we don't know how to handle...
mopoke96143402006-10-30 04:37:26 +00002522 if ( oi.ObjectFormat != PTP_OFC_WAV &&
2523 oi.ObjectFormat != PTP_OFC_MP3 &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002524 oi.ObjectFormat != PTP_OFC_MTP_MP2 &&
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002525 oi.ObjectFormat != PTP_OFC_MTP_WMA &&
mopoke96143402006-10-30 04:37:26 +00002526 oi.ObjectFormat != PTP_OFC_MTP_OGG &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002527 oi.ObjectFormat != PTP_OFC_MTP_FLAC &&
2528 oi.ObjectFormat != PTP_OFC_MTP_AAC &&
2529 oi.ObjectFormat != PTP_OFC_MTP_M4A &&
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002530 oi.ObjectFormat != PTP_OFC_MTP_MP4 &&
2531 oi.ObjectFormat != PTP_OFC_MTP_UndefinedAudio ) {
2532 return NULL;
2533 }
mopoke96143402006-10-30 04:37:26 +00002534
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002535 // Allocate a new track type
2536 track = LIBMTP_new_track_t();
Linus Walleij8ab54262006-06-21 07:12:28 +00002537
2538 // This is some sort of unique ID so we can keep track of the track.
2539 track->item_id = params->handles.Handler[i];
mopoke31364442006-11-20 04:53:04 +00002540 track->parent_id = oi.ParentObject;
mopoke96143402006-10-30 04:37:26 +00002541
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002542 track->filetype = map_ptp_type_to_libmtp_type(oi.ObjectFormat);
mopoke96143402006-10-30 04:37:26 +00002543
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002544 // Original file-specific properties
2545 track->filesize = oi.ObjectCompressedSize;
2546 if (oi.Filename != NULL) {
2547 track->filename = strdup(oi.Filename);
2548 }
Linus Walleij8ab54262006-06-21 07:12:28 +00002549
2550 get_track_metadata(device, oi.ObjectFormat, track);
mopoke96143402006-10-30 04:37:26 +00002551
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002552 return track;
2553
2554 } else {
2555 return NULL;
2556 }
2557
2558 }
2559 return NULL;
2560}
2561
Linus Walleijf6bc1782006-03-24 15:12:47 +00002562
2563/**
2564 * This gets a file off the device to a local file identified
2565 * by a filename.
2566 * @param device a pointer to the device to get the track from.
2567 * @param id the file ID of the file to retrieve.
2568 * @param path a filename to use for the retrieved file.
2569 * @param callback a progress indicator function or NULL to ignore.
2570 * @param data a user-defined pointer that is passed along to
2571 * the <code>progress</code> function in order to
2572 * pass along some user defined data to the progress
2573 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00002574 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00002575 * failure.
2576 * @see LIBMTP_Get_File_To_File_Descriptor()
2577 */
mopoke96143402006-10-30 04:37:26 +00002578int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00002579 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00002580 void const * const data)
2581{
2582 int fd = -1;
2583 int ret;
2584
2585 // Sanity check
2586 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002587 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Bad arguments, path was NULL.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002588 return -1;
2589 }
2590
2591 // Open file
2592#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00002593#ifdef USE_WINDOWS_IO_H
2594 if ( (fd = _open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,_S_IREAD)) == -1 ) {
2595#else
flavienbfd80eb2006-06-01 22:41:49 +00002596 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IRWXU|S_IRGRP)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00002597#endif
Linus Walleijf6bc1782006-03-24 15:12:47 +00002598#else
2599 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP)) == -1) {
2600#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00002601 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Could not create file.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002602 return -1;
2603 }
mopoke96143402006-10-30 04:37:26 +00002604
Linus Walleijf6bc1782006-03-24 15:12:47 +00002605 ret = LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
2606
2607 // Close file
2608 close(fd);
mopoke96143402006-10-30 04:37:26 +00002609
Linus Walleijf6bc1782006-03-24 15:12:47 +00002610 return ret;
2611}
2612
2613/**
2614 * This gets a file off the device to a file identified
2615 * by a file descriptor.
Linus Walleij0558ac52006-09-07 06:55:03 +00002616 *
mopoke96143402006-10-30 04:37:26 +00002617 * This function can potentially be used for streaming
2618 * files off the device for playback or broadcast for example,
Linus Walleij0558ac52006-09-07 06:55:03 +00002619 * by downloading the file into a stream sink e.g. a socket.
2620 *
Linus Walleijf6bc1782006-03-24 15:12:47 +00002621 * @param device a pointer to the device to get the file from.
2622 * @param id the file ID of the file to retrieve.
2623 * @param fd a local file descriptor to write the file to.
2624 * @param callback a progress indicator function or NULL to ignore.
2625 * @param data a user-defined pointer that is passed along to
2626 * the <code>progress</code> function in order to
2627 * pass along some user defined data to the progress
2628 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00002629 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00002630 * failure.
2631 * @see LIBMTP_Get_File_To_File()
2632 */
mopoke96143402006-10-30 04:37:26 +00002633int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
2634 uint32_t const id,
2635 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00002636 LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00002637 void const * const data)
2638{
2639 PTPObjectInfo oi;
Linus Walleij438bd7f2006-06-08 11:35:44 +00002640 uint16_t ret;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002641 PTPParams *params = (PTPParams *) device->params;
Linus Walleijee73ef22006-08-27 19:56:00 +00002642 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002643
Linus Walleij070e9b42007-01-22 08:49:28 +00002644 ret = ptp_getobjectinfo(params, id, &oi);
2645 if (ret != PTP_RC_OK) {
2646 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002647 return -1;
2648 }
2649 if (oi.ObjectFormat == PTP_OFC_Association) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002650 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002651 return -1;
2652 }
Linus Walleijf6bc1782006-03-24 15:12:47 +00002653
Linus Walleijee73ef22006-08-27 19:56:00 +00002654 // Callbacks
2655 ptp_usb->callback_active = 1;
Linus Walleij7f0c72e2006-09-06 13:01:58 +00002656 ptp_usb->current_transfer_total = oi.ObjectCompressedSize+
2657 PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
Linus Walleijee73ef22006-08-27 19:56:00 +00002658 ptp_usb->current_transfer_complete = 0;
2659 ptp_usb->current_transfer_callback = callback;
2660 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00002661
Linus Walleij96c62432006-08-21 10:04:02 +00002662 // This now exist in upstream
2663 ret = ptp_getobject_tofd(params, id, fd);
Linus Walleijee73ef22006-08-27 19:56:00 +00002664
2665 ptp_usb->callback_active = 0;
2666 ptp_usb->current_transfer_callback = NULL;
2667 ptp_usb->current_transfer_callback_data = NULL;
mopoke96143402006-10-30 04:37:26 +00002668
Linus Walleijb02a0662006-04-25 08:05:09 +00002669 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002670 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00002671 return -1;
2672 }
mopoke96143402006-10-30 04:37:26 +00002673
Linus Walleijf6bc1782006-03-24 15:12:47 +00002674 return 0;
2675}
2676
Linus Walleijdcde6082006-02-17 16:16:34 +00002677/**
2678 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00002679 * by a filename. This is actually just a wrapper for the
2680 * \c LIBMTP_Get_Track_To_File() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00002681 * @param device a pointer to the device to get the track from.
2682 * @param id the track ID of the track to retrieve.
2683 * @param path a filename to use for the retrieved track.
2684 * @param callback a progress indicator function or NULL to ignore.
2685 * @param data a user-defined pointer that is passed along to
2686 * the <code>progress</code> function in order to
2687 * pass along some user defined data to the progress
2688 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00002689 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00002690 * failure.
2691 * @see LIBMTP_Get_Track_To_File_Descriptor()
2692 */
mopoke96143402006-10-30 04:37:26 +00002693int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00002694 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00002695 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00002696{
Linus Walleijf6bc1782006-03-24 15:12:47 +00002697 // This is just a wrapper
2698 return LIBMTP_Get_File_To_File(device, id, path, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00002699}
2700
2701/**
2702 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00002703 * by a file descriptor. This is actually just a wrapper for
2704 * the \c LIBMTP_Get_File_To_File_Descriptor() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00002705 * @param device a pointer to the device to get the track from.
2706 * @param id the track ID of the track to retrieve.
2707 * @param fd a file descriptor to write the track to.
2708 * @param callback a progress indicator function or NULL to ignore.
2709 * @param data a user-defined pointer that is passed along to
2710 * the <code>progress</code> function in order to
2711 * pass along some user defined data to the progress
2712 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00002713 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00002714 * failure.
2715 * @see LIBMTP_Get_Track_To_File()
2716 */
mopoke96143402006-10-30 04:37:26 +00002717int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
2718 uint32_t const id,
2719 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00002720 LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00002721 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00002722{
Linus Walleijf6bc1782006-03-24 15:12:47 +00002723 // This is just a wrapper
2724 return LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00002725}
Linus Walleij394bbbe2006-02-22 16:10:53 +00002726
2727/**
2728 * This function sends a track from a local file to an
2729 * MTP device. A filename and a set of metadata must be
2730 * given as input.
2731 * @param device a pointer to the device to send the track to.
2732 * @param path the filename of a local file which will be sent.
2733 * @param metadata a track metadata set to be written along with the file.
2734 * @param callback a progress indicator function or NULL to ignore.
2735 * @param data a user-defined pointer that is passed along to
2736 * the <code>progress</code> function in order to
2737 * pass along some user defined data to the progress
2738 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00002739 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00002740 * in. Since some devices are a bit picky about where files
2741 * are placed, a default folder will be chosen if libmtp
2742 * has detected one for the current filetype and this
2743 * parameter is set to 0. If this is 0 and no default folder
2744 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00002745 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00002746 * failure.
2747 * @see LIBMTP_Send_Track_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00002748 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00002749 */
mopoke96143402006-10-30 04:37:26 +00002750int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00002751 char const * const path, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00002752 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00002753 void const * const data, uint32_t const parenthandle)
Linus Walleij394bbbe2006-02-22 16:10:53 +00002754{
2755 int fd;
2756 int ret;
2757
2758 // Sanity check
2759 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002760 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File(): Bad arguments, path was NULL.");
Linus Walleij394bbbe2006-02-22 16:10:53 +00002761 return -1;
2762 }
2763
2764 // Open file
2765#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00002766#ifdef USE_WINDOWS_IO_H
2767 if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1 ) {
2768#else
Linus Walleij394bbbe2006-02-22 16:10:53 +00002769 if ( (fd = open(path, O_RDONLY|O_BINARY) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00002770#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00002771#else
2772 if ( (fd = open(path, O_RDONLY)) == -1) {
2773#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00002774 printf("LIBMTP_Send_Track_From_File(): Could not open source file \"%s\"\n", path);
Linus Walleij394bbbe2006-02-22 16:10:53 +00002775 return -1;
2776 }
2777
Linus Walleijd6a49972006-05-02 08:24:54 +00002778 ret = LIBMTP_Send_Track_From_File_Descriptor(device, fd, metadata, callback, data, parenthandle);
mopoke96143402006-10-30 04:37:26 +00002779
Linus Walleij394bbbe2006-02-22 16:10:53 +00002780 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00002781#ifdef USE_WINDOWS_IO_H
2782 _close(fd);
2783#else
Linus Walleij394bbbe2006-02-22 16:10:53 +00002784 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00002785#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00002786
2787 return ret;
2788}
2789
Linus Walleijfa1374c2006-02-27 07:41:46 +00002790
Linus Walleij99310d42006-11-01 08:29:39 +00002791static MTPPropList *New_MTP_Prop_Entry()
2792{
2793 MTPPropList *prop;
2794 prop = (MTPPropList *) malloc(sizeof(MTPPropList));
2795 prop->property = PTP_OPC_StorageID; /* Should be "unknown" */
2796 prop->datatype = PTP_DTC_UNDEF;
Richard Low6c0a6ce2006-11-26 10:42:08 +00002797 prop->ObjectHandle = 0x00000000U;
Linus Walleij99310d42006-11-01 08:29:39 +00002798 prop->next = NULL;
2799 return prop;
2800}
2801
2802static void Destroy_MTP_Prop_Entry(MTPPropList *prop)
2803{
2804 if (prop->datatype == PTP_DTC_STR) {
2805 free(prop->propval.str);
2806 }
2807 free(prop);
2808}
2809
Linus Walleijfa1374c2006-02-27 07:41:46 +00002810/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00002811 * This function sends a track from a file descriptor to an
2812 * MTP device. A filename and a set of metadata must be
2813 * given as input.
2814 * @param device a pointer to the device to send the track to.
2815 * @param fd the filedescriptor for a local file which will be sent.
2816 * @param metadata a track metadata set to be written along with the file.
2817 * After this call the field <code>item_id</code>
2818 * will contain the new track ID.
2819 * @param callback a progress indicator function or NULL to ignore.
2820 * @param data a user-defined pointer that is passed along to
2821 * the <code>progress</code> function in order to
2822 * pass along some user defined data to the progress
2823 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00002824 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00002825 * in. Since some devices are a bit picky about where files
2826 * are placed, a default folder will be chosen if libmtp
2827 * has detected one for the current filetype and this
2828 * parameter is set to 0. If this is 0 and no default folder
2829 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00002830 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00002831 * failure.
2832 * @see LIBMTP_Send_Track_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00002833 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00002834 */
mopoke96143402006-10-30 04:37:26 +00002835int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00002836 int const fd, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00002837 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00002838 void const * const data, uint32_t const parenthandle)
Linus Walleij394bbbe2006-02-22 16:10:53 +00002839{
Linus Walleij394bbbe2006-02-22 16:10:53 +00002840 uint16_t ret;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002841 uint32_t store = get_first_storageid(device);
Linus Walleij17e39f72006-02-23 15:54:28 +00002842 int subcall_ret;
Linus Walleij9b28da32006-03-16 13:47:58 +00002843 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd6a49972006-05-02 08:24:54 +00002844 uint32_t localph = parenthandle;
Linus Walleijd214b9b2006-08-26 22:08:37 +00002845 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij99310d42006-11-01 08:29:39 +00002846 uint8_t nonconsumable = 0x00U; /* By default it is consumable */
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002847 uint32_t i = 0;
mopoke31364442006-11-20 04:53:04 +00002848
Linus Walleijf5fcda32006-12-03 22:31:02 +00002849 subcall_ret = check_if_file_fits(device, metadata->filesize);
2850 if (subcall_ret != 0) {
2851 return -1;
2852 }
2853
Linus Walleij05ccbe72006-06-13 07:46:58 +00002854 if (localph == 0) {
2855 localph = device->default_music_folder;
2856 }
2857
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002858 // Sanity check: no zerolength files
2859 if (metadata->filesize == 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002860 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File_Descriptor(): File of zero size.");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002861 return -1;
2862 }
2863
Linus Walleij16c51f02006-05-04 13:20:22 +00002864 // Sanity check, is this really a track?
2865 if (metadata->filetype != LIBMTP_FILETYPE_WAV &&
2866 metadata->filetype != LIBMTP_FILETYPE_MP3 &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002867 metadata->filetype != LIBMTP_FILETYPE_MP2 &&
Linus Walleij16c51f02006-05-04 13:20:22 +00002868 metadata->filetype != LIBMTP_FILETYPE_WMA &&
2869 metadata->filetype != LIBMTP_FILETYPE_OGG &&
Linus Walleij5fb47132006-12-30 15:35:48 +00002870 metadata->filetype != LIBMTP_FILETYPE_FLAC &&
2871 metadata->filetype != LIBMTP_FILETYPE_AAC &&
2872 metadata->filetype != LIBMTP_FILETYPE_M4A &&
Linus Walleij16c51f02006-05-04 13:20:22 +00002873 metadata->filetype != LIBMTP_FILETYPE_MP4 &&
2874 metadata->filetype != LIBMTP_FILETYPE_UNDEF_AUDIO) {
2875 printf("LIBMTP_Send_Track_From_File_Descriptor: I don't think this is actually a track, strange filetype...\n");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00002876 nonconsumable = 0x01U; /* Not suitable for consumption, atleast it's no track! */
Linus Walleij20698482006-11-24 20:54:21 +00002877 } else if (metadata->filetype == LIBMTP_FILETYPE_UNDEF_AUDIO) {
Linus Walleij99310d42006-11-01 08:29:39 +00002878 nonconsumable = 0x01U; /* Not suitable for consumption */
2879 }
Linus Walleij37253292006-10-11 08:38:14 +00002880
Linus Walleij99310d42006-11-01 08:29:39 +00002881#ifdef ENABLE_MTP_ENHANCED
2882 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
2883 /*
2884 * MTP enhanched does it this way (from a sniff):
2885 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
2886 * 20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
2887 * FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
2888 * Length: 0x00000020
2889 * Type: 0x0001 PTP_USB_CONTAINER_COMMAND
2890 * Code: 0x9808
2891 * Transaction ID: 0x0000001B
2892 * Param1: 0x00010001 <- store
2893 * Param2: 0xffffffff <- parent handle (-1 ?)
2894 * Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
2895 * Param4: 0x00000000 <- file length MSB (-0x0c header len)
2896 * Param5: 0x00005e12 <- file length LSB (-0x0c header len)
2897 *
2898 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
2899 * 46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
2900 * 00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
2901 * 00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
2902 * 00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
2903 * 00 4F DC 02 00 01 - dc4f = non consumable
2904 * Length: 0x00000046
2905 * Type: 0x0002 PTP_USB_CONTAINER_DATA
2906 * Code: 0x9808
2907 * Transaction ID: 0x0000001B
2908 * Metadata....
2909 * 0x00000003 <- Number of metadata items
2910 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
2911 * 0xdc07 <- metadata type: file name
2912 * 0xffff <- metadata type: string
2913 * 0x0d <- number of (uint16_t) characters
2914 * 4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
2915 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
2916 * 0xdc03 <- metadata type: protection status
2917 * 0x0004 <- metadata type: uint16_t
2918 * 0x0000 <- not protected
2919 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
2920 * 0xdc4f <- non consumable
2921 * 0x0002 <- metadata type: uint8_t
2922 * 0x01 <- non-consumable (this device cannot display PDF)
2923 *
2924 * <- Read 0x18 bytes back
2925 * 18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
2926 * 00 00 00 00 01 40 00 00
2927 * Length: 0x000000018
2928 * Type: 0x0003 PTP_USB_CONTAINER_RESPONSE
2929 * Code: 0x2001 PTP_OK
2930 * Transaction ID: 0x0000001B
2931 * Param1: 0x00010001 <- store
2932 * Param2: 0x00000000 <- parent handle
2933 * Param3: 0x00004001 <- new file/object ID
2934 *
2935 * -> PTP_OC_SendObject (0x100d)
2936 * 0C 00 00 00 01 00 0D 10 1C 00 00 00
2937 * -> ... all the bytes ...
2938 * <- Read 0x0c bytes back
2939 * 0C 00 00 00 03 00 01 20 1C 00 00 00
2940 * ... Then update metadata one-by one, actually (instead of sending it first!) ...
2941 */
Richard Low4c60f6e2006-11-07 20:36:42 +00002942 MTPPropList *proplist = NULL;
2943 MTPPropList *prop = NULL;
2944 MTPPropList *previous = NULL;
Linus Walleijf8574f02006-12-04 19:42:28 +00002945 uint16_t *props = NULL;
2946 uint32_t propcnt = 0;
Linus Walleij37253292006-10-11 08:38:14 +00002947
rreardon5332f9c2006-12-05 10:18:23 +00002948 /* Send an object property list of that is supported */
mopoke31364442006-11-20 04:53:04 +00002949
Richard Low91354812006-11-17 22:44:05 +00002950 // default handle
2951 if (localph == 0)
2952 localph = 0xFFFFFFFFU; // Set to -1
mopoke31364442006-11-20 04:53:04 +00002953
Linus Walleij99310d42006-11-01 08:29:39 +00002954 metadata->item_id = 0x00000000U;
mopoke31364442006-11-20 04:53:04 +00002955
Richard Low4c60f6e2006-11-07 20:36:42 +00002956 ret = ptp_mtp_getobjectpropssupported (params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00002957
Richard Low4c60f6e2006-11-07 20:36:42 +00002958 if (ret == PTP_RC_OK)
2959 {
2960 for (i=0;i<propcnt;i++) {
2961 switch (props[i]) {
2962 case PTP_OPC_ObjectFileName:
2963 prop = New_MTP_Prop_Entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00002964 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00002965 prop->property = PTP_OPC_ObjectFileName;
2966 prop->datatype = PTP_DTC_STR;
2967 prop->propval.str = strdup(metadata->filename);
mopoke96143402006-10-30 04:37:26 +00002968
Richard Low4c60f6e2006-11-07 20:36:42 +00002969 if (previous != NULL)
2970 previous->next = prop;
2971 else
2972 proplist = prop;
2973 previous = prop;
2974 prop->next = NULL;
2975 break;
2976 case PTP_OPC_ProtectionStatus:
2977 prop = New_MTP_Prop_Entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00002978 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00002979 prop->property = PTP_OPC_ProtectionStatus;
2980 prop->datatype = PTP_DTC_UINT16;
2981 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleij99310d42006-11-01 08:29:39 +00002982
Richard Low4c60f6e2006-11-07 20:36:42 +00002983 if (previous != NULL)
2984 previous->next = prop;
2985 else
2986 proplist = prop;
2987 previous = prop;
2988 prop->next = NULL;
2989 break;
2990 case PTP_OPC_NonConsumable:
2991 prop = New_MTP_Prop_Entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00002992 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00002993 prop->property = PTP_OPC_NonConsumable;
2994 prop->datatype = PTP_DTC_UINT8;
2995 prop->propval.u8 = nonconsumable;
2996
2997 if (previous != NULL)
2998 previous->next = prop;
2999 else
3000 proplist = prop;
3001 previous = prop;
3002 prop->next = NULL;
3003 break;
3004 }
3005 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00003006 free(props);
Richard Low4c60f6e2006-11-07 20:36:42 +00003007 }
3008
mopoke31364442006-11-20 04:53:04 +00003009
Linus Walleij99310d42006-11-01 08:29:39 +00003010 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &metadata->item_id,
3011 map_libmtp_type_to_ptp_type(metadata->filetype),
3012 metadata->filesize, proplist);
3013
3014 /* Free property list */
3015 prop = proplist;
3016 while (prop != NULL) {
Linus Walleij3fcfea52006-11-13 07:07:36 +00003017 previous = prop;
Linus Walleij99310d42006-11-01 08:29:39 +00003018 prop = prop->next;
Linus Walleij3fcfea52006-11-13 07:07:36 +00003019 Destroy_MTP_Prop_Entry(previous);
Linus Walleij99310d42006-11-01 08:29:39 +00003020 }
3021
3022 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003023 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: Could not send object property list.");
Linus Walleij99310d42006-11-01 08:29:39 +00003024 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003025 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00003026 }
3027 return -1;
3028 }
3029 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
3030#else // !ENABLE_MTP_ENHANCED
3031 {
3032#endif // ENABLE_MTP_ENHANCED
3033 PTPObjectInfo new_track;
Richard Lowab0d22d2006-11-30 22:17:49 +00003034
3035 memset(&new_track, 0, sizeof(PTPObjectInfo));
Linus Walleij99310d42006-11-01 08:29:39 +00003036
3037 /* Else use the fallback compatibility mode */
3038 new_track.Filename = metadata->filename;
3039 new_track.ObjectCompressedSize = metadata->filesize;
3040 new_track.ObjectFormat = map_libmtp_type_to_ptp_type(metadata->filetype);
Richard Lowaf20b5d2006-12-17 18:00:59 +00003041 new_track.StorageID = store;
3042 new_track.ParentObject = parenthandle;
Richard Low021421e2007-01-01 18:08:57 +00003043
Linus Walleij99310d42006-11-01 08:29:39 +00003044 // Create the object
3045 ret = ptp_sendobjectinfo(params, &store, &localph, &metadata->item_id, &new_track);
Richard Low021421e2007-01-01 18:08:57 +00003046
Linus Walleij99310d42006-11-01 08:29:39 +00003047 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003048 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: Could not send object info.");
Linus Walleij99310d42006-11-01 08:29:39 +00003049 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003050 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00003051 }
3052 return -1;
3053 }
Linus Walleij394bbbe2006-02-22 16:10:53 +00003054 }
3055
Linus Walleijee73ef22006-08-27 19:56:00 +00003056 // Callbacks
Linus Walleijd214b9b2006-08-26 22:08:37 +00003057 ptp_usb->callback_active = 1;
Linus Walleij7f0c72e2006-09-06 13:01:58 +00003058 // The callback will deactivate itself after this amount of data has been sent
3059 // One BULK header for the request, one for the data phase. No parameters to the request.
3060 ptp_usb->current_transfer_total = metadata->filesize+PTP_USB_BULK_HDR_LEN*2;
Linus Walleijd214b9b2006-08-26 22:08:37 +00003061 ptp_usb->current_transfer_complete = 0;
3062 ptp_usb->current_transfer_callback = callback;
3063 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00003064
Linus Walleije7f44be2006-08-25 19:32:29 +00003065 ret = ptp_sendobject_fromfd(params, fd, metadata->filesize);
mopoke96143402006-10-30 04:37:26 +00003066
Linus Walleijee73ef22006-08-27 19:56:00 +00003067 ptp_usb->callback_active = 0;
3068 ptp_usb->current_transfer_callback = NULL;
3069 ptp_usb->current_transfer_callback_data = NULL;
3070
Linus Walleije7f44be2006-08-25 19:32:29 +00003071 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003072 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: Could not send object.");
Linus Walleij394bbbe2006-02-22 16:10:53 +00003073 return -1;
3074 }
mopoke96143402006-10-30 04:37:26 +00003075
Linus Walleij17e39f72006-02-23 15:54:28 +00003076 // Set track metadata for the new fine track
3077 subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
3078 if (subcall_ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003079 // Subcall will add error to errorstack
Linus Walleij438bd7f2006-06-08 11:35:44 +00003080 (void) LIBMTP_Delete_Object(device, metadata->item_id);
Linus Walleij17e39f72006-02-23 15:54:28 +00003081 return -1;
3082 }
Linus Walleij99310d42006-11-01 08:29:39 +00003083 if (nonconsumable != 0x00U) {
3084 /* Flag it as non-consumable if it is */
Linus Walleij9901e222006-11-30 12:28:19 +00003085 subcall_ret = set_object_u8(device, metadata->item_id, PTP_OPC_NonConsumable, nonconsumable);
Linus Walleij99310d42006-11-01 08:29:39 +00003086 if (subcall_ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003087 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set non-consumable status.");
Linus Walleij99310d42006-11-01 08:29:39 +00003088 return -1;
mopoke31364442006-11-20 04:53:04 +00003089 }
Linus Walleij99310d42006-11-01 08:29:39 +00003090 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00003091
3092 // Added object so flush handles
3093 flush_handles(device);
mopoke96143402006-10-30 04:37:26 +00003094
Linus Walleij17e39f72006-02-23 15:54:28 +00003095 return 0;
3096}
3097
Linus Walleijd6a49972006-05-02 08:24:54 +00003098/**
mopoke96143402006-10-30 04:37:26 +00003099 * This function sends a local file to an MTP device.
Linus Walleijd6a49972006-05-02 08:24:54 +00003100 * A filename and a set of metadata must be
3101 * given as input.
3102 * @param device a pointer to the device to send the track to.
3103 * @param path the filename of a local file which will be sent.
3104 * @param filedata a file strtuct to pass in info about the file.
3105 * After this call the field <code>item_id</code>
3106 * will contain the new file ID.
3107 * @param callback a progress indicator function or NULL to ignore.
3108 * @param data a user-defined pointer that is passed along to
3109 * the <code>progress</code> function in order to
3110 * pass along some user defined data to the progress
3111 * updates. If not used, set this to NULL.
3112 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003113 * in. Since some devices are a bit picky about where files
3114 * are placed, a default folder will be chosen if libmtp
3115 * has detected one for the current filetype and this
3116 * parameter is set to 0. If this is 0 and no default folder
3117 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003118 * @return 0 if the transfer was successful, any other value means
Linus Walleijd6a49972006-05-02 08:24:54 +00003119 * failure.
3120 * @see LIBMTP_Send_File_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003121 * @see LIBMTP_Delete_Object()
Linus Walleijd6a49972006-05-02 08:24:54 +00003122 */
mopoke96143402006-10-30 04:37:26 +00003123int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleijd6a49972006-05-02 08:24:54 +00003124 char const * const path, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003125 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003126 void const * const data, uint32_t const parenthandle)
3127{
3128 int fd;
3129 int ret;
3130
3131 // Sanity check
3132 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003133 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Bad arguments, path was NULL.");
Linus Walleijd6a49972006-05-02 08:24:54 +00003134 return -1;
3135 }
3136
3137 // Open file
3138#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00003139#ifdef USE_WINDOWS_IO_H
3140 if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1 ) {
3141#else
Linus Walleijd6a49972006-05-02 08:24:54 +00003142 if ( (fd = open(path, O_RDONLY|O_BINARY) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00003143#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00003144#else
3145 if ( (fd = open(path, O_RDONLY)) == -1) {
3146#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00003147 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Could not open source file.");
Linus Walleijd6a49972006-05-02 08:24:54 +00003148 return -1;
3149 }
3150
3151 ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data, parenthandle);
mopoke96143402006-10-30 04:37:26 +00003152
Linus Walleijd6a49972006-05-02 08:24:54 +00003153 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00003154#ifdef USE_WINDOWS_IO_H
3155 _close(fd);
3156#else
Linus Walleijd6a49972006-05-02 08:24:54 +00003157 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00003158#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00003159
3160 return ret;
3161}
3162
Linus Walleijd208f9c2006-04-27 14:16:06 +00003163/**
3164 * This function sends a generic file from a file descriptor to an
3165 * MTP device. A filename and a set of metadata must be
3166 * given as input.
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003167 *
3168 * This can potentially be used for sending in a stream of unknown
3169 * length. Set <code>filedata->filesize = (uint64_t) -1</code> to
3170 * make libmtp send some dummy length to the device and just
3171 * accept a stream up to some device-determined max length. There
3172 * is not guarantee this will work on all devices... Remember to
3173 * set correct metadata for the track with
3174 * <code>LIBMTP_Update_Track_Metadata()</code> afterwards if it's
3175 * a music file. (This doesn't seem to work very well right now.)
3176 *
Linus Walleijd208f9c2006-04-27 14:16:06 +00003177 * @param device a pointer to the device to send the file to.
3178 * @param fd the filedescriptor for a local file which will be sent.
Linus Walleijd6a49972006-05-02 08:24:54 +00003179 * @param filedata a file strtuct to pass in info about the file.
Linus Walleijd208f9c2006-04-27 14:16:06 +00003180 * After this call the field <code>item_id</code>
3181 * will contain the new track ID.
3182 * @param callback a progress indicator function or NULL to ignore.
3183 * @param data a user-defined pointer that is passed along to
3184 * the <code>progress</code> function in order to
3185 * pass along some user defined data to the progress
3186 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00003187 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003188 * in. Since some devices are a bit picky about where files
3189 * are placed, a default folder will be chosen if libmtp
3190 * has detected one for the current filetype and this
3191 * parameter is set to 0. If this is 0 and no default folder
3192 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003193 * @return 0 if the transfer was successful, any other value means
Linus Walleijd208f9c2006-04-27 14:16:06 +00003194 * failure.
Linus Walleijd6a49972006-05-02 08:24:54 +00003195 * @see LIBMTP_Send_File_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003196 * @see LIBMTP_Delete_Object()
Linus Walleijd208f9c2006-04-27 14:16:06 +00003197 */
mopoke96143402006-10-30 04:37:26 +00003198int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleijd208f9c2006-04-27 14:16:06 +00003199 int const fd, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003200 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003201 void const * const data, uint32_t const parenthandle)
Linus Walleijd208f9c2006-04-27 14:16:06 +00003202{
3203 uint16_t ret;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003204 uint32_t store = get_first_storageid(device);
Linus Walleijd6a49972006-05-02 08:24:54 +00003205 uint32_t localph = parenthandle;
Linus Walleijd208f9c2006-04-27 14:16:06 +00003206 PTPObjectInfo new_file;
3207 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd214b9b2006-08-26 22:08:37 +00003208 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
rreardonbbb4e562006-11-19 22:16:11 +00003209 int i;
Linus Walleijf5fcda32006-12-03 22:31:02 +00003210 int subcall_ret;
rreardonbbb4e562006-11-19 22:16:11 +00003211 uint16_t *props = NULL;
3212 uint32_t propcnt = 0;
Linus Walleij7430b1b2006-11-21 09:25:55 +00003213 uint8_t nonconsumable = 0x01U; /* By default it is non-consumable */
Richard Lowab0d22d2006-11-30 22:17:49 +00003214
Linus Walleijf5fcda32006-12-03 22:31:02 +00003215 subcall_ret = check_if_file_fits(device, filedata->filesize);
3216 if (subcall_ret != 0) {
3217 return -1;
3218 }
3219
Richard Lowab0d22d2006-11-30 22:17:49 +00003220 memset(&new_file, 0, sizeof(PTPObjectInfo));
Linus Walleij05ccbe72006-06-13 07:46:58 +00003221
Linus Walleijd208f9c2006-04-27 14:16:06 +00003222 new_file.Filename = filedata->filename;
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003223 if (filedata->filesize == (uint64_t) -1) {
3224 // This is a stream. Set a dummy length...
3225 new_file.ObjectCompressedSize = 1;
3226 } else {
Linus Walleij20698482006-11-24 20:54:21 +00003227 // Sanity check: no zerolength files
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003228 if (filedata->filesize == 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003229 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File_Descriptor(): File of zero size.");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003230 return -1;
3231 }
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003232 new_file.ObjectCompressedSize = filedata->filesize;
3233 }
Linus Walleij16c51f02006-05-04 13:20:22 +00003234 new_file.ObjectFormat = map_libmtp_type_to_ptp_type(filedata->filetype);
Linus Walleijd208f9c2006-04-27 14:16:06 +00003235
mopoke96143402006-10-30 04:37:26 +00003236 /*
Linus Walleij7430b1b2006-11-21 09:25:55 +00003237 * If this file is among the supported filetypes for this device,
3238 * then it is indeed consumable.
3239 */
3240 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
3241 if (params->deviceinfo.ImageFormats[i] == new_file.ObjectFormat) {
3242 nonconsumable = 0x00U;
3243 break;
3244 }
3245 }
3246
3247 /*
Linus Walleij05ccbe72006-06-13 07:46:58 +00003248 * If no destination folder was given, look up a default
3249 * folder if possible. Perhaps there is some way of retrieveing
3250 * the default folder for different forms of content, what
3251 * do I know, we use a fixed list in lack of any better method.
3252 * Some devices obviously need to have their files in certain
mopoke96143402006-10-30 04:37:26 +00003253 * folders in order to find/display them at all (hello Creative),
Linus Walleij05ccbe72006-06-13 07:46:58 +00003254 * so we have to have a method for this.
3255 */
3256
3257 if (localph == 0) {
3258 uint16_t of = new_file.ObjectFormat;
3259 if (of == PTP_OFC_WAV ||
3260 of == PTP_OFC_MP3 ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003261 of == PTP_OFC_MTP_MP2 ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003262 of == PTP_OFC_MTP_WMA ||
3263 of == PTP_OFC_MTP_OGG ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003264 of == PTP_OFC_MTP_FLAC ||
3265 of == PTP_OFC_MTP_AAC ||
3266 of == PTP_OFC_MTP_M4A ||
3267 of == PTP_OFC_AIFF ||
3268 //of == PTP_OFC_MTP_MP4 || /* ambiguous mp4 can contain video */
3269 of == PTP_OFC_MTP_AudibleCodec ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003270 of == PTP_OFC_MTP_UndefinedAudio) {
3271 localph = device->default_music_folder;
3272 } else if (of == PTP_OFC_MTP_WMV ||
3273 of == PTP_OFC_AVI ||
3274 of == PTP_OFC_MPEG ||
3275 of == PTP_OFC_ASF ||
3276 of == PTP_OFC_QT ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003277 of == PTP_OFC_MTP_3GP ||
3278 of == PTP_OFC_MTP_MP4 || /* ambiguous mp4 can also contain only audio */
Linus Walleij05ccbe72006-06-13 07:46:58 +00003279 of == PTP_OFC_MTP_UndefinedVideo) {
3280 localph = device->default_video_folder;
3281 } else if (of == PTP_OFC_EXIF_JPEG ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003282 of == PTP_OFC_JP2 ||
3283 of == PTP_OFC_JPX ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003284 of == PTP_OFC_JFIF ||
3285 of == PTP_OFC_TIFF ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003286 of == PTP_OFC_TIFF_IT ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003287 of == PTP_OFC_BMP ||
3288 of == PTP_OFC_GIF ||
3289 of == PTP_OFC_PICT ||
3290 of == PTP_OFC_PNG ||
3291 of == PTP_OFC_MTP_WindowsImageFormat) {
3292 localph = device->default_picture_folder;
3293 } else if (of == PTP_OFC_MTP_vCalendar1 ||
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003294 of == PTP_OFC_MTP_vCalendar2 ||
3295 of == PTP_OFC_MTP_UndefinedContact ||
3296 of == PTP_OFC_MTP_vCard2 ||
3297 of == PTP_OFC_MTP_vCard3 ||
3298 of == PTP_OFC_MTP_UndefinedCalendarItem) {
Linus Walleij05ccbe72006-06-13 07:46:58 +00003299 localph = device->default_organizer_folder;
Linus Walleij5fb47132006-12-30 15:35:48 +00003300 } else if (of == PTP_OFC_Text
3301 ) {
Linus Walleij9316e652006-12-07 09:55:21 +00003302 localph = device->default_text_folder;
Linus Walleij05ccbe72006-06-13 07:46:58 +00003303 }
3304 }
mopoke31364442006-11-20 04:53:04 +00003305
rreardonbbb4e562006-11-19 22:16:11 +00003306#ifdef ENABLE_MTP_ENHANCED
3307 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
mopoke31364442006-11-20 04:53:04 +00003308
rreardonbbb4e562006-11-19 22:16:11 +00003309 MTPPropList *proplist = NULL;
3310 MTPPropList *prop = NULL;
3311 MTPPropList *previous = NULL;
Richard Low6c0a6ce2006-11-26 10:42:08 +00003312
3313 // Must be 0x00000000U for new objects
3314 filedata->item_id = 0x00000000U;
Linus Walleij05ccbe72006-06-13 07:46:58 +00003315
rreardonbbb4e562006-11-19 22:16:11 +00003316 ret = ptp_mtp_getobjectpropssupported(params, new_file.ObjectFormat, &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00003317
rreardonbbb4e562006-11-19 22:16:11 +00003318 for (i=0;i<propcnt;i++) {
3319 switch (props[i]) {
3320 case PTP_OPC_ObjectFileName:
3321 prop = New_MTP_Prop_Entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003322 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003323 prop->property = PTP_OPC_ObjectFileName;
3324 prop->datatype = PTP_DTC_STR;
3325 prop->propval.str = strdup(new_file.Filename);
Linus Walleij9be685b2006-11-21 09:44:53 +00003326
rreardonbbb4e562006-11-19 22:16:11 +00003327 if (previous != NULL)
3328 previous->next = prop;
3329 else
3330 proplist = prop;
3331 previous = prop;
3332 prop->next = NULL;
3333 break;
3334 case PTP_OPC_ProtectionStatus:
3335 prop = New_MTP_Prop_Entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003336 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003337 prop->property = PTP_OPC_ProtectionStatus;
3338 prop->datatype = PTP_DTC_UINT16;
3339 prop->propval.u16 = 0x0000U; /* Not protected */
mopoke31364442006-11-20 04:53:04 +00003340
rreardonbbb4e562006-11-19 22:16:11 +00003341 if (previous != NULL)
3342 previous->next = prop;
3343 else
3344 proplist = prop;
3345 previous = prop;
3346 prop->next = NULL;
3347 break;
3348 case PTP_OPC_NonConsumable:
3349 prop = New_MTP_Prop_Entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003350 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003351 prop->property = PTP_OPC_NonConsumable;
3352 prop->datatype = PTP_DTC_UINT8;
3353 prop->propval.u8 = nonconsumable;
mopoke31364442006-11-20 04:53:04 +00003354
rreardonbbb4e562006-11-19 22:16:11 +00003355 if (previous != NULL)
3356 previous->next = prop;
3357 else
3358 proplist = prop;
3359 previous = prop;
3360 prop->next = NULL;
3361 break;
3362 case PTP_OPC_Name:
3363 prop = New_MTP_Prop_Entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003364 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003365 prop->property = PTP_OPC_Name;
3366 prop->datatype = PTP_DTC_STR;
3367 prop->propval.str = strdup(filedata->filename);
mopoke31364442006-11-20 04:53:04 +00003368
rreardonbbb4e562006-11-19 22:16:11 +00003369 if (previous != NULL)
3370 previous->next = prop;
3371 else
3372 proplist = prop;
3373 previous = prop;
3374 prop->next = NULL;
3375 break;
3376 }
Linus Walleij99310d42006-11-01 08:29:39 +00003377 }
rreardonbbb4e562006-11-19 22:16:11 +00003378 free(props);
mopoke31364442006-11-20 04:53:04 +00003379
Linus Walleij9be685b2006-11-21 09:44:53 +00003380 // default handle
3381 if (localph == 0)
3382 localph = 0xFFFFFFFFU; // Set to -1
3383
rreardonbbb4e562006-11-19 22:16:11 +00003384 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id,
3385 new_file.ObjectFormat,
3386 new_file.ObjectCompressedSize, proplist);
mopoke31364442006-11-20 04:53:04 +00003387
rreardonbbb4e562006-11-19 22:16:11 +00003388 /* Free property list */
3389 prop = proplist;
3390 while (prop != NULL) {
3391 previous = prop;
3392 prop = prop->next;
3393 Destroy_MTP_Prop_Entry(previous);
3394 }
mopoke31364442006-11-20 04:53:04 +00003395
rreardonbbb4e562006-11-19 22:16:11 +00003396 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003397 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File(): Could not send object property list.");
rreardonbbb4e562006-11-19 22:16:11 +00003398 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003399 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardonbbb4e562006-11-19 22:16:11 +00003400 }
3401 return -1;
3402 }
3403 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
3404#else // !ENABLE_MTP_ENHANCED
3405 {
3406#endif // ENABLE_MTP_ENHANCED
Linus Walleijee73ef22006-08-27 19:56:00 +00003407
Linus Walleij9be685b2006-11-21 09:44:53 +00003408 // Create the object
3409 ret = ptp_sendobjectinfo(params, &store, &localph, &filedata->item_id, &new_file);
3410 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003411 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Could not send object info.");
Linus Walleij9be685b2006-11-21 09:44:53 +00003412 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003413 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij9be685b2006-11-21 09:44:53 +00003414 }
3415 return -1;
3416 }
rreardonbbb4e562006-11-19 22:16:11 +00003417 }
Richard Low021421e2007-01-01 18:08:57 +00003418
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003419 if (filedata->filesize != (uint64_t) -1) {
3420 // Callbacks
3421 ptp_usb->callback_active = 1;
Linus Walleij7f0c72e2006-09-06 13:01:58 +00003422 // The callback will deactivate itself after this amount of data has been sent
3423 // One BULK header for the request, one for the data phase. No parameters to the request.
3424 ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003425 ptp_usb->current_transfer_complete = 0;
3426 ptp_usb->current_transfer_callback = callback;
3427 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00003428
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003429 ret = ptp_sendobject_fromfd(params, fd, filedata->filesize);
mopoke96143402006-10-30 04:37:26 +00003430
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003431 ptp_usb->callback_active = 0;
3432 ptp_usb->current_transfer_callback = NULL;
3433 ptp_usb->current_transfer_callback_data = NULL;
3434 } else {
3435 // This is a stream..
Linus Walleija9310fa2006-09-04 06:47:42 +00003436 ret = ptp_sendobject_fromfd(params, fd, 0xFFFFFFFFU-PTP_USB_BULK_HDR_LEN);
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003437 if (ret == PTP_ERROR_IO) {
3438 // That's expected. The stream ends, simply...
3439 ret = PTP_RC_OK;
3440 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00003441 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Error while sending stream.");
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003442 }
3443 }
Linus Walleijee73ef22006-08-27 19:56:00 +00003444
3445 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003446 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Could not send object.");
Linus Walleijee73ef22006-08-27 19:56:00 +00003447 return -1;
3448 }
mopoke96143402006-10-30 04:37:26 +00003449
Linus Walleij438bd7f2006-06-08 11:35:44 +00003450 // Added object so flush handles.
3451 flush_handles(device);
Linus Walleijd208f9c2006-04-27 14:16:06 +00003452 return 0;
3453}
3454
Linus Walleij17e39f72006-02-23 15:54:28 +00003455/**
3456 * This function updates the MTP object metadata on a single file
3457 * identified by an object ID.
mopoke96143402006-10-30 04:37:26 +00003458 * @param device a pointer to the device to update the track
Linus Walleij95698cd2006-02-24 10:40:40 +00003459 * metadata on.
Linus Walleij17e39f72006-02-23 15:54:28 +00003460 * @param metadata a track metadata set to be written to the file.
3461 * notice that the <code>track_id</code> field of the
3462 * metadata structure must be correct so that the
Linus Walleija4982732006-02-24 15:46:02 +00003463 * function can update the right file. If some properties
3464 * of this metadata are set to NULL (strings) or 0
3465 * (numerical values) they will be discarded and the
3466 * track will not be tagged with these blank values.
Richard Lowaf20b5d2006-12-17 18:00:59 +00003467 * @return 0 on success, any other value means failure. If some
3468 * or all of the properties fail to update we will still
3469 * return success. On some devices (notably iRiver T30)
3470 * properties that exist cannot be updated.
Linus Walleij17e39f72006-02-23 15:54:28 +00003471 */
mopoke96143402006-10-30 04:37:26 +00003472int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *device,
Linus Walleij17e39f72006-02-23 15:54:28 +00003473 LIBMTP_track_t const * const metadata)
3474{
Linus Walleij17e39f72006-02-23 15:54:28 +00003475 uint16_t ret;
Linus Walleij00cf0642006-07-26 20:40:59 +00003476 PTPParams *params = (PTPParams *) device->params;
3477 uint32_t i;
3478 uint16_t *props = NULL;
3479 uint32_t propcnt = 0;
Linus Walleij17e39f72006-02-23 15:54:28 +00003480
mopoke96143402006-10-30 04:37:26 +00003481 // First see which properties can be set on this file format and apply accordingly
Linus Walleij00cf0642006-07-26 20:40:59 +00003482 // i.e only try to update this metadata for object tags that exist on the current player.
Linus Walleij304433d2007-02-26 08:02:47 +00003483 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &props);
Linus Walleij00cf0642006-07-26 20:40:59 +00003484 if (ret != PTP_RC_OK) {
3485 // Just bail out for now, nothing is ever set.
Linus Walleij304433d2007-02-26 08:02:47 +00003486 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not retrieve supported object properties.");
raveloxd9a28642006-05-26 23:42:22 +00003487 return -1;
Linus Walleij00cf0642006-07-26 20:40:59 +00003488 } else {
Linus Walleij304433d2007-02-26 08:02:47 +00003489 // We must have this operation!
3490 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
3491 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): operation PTP_OC_MTP_SetObjectPropValue "
3492 "(0x9804) is not supported by this device.");
3493 if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList)) {
3494 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "Your device supports only operation "
3495 "PTP_OC_MTP_SetObjPropList (0x9806) but we haven't\n"
3496 "implemented that operation yet. So wait or bug the libmtp development team!"
3497 "Your file is fine but we cannot set metadata.");
3498 /*
3499 * FIXME: implement support for PTP_OC_MTP_SetObjPropList and use it here.
3500 */
3501 } else {
3502 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3503 "Your device doesn't seem to support any known way of setting metadata.");
3504 }
3505 return -1;
3506 }
Linus Walleij00cf0642006-07-26 20:40:59 +00003507 for (i=0;i<propcnt;i++) {
3508 switch (props[i]) {
3509 case PTP_OPC_Name:
3510 // Update title
Linus Walleij9901e222006-11-30 12:28:19 +00003511 ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title);
Linus Walleij00cf0642006-07-26 20:40:59 +00003512 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003513 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track title.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003514 }
3515 break;
3516 case PTP_OPC_AlbumName:
3517 // Update album
Linus Walleij9901e222006-11-30 12:28:19 +00003518 ret = set_object_string(device, metadata->item_id, PTP_OPC_AlbumName, metadata->album);
Linus Walleij00cf0642006-07-26 20:40:59 +00003519 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003520 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track album name.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003521 }
3522 break;
3523 case PTP_OPC_Artist:
3524 // Update artist
Linus Walleij9901e222006-11-30 12:28:19 +00003525 ret = set_object_string(device, metadata->item_id, PTP_OPC_Artist, metadata->artist);
Linus Walleij00cf0642006-07-26 20:40:59 +00003526 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003527 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track artist name.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003528 }
3529 break;
3530 case PTP_OPC_Genre:
3531 // Update genre
Linus Walleij9901e222006-11-30 12:28:19 +00003532 ret = set_object_string(device, metadata->item_id, PTP_OPC_Genre, metadata->genre);
Linus Walleij00cf0642006-07-26 20:40:59 +00003533 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003534 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track genre name.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003535 }
3536 break;
3537 case PTP_OPC_Duration:
3538 // Update duration
3539 if (metadata->duration != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003540 ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, metadata->duration);
Linus Walleij00cf0642006-07-26 20:40:59 +00003541 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003542 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track duration.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003543 }
3544 }
3545 break;
3546 case PTP_OPC_Track:
3547 // Update track number.
3548 if (metadata->tracknumber != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003549 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, metadata->tracknumber);
Linus Walleij00cf0642006-07-26 20:40:59 +00003550 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003551 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track tracknumber.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003552 }
3553 }
3554 break;
3555 case PTP_OPC_OriginalReleaseDate:
3556 // Update creation datetime
Linus Walleij9901e222006-11-30 12:28:19 +00003557 ret = set_object_string(device, metadata->item_id, PTP_OPC_OriginalReleaseDate, metadata->date);
Linus Walleij00cf0642006-07-26 20:40:59 +00003558 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003559 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set track release date.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003560 }
3561 break;
3562 // These are, well not so important.
3563 case PTP_OPC_SampleRate:
3564 // Update sample rate
3565 if (metadata->samplerate != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003566 ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, metadata->samplerate);
Linus Walleij00cf0642006-07-26 20:40:59 +00003567 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003568 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set samplerate.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003569 }
3570 }
3571 break;
3572 case PTP_OPC_NumberOfChannels:
3573 // Update number of channels
3574 if (metadata->nochannels != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003575 ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, metadata->nochannels);
Linus Walleij00cf0642006-07-26 20:40:59 +00003576 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003577 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set number of channels.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003578 }
3579 }
3580 break;
3581 case PTP_OPC_AudioWAVECodec:
3582 // Update WAVE codec
3583 if (metadata->wavecodec != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003584 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, metadata->wavecodec);
Linus Walleij00cf0642006-07-26 20:40:59 +00003585 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003586 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set WAVE codec.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003587 }
3588 }
3589 break;
3590 case PTP_OPC_AudioBitRate:
3591 // Update bitrate
3592 if (metadata->bitrate != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003593 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, metadata->bitrate);
Linus Walleij00cf0642006-07-26 20:40:59 +00003594 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003595 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set bitrate.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003596 }
3597 }
3598 break;
3599 case PTP_OPC_BitRateType:
3600 // Update bitrate type
3601 if (metadata->bitratetype != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003602 ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, metadata->bitratetype);
Linus Walleij00cf0642006-07-26 20:40:59 +00003603 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003604 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set bitratetype.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003605 }
3606 }
3607 break;
3608 case PTP_OPC_Rating:
3609 // Update user rating
3610 // TODO: shall this be set for rating 0?
3611 if (metadata->rating != 0) {
Linus Walleij9901e222006-11-30 12:28:19 +00003612 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, metadata->rating);
Linus Walleij00cf0642006-07-26 20:40:59 +00003613 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003614 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set user rating.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003615 }
3616 }
3617 break;
3618 case PTP_OPC_UseCount:
3619 // Update use count, set even to zero if desired.
Linus Walleij9901e222006-11-30 12:28:19 +00003620 ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, metadata->usecount);
Linus Walleij00cf0642006-07-26 20:40:59 +00003621 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003622 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set use count.");
Linus Walleij00cf0642006-07-26 20:40:59 +00003623 }
3624 break;
Linus Walleij17e39f72006-02-23 15:54:28 +00003625
Linus Walleij00cf0642006-07-26 20:40:59 +00003626 // NOTE: File size is not updated, this should not change anyway.
3627 // neither will we change the filename.
3628 }
Linus Walleija4982732006-02-24 15:46:02 +00003629 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00003630 free(props);
Linus Walleij17e39f72006-02-23 15:54:28 +00003631 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00003632 return 0;
Linus Walleij394bbbe2006-02-22 16:10:53 +00003633}
Linus Walleij95698cd2006-02-24 10:40:40 +00003634
3635/**
Linus Walleij438bd7f2006-06-08 11:35:44 +00003636 * This function deletes a single file, track, playlist or
3637 * any other object off the MTP device,
Linus Walleij95698cd2006-02-24 10:40:40 +00003638 * identified by an object ID.
Linus Walleijf6bc1782006-03-24 15:12:47 +00003639 * @param device a pointer to the device to delete the file or track from.
Linus Walleij95698cd2006-02-24 10:40:40 +00003640 * @param item_id the item to delete.
3641 * @return 0 on success, any other value means failure.
3642 */
mopoke96143402006-10-30 04:37:26 +00003643int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
Linus Walleij438bd7f2006-06-08 11:35:44 +00003644 uint32_t object_id)
Linus Walleij95698cd2006-02-24 10:40:40 +00003645{
Linus Walleij438bd7f2006-06-08 11:35:44 +00003646 uint16_t ret;
3647 PTPParams *params = (PTPParams *) device->params;
3648
3649 ret = ptp_deleteobject(params, object_id, 0);
3650 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003651 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Delete_Object(): could not delete object.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00003652 return -1;
3653 }
3654 // Removed object so flush handles.
3655 flush_handles(device);
3656 return 0;
Linus Walleij95698cd2006-02-24 10:40:40 +00003657}
Linus Walleij9c6ca022006-04-21 10:24:15 +00003658
Linus Walleij9c6ca022006-04-21 10:24:15 +00003659/**
3660 * Helper function. This indicates if a track exists on the device
3661 * @param device a pointer to the device to get the track from.
3662 * @param id the track ID of the track to retrieve.
Linus Walleij070e9b42007-01-22 08:49:28 +00003663 * @return TRUE (!=0) if the track exists, FALSE (0) if not
Linus Walleij9c6ca022006-04-21 10:24:15 +00003664 */
3665int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *device,
3666 uint32_t const id)
3667{
3668 PTPObjectInfo oi;
3669 PTPParams *params = (PTPParams *) device->params;
3670
3671 if (ptp_getobjectinfo(params, id, &oi) == PTP_RC_OK) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00003672 return -1;
Linus Walleij9c6ca022006-04-21 10:24:15 +00003673 }
3674 return 0;
3675}
3676
3677/**
3678 * This creates a new folder structure and allocates memory
3679 * for it. Notice that if you add strings to this structure they
3680 * will be freed by the corresponding <code>LIBMTP_folder_track_t</code>
3681 * operation later, so be careful of using strdup() when assigning
3682 * strings, e.g.:
3683 *
3684 * @return a pointer to the newly allocated folder structure.
3685 * @see LIBMTP_destroy_folder_t()
3686 */
3687LIBMTP_folder_t *LIBMTP_new_folder_t(void)
3688{
3689 LIBMTP_folder_t *new = (LIBMTP_folder_t *) malloc(sizeof(LIBMTP_folder_t));
3690 if (new == NULL) {
3691 return NULL;
3692 }
3693 new->folder_id = 0;
3694 new->parent_id = 0;
3695 new->name = NULL;
3696 new->sibling = NULL;
3697 new->child = NULL;
3698 return new;
3699}
3700
3701/**
3702 * This recursively deletes the memory for a folder structure
3703 *
3704 * @param folder folder structure to destroy
3705 * @see LIBMTP_new_folder_t()
3706 */
3707void LIBMTP_destroy_folder_t(LIBMTP_folder_t *folder)
3708{
3709
3710 if(folder == NULL) {
3711 return;
3712 }
3713
3714 //Destroy from the bottom up
3715 if(folder->child != NULL) {
3716 LIBMTP_destroy_folder_t(folder->child);
3717 }
3718
3719 if(folder->sibling != NULL) {
3720 LIBMTP_destroy_folder_t(folder->sibling);
3721 }
3722
3723 if(folder->name != NULL) {
3724 free(folder->name);
3725 }
3726
3727 free(folder);
3728}
3729
3730/**
3731 * Helper function. Returns a folder structure for a
3732 * specified id.
3733 *
3734 * @param folderlist list of folders to search
3735 * @id id of folder to look for
3736 * @return a folder or NULL if not found
3737 */
3738LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t *folderlist, uint32_t id)
3739{
3740 LIBMTP_folder_t *ret = NULL;
mopoke96143402006-10-30 04:37:26 +00003741
Linus Walleij9c6ca022006-04-21 10:24:15 +00003742 if(folderlist == NULL) {
3743 return NULL;
3744 }
mopoke96143402006-10-30 04:37:26 +00003745
Linus Walleij9c6ca022006-04-21 10:24:15 +00003746 if(folderlist->folder_id == id) {
3747 return folderlist;
3748 }
mopoke96143402006-10-30 04:37:26 +00003749
Linus Walleij9c6ca022006-04-21 10:24:15 +00003750 if(folderlist->sibling) {
3751 ret = LIBMTP_Find_Folder(folderlist->sibling, id);
3752 }
mopoke96143402006-10-30 04:37:26 +00003753
Linus Walleij9c6ca022006-04-21 10:24:15 +00003754 if(folderlist->child && ret == NULL) {
3755 ret = LIBMTP_Find_Folder(folderlist->child, id);
3756 }
mopoke96143402006-10-30 04:37:26 +00003757
Linus Walleij9c6ca022006-04-21 10:24:15 +00003758 return ret;
3759}
3760
3761/**
3762 * This returns a list of all folders available
3763 * on the current MTP device.
3764 *
3765 * @param device a pointer to the device to get the track listing for.
3766 * @return a list of folders
3767 */
3768LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
3769{
3770 uint32_t i = 0;
3771 LIBMTP_folder_t *retfolders = NULL;
3772 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00003773
mopoke96143402006-10-30 04:37:26 +00003774 // Get all the handles if we haven't already done that
Linus Walleij9c6ca022006-04-21 10:24:15 +00003775 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00003776 flush_handles(device);
Linus Walleij9c6ca022006-04-21 10:24:15 +00003777 }
mopoke96143402006-10-30 04:37:26 +00003778
Linus Walleij9c6ca022006-04-21 10:24:15 +00003779 for (i = 0; i < params->handles.n; i++) {
3780 LIBMTP_folder_t *folder;
3781 PTPObjectInfo oi;
mopoke96143402006-10-30 04:37:26 +00003782
Linus Walleij9c6ca022006-04-21 10:24:15 +00003783 if (ptp_getobjectinfo(params, params->handles.Handler[i], &oi) == PTP_RC_OK) {
3784 if (oi.ObjectFormat != PTP_OFC_Association) {
3785 continue;
3786 }
3787 folder = LIBMTP_new_folder_t();
3788 folder->folder_id = params->handles.Handler[i];
3789 folder->parent_id = oi.ParentObject;
Richard Low64fa3992006-11-06 21:24:11 +00003790 if (oi.Filename != NULL)
3791 folder->name = (char *)strdup(oi.Filename);
3792 else
3793 folder->name = NULL;
mopoke31364442006-11-20 04:53:04 +00003794
Linus Walleij9c6ca022006-04-21 10:24:15 +00003795 // Work out where to put this new item
3796 if(retfolders == NULL) {
3797 retfolders = folder;
3798 continue;
3799 } else {
3800 LIBMTP_folder_t *parent_folder;
3801 LIBMTP_folder_t *current_folder;
mopoke96143402006-10-30 04:37:26 +00003802
Linus Walleij9c6ca022006-04-21 10:24:15 +00003803 parent_folder = LIBMTP_Find_Folder(retfolders, folder->parent_id);
mopoke96143402006-10-30 04:37:26 +00003804
Linus Walleij9c6ca022006-04-21 10:24:15 +00003805 if(parent_folder == NULL) {
3806 current_folder = retfolders;
3807 } else {
3808 if(parent_folder->child == NULL) {
3809 parent_folder->child = folder;
3810 continue;
3811 } else {
3812 current_folder = parent_folder->child;
3813 }
3814 }
mopoke96143402006-10-30 04:37:26 +00003815
Linus Walleij9c6ca022006-04-21 10:24:15 +00003816 while(current_folder->sibling != NULL) {
3817 current_folder=current_folder->sibling;
3818 }
mopoke96143402006-10-30 04:37:26 +00003819
Linus Walleij9c6ca022006-04-21 10:24:15 +00003820 current_folder->sibling = folder;
3821 }
3822 }
3823 }
3824 return retfolders;
3825}
3826
3827/**
Linus Walleijc86afbd2006-05-04 19:05:24 +00003828 * This create a folder on the current MTP device. The PTP name
3829 * for a folder is "association". The PTP/MTP devices does not
3830 * have an internal "folder" concept really, it contains a flat
3831 * list of all files and some file are "associations" that other
3832 * files and folders may refer to as its "parent".
Linus Walleij9c6ca022006-04-21 10:24:15 +00003833 *
Linus Walleijc86afbd2006-05-04 19:05:24 +00003834 * @param device a pointer to the device to create the folder on.
3835 * @param name the name of the new folder.
3836 * @param parent_id id of parent folder to add the new folder to,
3837 * or 0 to put it in the root directory.
3838 * @return id to new folder or 0 if an error occured
Linus Walleij9c6ca022006-04-21 10:24:15 +00003839 */
3840uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name, uint32_t parent_id)
3841{
3842 PTPParams *params = (PTPParams *) device->params;
3843 uint32_t parenthandle = 0;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003844 uint32_t store = get_first_storageid(device);
Linus Walleij9c6ca022006-04-21 10:24:15 +00003845 PTPObjectInfo new_folder;
Linus Walleij438bd7f2006-06-08 11:35:44 +00003846 uint16_t ret;
Linus Walleij9c6ca022006-04-21 10:24:15 +00003847 uint32_t new_id = 0;
3848
3849 memset(&new_folder, 0, sizeof(new_folder));
3850 new_folder.Filename = name;
3851 new_folder.ObjectCompressedSize = 1;
3852 new_folder.ObjectFormat = PTP_OFC_Association;
3853 new_folder.ParentObject = parent_id;
3854
3855 parenthandle = parent_id;
3856 // Create the object
3857 ret = ptp_sendobjectinfo(params, &store, &parenthandle, &new_id, &new_folder);
3858 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003859 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Create_Folder: Could not send object info.");
Linus Walleij99310d42006-11-01 08:29:39 +00003860 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003861 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00003862 }
Linus Walleijc86afbd2006-05-04 19:05:24 +00003863 return 0;
Linus Walleij9c6ca022006-04-21 10:24:15 +00003864 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00003865 // Created new object so flush handles
3866 flush_handles(device);
Linus Walleij9c6ca022006-04-21 10:24:15 +00003867 return new_id;
3868}
raveloxd9a28642006-05-26 23:42:22 +00003869
Linus Walleij438bd7f2006-06-08 11:35:44 +00003870/**
3871 * This creates a new playlist metadata structure and allocates memory
3872 * for it. Notice that if you add strings to this structure they
3873 * will be freed by the corresponding <code>LIBMTP_destroy_playlist_t</code>
mopoke96143402006-10-30 04:37:26 +00003874 * operation later, so be careful of using strdup() when assigning
Linus Walleij438bd7f2006-06-08 11:35:44 +00003875 * strings, e.g.:
3876 *
3877 * <pre>
3878 * LIBMTP_playlist_t *pl = LIBMTP_new_playlist_t();
3879 * pl->name = strdup(str);
3880 * ....
3881 * LIBMTP_destroy_playlist_t(pl);
3882 * </pre>
3883 *
3884 * @return a pointer to the newly allocated metadata structure.
3885 * @see LIBMTP_destroy_playlist_t()
3886 */
3887LIBMTP_playlist_t *LIBMTP_new_playlist_t(void)
3888{
3889 LIBMTP_playlist_t *new = (LIBMTP_playlist_t *) malloc(sizeof(LIBMTP_playlist_t));
3890 if (new == NULL) {
3891 return NULL;
3892 }
3893 new->playlist_id = 0;
3894 new->name = NULL;
3895 new->tracks = NULL;
3896 new->no_tracks = 0;
3897 new->next = NULL;
3898 return new;
3899}
3900
3901/**
3902 * This destroys a playlist metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00003903 * used by it, including any strings. Never use a track metadata
Linus Walleij438bd7f2006-06-08 11:35:44 +00003904 * structure again after calling this function on it.
3905 * @param playlist the playlist metadata to destroy.
3906 * @see LIBMTP_new_playlist_t()
3907 */
3908void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *playlist)
3909{
3910 if (playlist == NULL) {
3911 return;
3912 }
3913 if (playlist->name != NULL)
3914 free(playlist->name);
3915 if (playlist->tracks != NULL)
3916 free(playlist->tracks);
3917 free(playlist);
3918 return;
3919}
3920
3921/**
3922 * This function returns a list of the playlists available on the
3923 * device. Typical usage:
3924 *
3925 * <pre>
3926 * </pre>
3927 *
3928 * @param device a pointer to the device to get the playlist listing from.
3929 * @return a playlist list on success, else NULL. If there are no playlists
3930 * on the device, NULL will be returned as well.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00003931 * @see LIBMTP_Get_Playlist()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003932 */
3933LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *device)
3934{
3935 PTPParams *params = (PTPParams *) device->params;
3936 LIBMTP_playlist_t *retlists = NULL;
3937 LIBMTP_playlist_t *curlist = NULL;
3938 uint32_t i;
3939
3940 // Get all the handles if we haven't already done that
3941 if (params->handles.Handler == NULL) {
3942 flush_handles(device);
3943 }
3944
3945 for (i = 0; i < params->handles.n; i++) {
3946 LIBMTP_playlist_t *pl;
3947 PTPObjectInfo oi;
3948 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00003949
Linus Walleij438bd7f2006-06-08 11:35:44 +00003950 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
3951 if ( ret == PTP_RC_OK) {
mopoke96143402006-10-30 04:37:26 +00003952
Linus Walleij438bd7f2006-06-08 11:35:44 +00003953 // Ignore stuff that isn't playlists
3954 if ( oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
3955 continue;
3956 }
mopoke96143402006-10-30 04:37:26 +00003957
Linus Walleij438bd7f2006-06-08 11:35:44 +00003958 // Allocate a new playlist type
3959 pl = LIBMTP_new_playlist_t();
mopoke96143402006-10-30 04:37:26 +00003960
Linus Walleij438bd7f2006-06-08 11:35:44 +00003961 // Ignoring the io.Filename field.
Linus Walleij9901e222006-11-30 12:28:19 +00003962 pl->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
mopoke96143402006-10-30 04:37:26 +00003963
Linus Walleij438bd7f2006-06-08 11:35:44 +00003964 // This is some sort of unique playlist ID so we can keep track of it
3965 pl->playlist_id = params->handles.Handler[i];
3966
3967 // Then get the track listing for this playlist
3968 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
3969 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003970 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist: Could not get object references.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00003971 pl->tracks = NULL;
3972 pl->no_tracks = 0;
3973 }
mopoke96143402006-10-30 04:37:26 +00003974
Linus Walleij438bd7f2006-06-08 11:35:44 +00003975 // Add playlist to a list that will be returned afterwards.
3976 if (retlists == NULL) {
3977 retlists = pl;
3978 curlist = pl;
3979 } else {
3980 curlist->next = pl;
3981 curlist = pl;
3982 }
mopoke96143402006-10-30 04:37:26 +00003983
Linus Walleij438bd7f2006-06-08 11:35:44 +00003984 // Call callback here if we decide to add that possibility...
3985
3986 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00003987 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist_List(): Found a bad handle, trying to ignore it.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00003988 }
mopoke96143402006-10-30 04:37:26 +00003989 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00003990 return retlists;
3991}
3992
Linus Walleij2e4b5f92006-06-16 14:00:49 +00003993
3994/**
3995 * This function retrieves an individual playlist from the device.
3996 * @param device a pointer to the device to get the playlist from.
3997 * @param plid the unique ID of the playlist to retrieve.
3998 * @return a valid playlist metadata post or NULL on failure.
3999 * @see LIBMTP_Get_Playlist_List()
4000 */
4001LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *device, uint32_t const plid)
4002{
4003 PTPParams *params = (PTPParams *) device->params;
4004 uint32_t i;
4005
4006 // Get all the handles if we haven't already done that
4007 if (params->handles.Handler == NULL) {
4008 flush_handles(device);
4009 }
4010
4011 for (i = 0; i < params->handles.n; i++) {
4012 LIBMTP_playlist_t *pl;
4013 PTPObjectInfo oi;
4014 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00004015
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004016 if (params->handles.Handler[i] != plid) {
4017 continue;
4018 }
4019
4020 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
4021 if ( ret == PTP_RC_OK) {
mopoke96143402006-10-30 04:37:26 +00004022
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004023 // Ignore stuff that isn't playlists
4024 if ( oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
4025 return NULL;
4026 }
mopoke96143402006-10-30 04:37:26 +00004027
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004028 // Allocate a new playlist type
4029 pl = LIBMTP_new_playlist_t();
mopoke96143402006-10-30 04:37:26 +00004030
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004031 // Ignoring the io.Filename field.
Linus Walleij9901e222006-11-30 12:28:19 +00004032 pl->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
mopoke96143402006-10-30 04:37:26 +00004033
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004034 // This is some sort of unique playlist ID so we can keep track of it
4035 pl->playlist_id = params->handles.Handler[i];
4036
4037 // Then get the track listing for this playlist
4038 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
4039 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004040 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist(): Could not get object references.");
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004041 pl->tracks = NULL;
4042 pl->no_tracks = 0;
4043 }
mopoke96143402006-10-30 04:37:26 +00004044
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004045 return pl;
4046 } else {
4047 return NULL;
4048 }
mopoke96143402006-10-30 04:37:26 +00004049 }
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004050 return NULL;
4051}
4052
Linus Walleij20698482006-11-24 20:54:21 +00004053/*
Linus Walleijb7426d12006-11-25 23:19:11 +00004054 * This function creates a new abstract list such as a playlist
4055 * or an album.
4056 *
4057 * @param device a pointer to the device to create the new abstract list
4058 * on.
4059 * @param name the name of the new abstract list.
4060 * @param parenthandle the handle of the parent or 0 for no parent
4061 * i.e. the root folder.
4062 * @param objectformat the abstract list type to create.
4063 * @param suffix the ".foo" (4 characters) suffix to use for the virtual
4064 * "file" created by this operation.
4065 * @param newid a pointer to a variable that will hold the new object
4066 * ID if this call is successful.
4067 * @param tracks an array of tracks to associate with this list.
4068 * @param no_tracks the number of tracks in the list.
Linus Walleij438bd7f2006-06-08 11:35:44 +00004069 * @return 0 on success, any other value means failure.
Linus Walleij438bd7f2006-06-08 11:35:44 +00004070 */
Linus Walleijb7426d12006-11-25 23:19:11 +00004071static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
4072 char const * const name,
4073 uint32_t const parenthandle,
4074 uint16_t const objectformat,
4075 char const * const suffix,
4076 uint32_t * const newid,
4077 uint32_t const * const tracks,
4078 uint32_t const no_tracks)
4079
Linus Walleij438bd7f2006-06-08 11:35:44 +00004080{
Linus Walleijb7426d12006-11-25 23:19:11 +00004081 int i;
4082 int supported = 0;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004083 uint16_t ret;
rreardon508705f2006-11-19 21:27:22 +00004084 uint16_t *props = NULL;
4085 uint32_t propcnt = 0;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004086 uint32_t store = get_first_storageid(device);
Linus Walleij438bd7f2006-06-08 11:35:44 +00004087 uint32_t localph = parenthandle;
Linus Walleijb7426d12006-11-25 23:19:11 +00004088 uint8_t nonconsumable = 0x00U; /* By default it is consumable */
4089 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004090 char fname[256];
Linus Walleijd14e84f2006-06-16 14:50:59 +00004091 uint8_t data[2];
Linus Walleij438bd7f2006-06-08 11:35:44 +00004092
Linus Walleijb7426d12006-11-25 23:19:11 +00004093 // Check if we can create an object of this type
4094 for ( i=0; i < params->deviceinfo.ImageFormats_len; i++ ) {
4095 if (params->deviceinfo.ImageFormats[i] == objectformat) {
4096 supported = 1;
4097 break;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004098 }
4099 }
Linus Walleijb7426d12006-11-25 23:19:11 +00004100 if (!supported) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004101 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): player does not support this abstract type.");
4102 printf("Unsupported type: %04x\n", objectformat);
Linus Walleijb7426d12006-11-25 23:19:11 +00004103 return -1;
4104 }
4105
Linus Walleijb7426d12006-11-25 23:19:11 +00004106 // add the new suffix if it isn's there
Linus Walleij629fe402006-12-12 23:39:15 +00004107 fname[0] = '\0';
Linus Walleijb7426d12006-11-25 23:19:11 +00004108 if (strlen(name) > strlen(suffix)) {
4109 char const * const suff = &name[strlen(name)-strlen(suffix)];
4110 if (!strcmp(suff, suffix)) {
4111 // Home free.
Linus Walleij629fe402006-12-12 23:39:15 +00004112 strncpy(fname, name, sizeof(fname));
Linus Walleijb7426d12006-11-25 23:19:11 +00004113 }
4114 }
4115 // If it didn't end with "<suffix>" then add that here.
Linus Walleij629fe402006-12-12 23:39:15 +00004116 if (fname[0] == '\0') {
Linus Walleijb7426d12006-11-25 23:19:11 +00004117 strncpy(fname, name, sizeof(fname)-strlen(suffix)-1);
4118 strcat(fname, suffix);
Linus Walleij438bd7f2006-06-08 11:35:44 +00004119 fname[sizeof(fname)-1] = '\0';
Linus Walleij438bd7f2006-06-08 11:35:44 +00004120 }
4121
rreardon508705f2006-11-19 21:27:22 +00004122#ifdef ENABLE_MTP_ENHANCED
4123 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
mopoke31364442006-11-20 04:53:04 +00004124
rreardon508705f2006-11-19 21:27:22 +00004125 MTPPropList *proplist = NULL;
4126 MTPPropList *prop = NULL;
4127 MTPPropList *previous = NULL;
Richard Low6c0a6ce2006-11-26 10:42:08 +00004128
4129 *newid = 0x00000000U;
mopoke96143402006-10-30 04:37:26 +00004130
Linus Walleijb7426d12006-11-25 23:19:11 +00004131 ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00004132
rreardon508705f2006-11-19 21:27:22 +00004133 for (i=0;i<propcnt;i++) {
4134 switch (props[i]) {
4135 case PTP_OPC_ObjectFileName:
4136 prop = New_MTP_Prop_Entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004137 prop->ObjectHandle = *newid;
4138 prop->property = PTP_OPC_ObjectFileName;
rreardon508705f2006-11-19 21:27:22 +00004139 prop->datatype = PTP_DTC_STR;
Linus Walleij629fe402006-12-12 23:39:15 +00004140 prop->propval.str = strdup(fname);
mopoke31364442006-11-20 04:53:04 +00004141
rreardon508705f2006-11-19 21:27:22 +00004142 if (previous != NULL)
4143 previous->next = prop;
4144 else
4145 proplist = prop;
4146 previous = prop;
4147 prop->next = NULL;
4148 break;
4149 case PTP_OPC_ProtectionStatus:
4150 prop = New_MTP_Prop_Entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004151 prop->ObjectHandle = *newid;
rreardon508705f2006-11-19 21:27:22 +00004152 prop->property = PTP_OPC_ProtectionStatus;
4153 prop->datatype = PTP_DTC_UINT16;
4154 prop->propval.u16 = 0x0000U; /* Not protected */
mopoke31364442006-11-20 04:53:04 +00004155
rreardon508705f2006-11-19 21:27:22 +00004156 if (previous != NULL)
4157 previous->next = prop;
4158 else
4159 proplist = prop;
4160 previous = prop;
4161 prop->next = NULL;
4162 break;
4163 case PTP_OPC_NonConsumable:
4164 prop = New_MTP_Prop_Entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004165 prop->ObjectHandle = *newid;
rreardon508705f2006-11-19 21:27:22 +00004166 prop->property = PTP_OPC_NonConsumable;
4167 prop->datatype = PTP_DTC_UINT8;
4168 prop->propval.u8 = nonconsumable;
mopoke31364442006-11-20 04:53:04 +00004169
rreardon508705f2006-11-19 21:27:22 +00004170 if (previous != NULL)
4171 previous->next = prop;
4172 else
4173 proplist = prop;
4174 previous = prop;
4175 prop->next = NULL;
4176 break;
4177 case PTP_OPC_Name:
4178 prop = New_MTP_Prop_Entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004179 prop->ObjectHandle = *newid;
rreardon508705f2006-11-19 21:27:22 +00004180 prop->property = PTP_OPC_Name;
4181 prop->datatype = PTP_DTC_STR;
Linus Walleijb7426d12006-11-25 23:19:11 +00004182 prop->propval.str = strdup(name);
mopoke31364442006-11-20 04:53:04 +00004183
rreardon508705f2006-11-19 21:27:22 +00004184 if (previous != NULL)
4185 previous->next = prop;
4186 else
4187 proplist = prop;
4188 previous = prop;
4189 prop->next = NULL;
4190 break;
4191 }
4192 }
4193 free(props);
mopoke31364442006-11-20 04:53:04 +00004194
Linus Walleijb7426d12006-11-25 23:19:11 +00004195 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid,
Richard Lowa6c924c2006-12-29 17:44:28 +00004196 objectformat, 0, proplist);
mopoke31364442006-11-20 04:53:04 +00004197
rreardon508705f2006-11-19 21:27:22 +00004198 /* Free property list */
4199 prop = proplist;
4200 while (prop != NULL) {
4201 previous = prop;
4202 prop = prop->next;
4203 Destroy_MTP_Prop_Entry(previous);
4204 }
mopoke31364442006-11-20 04:53:04 +00004205
rreardon508705f2006-11-19 21:27:22 +00004206 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004207 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list.");
rreardon508705f2006-11-19 21:27:22 +00004208 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004209 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardon508705f2006-11-19 21:27:22 +00004210 }
4211 return -1;
4212 }
Richard Lowa6c924c2006-12-29 17:44:28 +00004213
4214 // now send the blank objet
4215 ret = ptp_sendobject(params, NULL, 0);
4216 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004217 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00004218 return -1;
4219 }
Linus Walleij629fe402006-12-12 23:39:15 +00004220
rreardon508705f2006-11-19 21:27:22 +00004221 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
4222#else // !ENABLE_MTP_ENHANCED
4223 {
4224#endif // ENABLE_MTP_ENHANCED
Linus Walleij629fe402006-12-12 23:39:15 +00004225 PTPObjectInfo new_object;
4226
4227 new_object.Filename = fname;
4228 new_object.ObjectCompressedSize = 1;
4229 new_object.ObjectFormat = objectformat;
4230
Linus Walleij20698482006-11-24 20:54:21 +00004231 // Create the object
Linus Walleijb7426d12006-11-25 23:19:11 +00004232 ret = ptp_sendobjectinfo(params, &store, &localph, newid, &new_object);
Linus Walleij20698482006-11-24 20:54:21 +00004233 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004234 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object info (the playlist itself).");
Linus Walleij20698482006-11-24 20:54:21 +00004235 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004236 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij20698482006-11-24 20:54:21 +00004237 }
4238 return -1;
4239 }
Richard Lowa6c924c2006-12-29 17:44:28 +00004240
4241 /*
4242 * We have to send this one blank data byte.
4243 * If we don't, the handle will not be created and thus there is no playlist.
4244 */
4245 data[0] = '\0';
4246 data[1] = '\0';
4247 ret = ptp_sendobject(params, data, 1);
4248 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004249 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00004250 return -1;
4251 }
4252
4253 // Update title
4254 ret = set_object_string(device, *newid, PTP_OPC_Name, name);
4255 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004256 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity name.");
Richard Lowa6c924c2006-12-29 17:44:28 +00004257 return -1;
4258 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00004259 }
4260
Linus Walleijb7426d12006-11-25 23:19:11 +00004261 if (no_tracks > 0) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00004262 // Add tracks to the new playlist as object references.
Linus Walleijb7426d12006-11-25 23:19:11 +00004263 ret = ptp_mtp_setobjectreferences (params, *newid, (uint32_t *) tracks, no_tracks);
Linus Walleij438bd7f2006-06-08 11:35:44 +00004264 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004265 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): could not add tracks as object references.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00004266 return -1;
4267 }
4268 }
mopoke96143402006-10-30 04:37:26 +00004269
Linus Walleij438bd7f2006-06-08 11:35:44 +00004270 // Created new item, so flush handles
4271 flush_handles(device);
4272
4273 return 0;
4274}
4275
Linus Walleijb7426d12006-11-25 23:19:11 +00004276
4277/**
4278 * This routine creates a new playlist based on the metadata
4279 * supplied. If the <code>tracks</code> field of the metadata
4280 * contains a track listing, these tracks will be added to the
4281 * playlist.
4282 * @param device a pointer to the device to create the new playlist on.
4283 * @param metadata the metadata for the new playlist. If the function
4284 * exits with success, the <code>playlist_id</code> field of this
4285 * struct will contain the new playlist ID of the playlist.
4286 * @param parenthandle the parent (e.g. folder) to store this playlist
4287 * in. Pass in 0 to put the playlist in the root directory.
4288 * @return 0 on success, any other value means failure.
4289 * @see LIBMTP_Update_Playlist()
4290 * @see LIBMTP_Delete_Object()
4291 */
4292int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *device,
4293 LIBMTP_playlist_t * const metadata,
4294 uint32_t const parenthandle)
4295{
4296 uint32_t localph = parenthandle;
4297
4298 // Use a default folder if none given
4299 if (localph == 0) {
4300 localph = device->default_playlist_folder;
4301 }
4302
4303 // Just create a new abstract audio/video playlist...
4304 return create_new_abstract_list(device,
4305 metadata->name,
4306 localph,
4307 PTP_OFC_MTP_AbstractAudioVideoPlaylist,
4308 ".zpl",
4309 &metadata->playlist_id,
4310 metadata->tracks,
4311 metadata->no_tracks);
4312}
4313
Linus Walleij438bd7f2006-06-08 11:35:44 +00004314/**
4315 * This routine updates a playlist based on the metadata
4316 * supplied. If the <code>tracks</code> field of the metadata
4317 * contains a track listing, these tracks will be added to the
4318 * playlist in place of those already present, i.e. the
4319 * previous track listing will be deleted.
4320 * @param device a pointer to the device to create the new playlist on.
4321 * @param metadata the metadata for the playlist to be updated.
mopoke96143402006-10-30 04:37:26 +00004322 * notice that the field <code>playlist_id</code>
Linus Walleij438bd7f2006-06-08 11:35:44 +00004323 * must contain the apropriate playlist ID.
4324 * @return 0 on success, any other value means failure.
4325 * @see LIBMTP_Create_New_Playlist()
4326 * @see LIBMTP_Delete_Object()
4327 */
mopoke96143402006-10-30 04:37:26 +00004328int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device,
Linus Walleijf5306352006-06-08 12:00:23 +00004329 LIBMTP_playlist_t const * const metadata)
Linus Walleij438bd7f2006-06-08 11:35:44 +00004330{
4331 uint16_t ret;
Linus Walleijf5306352006-06-08 12:00:23 +00004332 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004333
4334 // Update title
Linus Walleij9901e222006-11-30 12:28:19 +00004335 ret = set_object_string(device, metadata->playlist_id, PTP_OPC_Name, metadata->name);
Linus Walleijd14e84f2006-06-16 14:50:59 +00004336 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004337 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Playlist(): could not set playlist name.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00004338 return -1;
4339 }
4340
4341 if (metadata->no_tracks > 0) {
4342 // Add tracks to the new playlist as object references.
4343 ret = ptp_mtp_setobjectreferences (params, metadata->playlist_id, metadata->tracks, metadata->no_tracks);
4344 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004345 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Playlist(): could not add tracks as object references.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00004346 return -1;
4347 }
4348 }
4349 return 0;
4350}
Linus Walleijaa4b0752006-07-26 22:21:04 +00004351
Linus Walleij0c33ec02006-10-27 10:15:40 +00004352/**
4353 * This creates a new album metadata structure and allocates memory
4354 * for it. Notice that if you add strings to this structure they
4355 * will be freed by the corresponding <code>LIBMTP_destroy_album_t</code>
4356 * operation later, so be careful of using strdup() when assigning
4357 * strings.
4358 *
4359 * @return a pointer to the newly allocated metadata structure.
4360 * @see LIBMTP_destroy_album_t()
4361 */
4362LIBMTP_album_t *LIBMTP_new_album_t(void)
4363{
4364 LIBMTP_album_t *new = (LIBMTP_album_t *) malloc(sizeof(LIBMTP_album_t));
4365 if (new == NULL) {
4366 return NULL;
4367 }
4368 new->album_id = 0;
4369 new->name = NULL;
4370 new->tracks = NULL;
4371 new->no_tracks = 0;
4372 new->next = NULL;
4373 return new;
4374}
4375
4376/**
4377 * This recursively deletes the memory for an album structure
4378 *
4379 * @param album structure to destroy
4380 * @see LIBMTP_new_album_t()
4381 */
4382void LIBMTP_destroy_album_t(LIBMTP_album_t *album)
4383{
4384 if (album == NULL) {
4385 return;
4386 }
4387 if (album->name != NULL)
4388 free(album->name);
4389 if (album->tracks != NULL)
4390 free(album->tracks);
4391 free(album);
4392 return;
4393}
4394
4395/**
4396 * This function returns a list of the albums available on the
4397 * device.
4398 *
4399 * @param device a pointer to the device to get the album listing from.
4400 * @return an album list on success, else NULL. If there are no albums
4401 * on the device, NULL will be returned as well.
4402 * @see LIBMTP_Get_Album()
4403 */
4404LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *device)
4405{
4406 PTPParams *params = (PTPParams *) device->params;
4407 LIBMTP_album_t *retalbums = NULL;
4408 LIBMTP_album_t *curalbum = NULL;
4409 uint32_t i;
4410
4411 // Get all the handles if we haven't already done that
4412 if (params->handles.Handler == NULL) {
4413 flush_handles(device);
4414 }
4415
4416 for (i = 0; i < params->handles.n; i++) {
4417 LIBMTP_album_t *alb;
4418 PTPObjectInfo oi;
4419 uint16_t ret;
4420
4421 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
4422 if ( ret == PTP_RC_OK) {
4423
4424 // Ignore stuff that isn't an album
4425 if ( oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum ) {
4426 continue;
4427 }
4428
4429 // Allocate a new album type
4430 alb = LIBMTP_new_album_t();
Linus Walleij9901e222006-11-30 12:28:19 +00004431 alb->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004432 alb->album_id = params->handles.Handler[i];
4433
4434 // Then get the track listing for this album
4435 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
4436 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004437 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Could not get object references.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004438 alb->tracks = NULL;
4439 alb->no_tracks = 0;
4440 }
4441
4442 // Add album to a list that will be returned afterwards.
4443 if (retalbums == NULL) {
4444 retalbums = alb;
4445 curalbum = alb;
4446 } else {
4447 curalbum->next = alb;
4448 curalbum = alb;
4449 }
4450
4451 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00004452 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Found a bad handle, trying to ignore it.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004453 }
4454 }
4455 return retalbums;
4456}
4457
4458/**
4459 * This function retrieves an individual album from the device.
4460 * @param device a pointer to the device to get the album from.
4461 * @param albid the unique ID of the album to retrieve.
4462 * @return a valid album metadata or NULL on failure.
4463 * @see LIBMTP_Get_Album_List()
4464 */
4465LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *device, uint32_t const albid)
4466{
4467 PTPParams *params = (PTPParams *) device->params;
4468 uint32_t i;
4469
4470 // Get all the handles if we haven't already done that
4471 if (params->handles.Handler == NULL) {
4472 flush_handles(device);
4473 }
4474
4475 for (i = 0; i < params->handles.n; i++) {
4476 LIBMTP_album_t *alb;
4477 PTPObjectInfo oi;
4478 uint16_t ret;
4479
4480 if (params->handles.Handler[i] != albid) {
4481 continue;
4482 }
4483
4484 ret = ptp_getobjectinfo(params, params->handles.Handler[i], &oi);
4485 if ( ret == PTP_RC_OK) {
4486
4487 // Ignore stuff that isn't an album
4488 if ( oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum ) {
4489 return NULL;
4490 }
4491
4492 // Allocate a new album type
4493 alb = LIBMTP_new_album_t();
Linus Walleij9901e222006-11-30 12:28:19 +00004494 alb->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004495 alb->album_id = params->handles.Handler[i];
4496 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
4497 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004498 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album: Could not get object references.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004499 alb->tracks = NULL;
4500 alb->no_tracks = 0;
4501 }
4502
4503 return alb;
4504 } else {
4505 return NULL;
4506 }
4507 }
4508 return NULL;
4509}
4510
4511/**
4512 * This routine creates a new album based on the metadata
4513 * supplied. If the <code>tracks</code> field of the metadata
4514 * contains a track listing, these tracks will be added to the
4515 * album.
4516 * @param device a pointer to the device to create the new album on.
4517 * @param metadata the metadata for the new album. If the function
4518 * exits with success, the <code>album_id</code> field of this
4519 * struct will contain the new ID of the album.
4520 * @param parenthandle the parent (e.g. folder) to store this album
4521 * in. Pass in 0 to put the album in the default music directory.
4522 * @return 0 on success, any other value means failure.
4523 * @see LIBMTP_Update_Album()
4524 * @see LIBMTP_Delete_Object()
4525 */
4526int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *device,
4527 LIBMTP_album_t * const metadata,
4528 uint32_t const parenthandle)
4529{
Linus Walleij0c33ec02006-10-27 10:15:40 +00004530 uint32_t localph = parenthandle;
mopoke96143402006-10-30 04:37:26 +00004531
Linus Walleij0c33ec02006-10-27 10:15:40 +00004532 // Use a default folder if none given
4533 if (localph == 0) {
rreardon508705f2006-11-19 21:27:22 +00004534 localph = device->default_album_folder;
Linus Walleij0c33ec02006-10-27 10:15:40 +00004535 }
4536
Linus Walleijb7426d12006-11-25 23:19:11 +00004537 // Just create a new abstract album...
4538 return create_new_abstract_list(device,
4539 metadata->name,
4540 localph,
4541 PTP_OFC_MTP_AbstractAudioAlbum,
4542 ".alb",
4543 &metadata->album_id,
4544 metadata->tracks,
4545 metadata->no_tracks);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004546}
4547
4548/**
rreardon5332f9c2006-12-05 10:18:23 +00004549 * This creates a new sample data metadata structure and allocates memory
4550 * for it. Notice that if you add strings to this structure they
4551 * will be freed by the corresponding <code>LIBMTP_destroy_sampledata_t</code>
4552 * operation later, so be careful of using strdup() when assigning
4553 * strings.
4554 *
4555 * @return a pointer to the newly allocated metadata structure.
4556 * @see LIBMTP_destroy_sampledata_t()
4557 */
4558LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void)
4559{
4560 LIBMTP_filesampledata_t *new = (LIBMTP_filesampledata_t *) malloc(sizeof(LIBMTP_filesampledata_t));
4561 if (new == NULL) {
4562 return NULL;
4563 }
4564 new->height=0;
4565 new->width = 0;
4566 new->data = NULL;
4567 new->duration = 0;
4568 new->size = 0;
4569 return new;
4570}
4571
Linus Walleijf1b02f22006-12-06 09:03:23 +00004572/**
4573 * This destroys a file sample metadata type.
4574 * @param sample the file sample metadata to be destroyed.
4575 */
4576void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t * sample)
4577{
4578 if (sample == NULL) {
4579 return;
4580 }
4581 if (sample->data != NULL) {
4582 free(sample->data);
4583 }
4584 free(sample);
4585}
4586
4587/**
4588 * This routine figures out whether a certain filetype supports
4589 * representative samples (small thumbnail images) or not. This
4590 * typically applies to JPEG files, MP3 files and Album abstract
4591 * playlists, but in theory any filetype could support representative
4592 * samples.
4593 * @param device a pointer to the device which is to be examined.
4594 * @param the filetype to examine, and return the representative sample
4595 * properties for.
4596 * @param sample this will contain a new sample type with the fields
4597 * filled in with suitable default values. For example, the
4598 * supported sample type will be set, the supported height and
4599 * width will be set to max values if it is an image sample,
4600 * and duration will also be given some suitable default value
4601 * which should not be exceeded on audio samples. If the
4602 * device does not support samples for this filetype, this
4603 * pointer will be NULL. If it is not NULL, the user must
4604 * destroy this struct with <code>LIBMTP_destroy_filesampledata_t()</code>
4605 * after use.
4606 * @return 0 on success, any other value means failure.
4607 * @see LIBMTP_Send_Representative_Sample()
4608 * @see LIBMTP_Create_New_Album()
4609 */
4610int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *device,
4611 LIBMTP_filetype_t const filetype,
4612 LIBMTP_filesampledata_t ** sample)
4613{
4614 uint16_t ret;
4615 PTPParams *params = (PTPParams *) device->params;
4616 uint16_t *props = NULL;
4617 uint32_t propcnt = 0;
4618 int i;
4619 // TODO: Get rid of these when we can properly query the device.
4620 int support_data = 0;
4621 int support_format = 0;
4622 int support_height = 0;
4623 int support_width = 0;
4624 int support_duration = 0;
4625
4626 // Default to no type supported.
4627 *sample = NULL;
4628
4629 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
4630 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004631 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample_Format(): could not get object properties.");
Linus Walleijf1b02f22006-12-06 09:03:23 +00004632 return -1;
4633 }
4634 /*
4635 * TODO: when walking through these object properties, make calls to
4636 * a new function in ptp.h/ptp.c that can send the command
4637 * PTP_OC_MTP_GetObjectPropDesc to get max/min values of the properties
4638 * supported.
4639 */
4640 for (i = 0; i < propcnt; i++) {
4641 switch(props[i]) {
4642 case PTP_OPC_RepresentativeSampleData:
4643 support_data = 1;
4644 break;
4645 case PTP_OPC_RepresentativeSampleFormat:
4646 support_format = 1;
4647 break;
4648 case PTP_OPC_RepresentativeSampleSize:
4649 break;
4650 case PTP_OPC_RepresentativeSampleHeight:
4651 support_height = 1;
4652 break;
4653 case PTP_OPC_RepresentativeSampleWidth:
4654 support_width = 1;
4655 break;
4656 case PTP_OPC_RepresentativeSampleDuration:
4657 support_duration = 1;
4658 break;
4659 default:
4660 break;
4661 }
4662 }
4663 free(props);
4664
4665 /*
4666 * TODO: figure out what format, max height and width, or duration is actually
4667 * supported on this device.
4668 */
4669 if (support_data && support_format && support_height && support_width && !support_duration) {
4670 // Something that supports height and width and not duration is likely to be JPEG
4671 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
4672 retsam->filetype = LIBMTP_FILETYPE_JPEG;
4673 retsam->width = 100;
4674 retsam->height = 100;
4675 *sample = retsam;
4676 } else if (support_data && support_format && !support_height && !support_width && support_duration) {
4677 // Another qualified guess
4678 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
4679 retsam->filetype = LIBMTP_FILETYPE_MP3;
4680 retsam->duration = 2000; // 2 seconds
4681 *sample = retsam;
4682 }
4683 return 0;
4684}
rreardon5332f9c2006-12-05 10:18:23 +00004685
4686/**
4687 * This routine sends representative sample data for an object.
4688 * This uses the RepresentativeSampleData property of the album,
4689 * if the device supports it. The data should be of a format acceptable
4690 * to the player (for iRiver and Creative, this seems to be JPEG) and
4691 * must not be too large. (for a Creative, max seems to be about 20KB.)
Linus Walleij0c33ec02006-10-27 10:15:40 +00004692 * TODO: there must be a way to find the max size for an ObjectPropertyValue.
Linus Walleij7e3f2762006-12-03 22:52:05 +00004693 * @param device a pointer to the device which the object is on.
4694 * @param id unique id of the object to set artwork for.
4695 * @param data pointer to an array of uint8_t containing the representative
4696 * sample data.
4697 * @param size number of bytes in the sample.
Linus Walleij0c33ec02006-10-27 10:15:40 +00004698 * @return 0 on success, any other value means failure.
Linus Walleijf1b02f22006-12-06 09:03:23 +00004699 * @see LIBMTP_Get_Representative_Sample_Format()
Linus Walleij0c33ec02006-10-27 10:15:40 +00004700 * @see LIBMTP_Create_New_Album()
4701 */
Linus Walleij7e3f2762006-12-03 22:52:05 +00004702int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *device,
rreardon5332f9c2006-12-05 10:18:23 +00004703 uint32_t const id,
4704 LIBMTP_filesampledata_t *sampledata)
Linus Walleij0c33ec02006-10-27 10:15:40 +00004705{
4706 uint16_t ret;
4707 PTPParams *params = (PTPParams *) device->params;
4708 PTPPropertyValue propval;
rreardon5332f9c2006-12-05 10:18:23 +00004709 PTPObjectInfo oi;
Linus Walleij0c33ec02006-10-27 10:15:40 +00004710 int i;
Linus Walleijf1b02f22006-12-06 09:03:23 +00004711 uint16_t *props = NULL;
4712 uint32_t propcnt = 0;
4713 int supported = 0;
rreardon5332f9c2006-12-05 10:18:23 +00004714
4715 // get the file format for the object we're going to send representative data for
4716 ret = ptp_getobjectinfo(device->params, id, &oi);
4717
4718 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004719 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object info.");
rreardon5332f9c2006-12-05 10:18:23 +00004720 return -1;
4721 }
4722
4723 // check that we can send representative sample data for this object format
rreardon5332f9c2006-12-05 10:18:23 +00004724 ret = ptp_mtp_getobjectpropssupported(params, oi.ObjectFormat, &propcnt, &props);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004725 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004726 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object properties.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004727 return -1;
4728 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00004729
Linus Walleij0c33ec02006-10-27 10:15:40 +00004730 for (i = 0; i < propcnt; i++) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00004731 if (props[i] == PTP_OPC_RepresentativeSampleData) {
Linus Walleij0c33ec02006-10-27 10:15:40 +00004732 supported = 1;
Linus Walleijf1b02f22006-12-06 09:03:23 +00004733 break;
4734 }
Linus Walleij0c33ec02006-10-27 10:15:40 +00004735 }
4736 if (!supported) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00004737 free(props);
Linus Walleij070e9b42007-01-22 08:49:28 +00004738 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004739 return -1;
4740 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00004741 free(props);
rreardon5332f9c2006-12-05 10:18:23 +00004742
Linus Walleijf1b02f22006-12-06 09:03:23 +00004743 // Go ahead and send the data
rreardon5332f9c2006-12-05 10:18:23 +00004744 propval.a.count = sampledata->size;
4745 propval.a.v = malloc(sizeof(PTPPropertyValue) * sampledata->size);
4746 for (i = 0; i < sampledata->size; i++) {
4747 propval.a.v[i].u8 = sampledata->data[i];
Linus Walleij7e3f2762006-12-03 22:52:05 +00004748 }
4749
Linus Walleij0c33ec02006-10-27 10:15:40 +00004750 ret = ptp_mtp_setobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
Linus Walleijf1b02f22006-12-06 09:03:23 +00004751 &propval,PTP_DTC_AUINT8);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004752 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004753 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not send sample data.");
Linus Walleij7e3f2762006-12-03 22:52:05 +00004754 free(propval.a.v);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004755 return -1;
4756 }
Linus Walleij7e3f2762006-12-03 22:52:05 +00004757 free(propval.a.v);
Linus Walleijf1b02f22006-12-06 09:03:23 +00004758
4759 /*
4760 * TODO: Send Representative Sample Height, Width and Size here if it is an
4761 * image (typically JPEG) thumbnail, send Duration and Size if it is an audio
4762 * sample (MP3, WAV etc).
4763 */
rreardond2ddb632006-12-12 12:13:21 +00004764
4765 /* Set the height and width if the sample is an image, otherwise just
4766 * set the duration and size */
4767 switch(sampledata->filetype) {
Linus Walleije1ac07e2006-12-14 19:38:59 +00004768 case LIBMTP_FILETYPE_JPEG:
4769 case LIBMTP_FILETYPE_JFIF:
4770 case LIBMTP_FILETYPE_TIFF:
4771 case LIBMTP_FILETYPE_BMP:
4772 case LIBMTP_FILETYPE_GIF:
4773 case LIBMTP_FILETYPE_PICT:
4774 case LIBMTP_FILETYPE_PNG:
4775 // For images, set the height and width
4776 set_object_u32(device, id, PTP_OPC_RepresentativeSampleHeight, sampledata->height);
4777 set_object_u32(device, id, PTP_OPC_RepresentativeSampleWidth, sampledata->width);
4778 break;
4779 default:
4780 // For anything not an image, set the duration and size
4781 set_object_u32(device, id, PTP_OPC_RepresentativeSampleDuration, sampledata->duration);
4782 set_object_u32(device, id, PTP_OPC_RepresentativeSampleSize, sampledata->size);
4783 break;
rreardond2ddb632006-12-12 12:13:21 +00004784 }
rreardond2ddb632006-12-12 12:13:21 +00004785
Linus Walleij0c33ec02006-10-27 10:15:40 +00004786 return 0;
4787}
4788
4789/**
4790 * This routine updates an album based on the metadata
4791 * supplied. If the <code>tracks</code> field of the metadata
4792 * contains a track listing, these tracks will be added to the
4793 * album in place of those already present, i.e. the
4794 * previous track listing will be deleted.
4795 * @param device a pointer to the device to create the new album on.
4796 * @param metadata the metadata for the album to be updated.
4797 * notice that the field <code>album_id</code>
4798 * must contain the apropriate album ID.
4799 * @return 0 on success, any other value means failure.
4800 * @see LIBMTP_Create_New_Album()
4801 * @see LIBMTP_Delete_Object()
4802 */
4803int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device,
4804 LIBMTP_album_t const * const metadata)
4805{
4806 uint16_t ret;
4807 PTPParams *params = (PTPParams *) device->params;
4808
4809 // Update title
Linus Walleij9901e222006-11-30 12:28:19 +00004810 ret = set_object_string(device, metadata->album_id, PTP_OPC_Name, metadata->name);
Linus Walleij0c33ec02006-10-27 10:15:40 +00004811 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004812 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Album(): could not set album name.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004813 return -1;
4814 }
4815
4816 if (metadata->no_tracks > 0) {
4817 // Add tracks to the new album as object references.
4818 ret = ptp_mtp_setobjectreferences (params, metadata->album_id, metadata->tracks, metadata->no_tracks);
4819 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004820 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Album(): could not add tracks as object references.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00004821 return -1;
4822 }
4823 }
4824 return 0;
4825}
Linus Walleijaa4b0752006-07-26 22:21:04 +00004826
4827/**
4828 * Dummy function needed to interface to upstream
4829 * ptp.c/ptp.h files.
4830 */
4831void ptp_nikon_getptpipguid (unsigned char* guid) {
4832 return;
4833}