Source code upload
[framework/connectivity/libgphoto2.git] / libgphoto2 / gphoto2-abilities-list.c
1 /** \file gphoto2-abilities-list.c
2  * \brief List of supported camera models including their abilities.
3  *
4  * \author Copyright 2000 Scott Fritzinger
5  *
6  * \par
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * \par
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  * \par
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "config.h"
26 #include <gphoto2/gphoto2-abilities-list.h>
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <gphoto2/gphoto2_slp_support.h>
33
34 #include <gphoto2/gphoto2-result.h>
35 #include <gphoto2/gphoto2-port-log.h>
36 #include <gphoto2/gphoto2-library.h>
37
38 #ifdef ENABLE_NLS
39 #  include <libintl.h>
40 #  undef _
41 #  define _(String) dgettext (GETTEXT_PACKAGE, String)
42 #  ifdef gettext_noop
43 #    define N_(String) gettext_noop (String)
44 #  else
45 #    define N_(String) (String)
46 #  endif
47 #else
48 #  define textdomain(String) (String)
49 #  define gettext(String) (String)
50 #  define dgettext(Domain,Message) (Message)
51 #  define dcgettext(Domain,Message,Type) (Message)
52 #  define bindtextdomain(Domain,Directory) (Domain)
53 #  define bind_textdomain_codeset(Domain,Charset) (Domain)
54 #  define _(String) (String)
55 #  define N_(String) (String)
56 #endif
57
58 /** \internal */
59 #define GP_MODULE "gphoto2-abilities-list"
60
61 /** \internal */
62 #define CHECK_NULL(r)        {if (!(r)) return (GP_ERROR_BAD_PARAMETERS);}
63 /** \internal */
64 #define CHECK_RESULT(result) {int r = (result); if (r < 0) return (r);}
65 /** \internal */
66 #define CHECK_MEM(m)         {if (!(m)) return (GP_ERROR_NO_MEMORY);}
67
68 /** \internal */
69 struct _CameraAbilitiesList {
70         int count;
71         CameraAbilities *abilities;
72 };
73
74 /** \internal */
75 static int gp_abilities_list_lookup_id (CameraAbilitiesList *, const char *);
76 /** \internal */
77 static int gp_abilities_list_sort      (CameraAbilitiesList *);
78
79 /**
80  * \brief Set the current character codeset libgphoto2 is operating in.
81  *
82  * Set the codeset for all messages returned by libgphoto2.
83  * \param codeset New codeset for the messages. For instance "utf-8".
84  * \return old codeset as returned from bind_textdomain_codeset().
85  *
86  * You would then call gp_abilities_list_load() in order to
87  * populate it.
88  */
89 const char*
90 gp_message_codeset (const char *codeset)
91 {
92         gp_port_message_codeset (codeset);
93         return bind_textdomain_codeset (GETTEXT_PACKAGE, codeset);
94 }
95
96 /**
97  * \brief Allocate the memory for a new abilities list.
98  *
99  * Function to allocate the memory for a new abilities list.
100  * \param list CameraAbilitiesList object to initialize
101  * \return gphoto2 error code
102  *
103  * You would then call gp_abilities_list_load() in order to
104  * populate it.
105  */
106 int
107 gp_abilities_list_new (CameraAbilitiesList **list)
108 {
109         CHECK_NULL (list);
110
111         /*
112          * We do this here because everybody needs to call this function
113          * first before accessing a camera. Pretty ugly, but I don't know
114          * an other way without introducing a global initialization
115          * function...
116          */
117         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
118
119         CHECK_MEM (*list = malloc (sizeof (CameraAbilitiesList)));
120         memset (*list, 0, sizeof (CameraAbilitiesList));
121
122         return (GP_OK);
123 }
124
125 /**
126  * \brief Free the given CameraAbilitiesList object.
127  *
128  * \param list a CameraAbilitiesList
129  * \return a gphoto2 error code
130  */
131 int
132 gp_abilities_list_free (CameraAbilitiesList *list)
133 {
134         CHECK_NULL (list);
135
136         CHECK_RESULT (gp_abilities_list_reset (list));
137
138         free (list);
139
140         return (GP_OK);
141 }
142
143
144 typedef struct {
145         CameraList *list;
146         int result;
147 } foreach_data_t;
148
149
150 static int
151 foreach_func (const char *filename, lt_ptr data)
152 {
153         foreach_data_t *fd = data;
154         CameraList *list = fd->list;
155
156         gp_log (GP_LOG_DEBUG, "gphoto2-abilities-list",
157                 "Found '%s'.", filename);
158         fd->result = gp_list_append (list, filename, NULL);
159
160         return ((fd->result == GP_OK)?0:1);
161 }
162
163
164 static int
165 gp_abilities_list_load_dir (CameraAbilitiesList *list, const char *dir,
166                             GPContext *context)
167 {
168         CameraLibraryIdFunc id;
169         CameraLibraryAbilitiesFunc ab;
170         CameraText text;
171         int ret, x, old_count, new_count;
172         unsigned int i, p;
173         const char *filename;
174         CameraList *flist;
175         int count;
176         lt_dlhandle lh;
177
178         CHECK_NULL (list && dir);
179
180         gp_log (GP_LOG_DEBUG, "gphoto2-abilities-list",
181                 "Using ltdl to load camera libraries from '%s'...", dir);
182         CHECK_RESULT (gp_list_new (&flist));
183         ret = gp_list_reset (flist);
184         if (ret < GP_OK) {
185                 gp_list_free (flist);
186                 return ret;
187         }
188         if (1) { /* a new block in which we can define a temporary variable */
189                 int ret;
190                 foreach_data_t foreach_data = { NULL, GP_OK };
191                 foreach_data.list = flist;
192                 lt_dlinit ();
193                 lt_dladdsearchdir (dir);
194                 ret = lt_dlforeachfile (dir, foreach_func, &foreach_data);
195                 lt_dlexit ();
196                 if (ret != 0) {
197                         gp_list_free (flist);
198                         gp_log (GP_LOG_ERROR, "gp-abilities-list",
199                                 "Internal error looking for camlibs (%d)", ret);
200                         gp_context_error (context,
201                                           _("Internal error looking for camlibs. "
202                                             "(path names too long?)"));
203                         return (foreach_data.result!=GP_OK)?foreach_data.result:GP_ERROR;
204                 }
205         }
206         count = gp_list_count (flist);
207         if (count < GP_OK) {
208                 gp_list_free (flist);
209                 return ret;
210         }
211         gp_log (GP_LOG_DEBUG, "gp-abilities-list", "Found %i "
212                 "camera drivers.", count);
213         lt_dlinit ();
214         p = gp_context_progress_start (context, count,
215                 _("Loading camera drivers from '%s'..."), dir);
216         for (i = 0; i < count; i++) {
217                 ret = gp_list_get_name (flist, i, &filename);
218                 if (ret < GP_OK) {
219                         gp_list_free (flist);
220                         return ret;
221                 }
222                 lh = lt_dlopenext (filename);
223                 if (!lh) {
224                         gp_log (GP_LOG_DEBUG, "gphoto2-abilities-list",
225                                 "Failed to load '%s': %s.", filename,
226                                 lt_dlerror ());
227                         continue;
228                 }
229
230                 /* camera_id */
231                 id = lt_dlsym (lh, "camera_id");
232                 if (!id) {
233                         gp_log (GP_LOG_DEBUG, "gphoto2-abilities-list",
234                                 "Library '%s' does not seem to "
235                                 "contain a camera_id function: %s",
236                                 filename, lt_dlerror ());
237                         lt_dlclose (lh);
238                         continue;
239                 }
240
241                 /*
242                  * Make sure the camera driver hasn't been
243                  * loaded yet.
244                  */
245                 if (id (&text) != GP_OK) {
246                         lt_dlclose (lh);
247                         continue;
248                 }
249                 if (gp_abilities_list_lookup_id (list, text.text) >= 0) {
250                         lt_dlclose (lh);
251                         continue;
252                 }
253
254                 /* camera_abilities */
255                 ab = lt_dlsym (lh, "camera_abilities");
256                 if (!ab) {
257                         gp_log (GP_LOG_DEBUG, "gphoto2-abilities-list",
258                                 "Library '%s' does not seem to "
259                                 "contain a camera_abilities function: "
260                                 "%s", filename, lt_dlerror ());
261                         lt_dlclose (lh);
262                         continue;
263                 }
264
265                 old_count = gp_abilities_list_count (list);
266                 if (old_count < 0) {
267                         lt_dlclose (lh);
268                         continue;
269                 }
270
271                 if (ab (list) != GP_OK) {
272                         lt_dlclose (lh);
273                         continue;
274                 }
275
276                 lt_dlclose (lh);
277
278                 new_count = gp_abilities_list_count (list);
279                 if (new_count < 0)
280                         continue;
281
282                 /* Copy in the core-specific information */
283                 for (x = old_count; x < new_count; x++) {
284                         strcpy (list->abilities[x].id, text.text);
285                         strcpy (list->abilities[x].library, filename);
286                 }
287
288                 gp_context_progress_update (context, p, i);
289                 if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL) {
290                         lt_dlexit ();
291                         gp_list_free (flist);
292                         return (GP_ERROR_CANCEL);
293                 }
294         }
295         gp_context_progress_stop (context, p);
296         lt_dlexit ();
297         gp_list_free (flist);
298
299         return (GP_OK);
300 }
301
302
303 /**
304  * \brief Scans the system for camera drivers.
305  *
306  * \param list a CameraAbilitiesList
307  * \param context a GPContext
308  * \return a gphoto2 error code
309  *
310  * All supported camera models will then be added to the list.
311  *
312  */
313 int
314 gp_abilities_list_load (CameraAbilitiesList *list, GPContext *context)
315 {
316         const char *camlib_env = getenv(CAMLIBDIR_ENV);
317         const char *camlibs = (camlib_env != NULL)?camlib_env:CAMLIBS;
318         CHECK_NULL (list);
319
320         CHECK_RESULT (gp_abilities_list_load_dir (list, camlibs, context));
321         CHECK_RESULT (gp_abilities_list_sort (list));
322
323         return (GP_OK);
324 }
325
326
327 static int
328 gp_abilities_list_detect_usb (CameraAbilitiesList *list,
329                               int *ability, GPPort *port)
330 {
331         int i, count, res = GP_ERROR_IO_USB_FIND;
332
333         CHECK_RESULT (count = gp_abilities_list_count (list));
334
335         /* Detect USB cameras */
336         gp_log (GP_LOG_VERBOSE, __FILE__,
337                 "Auto-detecting USB cameras...");
338         *ability = -1;
339         for (i = 0; i < count; i++) {
340                 int v, p, c, s;
341
342                 if (!(list->abilities[i].port & port->type))
343                         continue;
344
345                 v = list->abilities[i].usb_vendor;
346                 p = list->abilities[i].usb_product;
347                 if (v) {
348                         res = gp_port_usb_find_device(port, v, p);
349                         if (res == GP_OK) {
350                                 gp_log(GP_LOG_DEBUG, __FILE__,
351                                         "Found '%s' (0x%x,0x%x)",
352                                         list->abilities[i].model,
353                                         v, p);
354                                 *ability = i;
355                         } else if (res < 0 && res != GP_ERROR_IO_USB_FIND) {
356                                 /* another error occurred.
357                                  * perhaps we should better
358                                  * report this to the calling
359                                  * method?
360                                  */
361                                 gp_log(GP_LOG_DEBUG, __FILE__,
362                                         "gp_port_usb_find_device(vendor=0x%x, "
363                                         "product=0x%x) returned %i, clearing "
364                                         "error message on port", v, p, res);
365                         }
366
367                         if (res != GP_ERROR_IO_USB_FIND)
368                                 return res;
369                 }
370
371                 c = list->abilities[i].usb_class;
372                 s = list->abilities[i].usb_subclass;
373                 p = list->abilities[i].usb_protocol;
374                 if (c) {
375                         res = gp_port_usb_find_device_by_class(port, c, s, p);
376                         if (res == GP_OK) {
377                                 gp_log(GP_LOG_DEBUG, __FILE__,
378                                         "Found '%s' (0x%x,0x%x,0x%x)",
379                                         list->abilities[i].model,
380                                         c, s, p);
381                                 *ability = i;
382                         } else if (res < 0 && res != GP_ERROR_IO_USB_FIND) {
383                                 /* another error occurred.
384                                  * perhaps we should better
385                                  * report this to the calling
386                                  * method?
387                                  */
388                                 gp_log(GP_LOG_DEBUG, __FILE__,
389                                         "gp_port_usb_find_device_by_class("
390                                         "class=0x%x, subclass=0x%x, "
391                                         "protocol=0x%x) returned %i, "
392                                         "clearing error message on port",
393                                         c, s, p, res);
394                         }
395
396                         if (res != GP_ERROR_IO_USB_FIND)
397                                 return res;
398                 }
399         }
400
401         return res;
402 }
403
404
405 /**
406  * \param list a CameraAbilitiesList
407  * \param info_list the GPPortInfoList of ports to use for detection
408  * \param l a #CameraList that contains the autodetected cameras after the call
409  * \param context a #GPContext
410  *
411  * Tries to detect any camera connected to the computer using the supplied
412  * list of supported cameras and the supplied info_list of ports.
413  *
414  * \return a gphoto2 error code
415  */
416 int
417 gp_abilities_list_detect (CameraAbilitiesList *list,
418                           GPPortInfoList *info_list, CameraList *l,
419                           GPContext *context)
420 {
421         GPPortInfo info;
422         GPPort *port;
423         int i, info_count;
424
425         CHECK_NULL (list && info_list && l);
426
427         gp_list_reset (l);
428
429         CHECK_RESULT (info_count = gp_port_info_list_count (info_list));
430
431         CHECK_RESULT (gp_port_new (&port));
432         for (i = 0; i < info_count; i++) {
433                 int res;
434
435                 CHECK_RESULT (gp_port_info_list_get_info (info_list, i, &info));
436                 CHECK_RESULT (gp_port_set_info (port, info));
437                 switch (info.type) {
438                 case GP_PORT_USB:
439                 case GP_PORT_USB_SCSI:
440                 case GP_PORT_USB_DISK_DIRECT: {
441                         int ability;
442                         res = gp_abilities_list_detect_usb (list, &ability, port);
443                         if (res == GP_OK) {
444                                 gp_list_append(l,
445                                         list->abilities[ability].model,
446                                         info.path);
447                         } else if (res < 0)
448                                 gp_port_set_error (port, NULL);
449
450                         break;
451                 }
452                 case GP_PORT_DISK: {
453                         char    *s, path[1024];
454                         struct stat stbuf;
455
456                         s = strchr (info.path, ':');
457                         if (!s)
458                                 break;
459                         s++;
460                         snprintf (path, sizeof(path), "%s/DCIM", s);
461                         if (-1 == stat(path, &stbuf)) {
462                                 snprintf (path, sizeof(path), "%s/dcim", s);
463                                 if (-1 == stat(path, &stbuf))
464                                         continue;
465                         }
466                         gp_list_append (l, "Mass Storage Camera", info.path);
467                         break;
468                 }
469                 case GP_PORT_PTPIP: {
470                         char    *s;
471
472                         s = strchr (info.path, ':');
473                         if (!s) break;
474                         s++;
475                         if (!strlen(s)) break;
476                         gp_list_append (l, "PTP/IP Camera", info.path);
477                         break;
478                 }
479                 default:
480                         /*
481                          * We currently cannot detect any cameras on this
482                          * port
483                          */
484                         break;
485                 }
486         }
487
488         gp_port_free (port);
489
490         return (GP_OK);
491 }
492
493
494 /**
495  * \brief Remove first colon from string, if any. Replace it by a space.
496  * \param str a char * string
497  */
498 static void
499 remove_colon_from_string (char *str)
500 {
501         char *ch;
502         ch = strchr(str, ':');
503         if (ch) {
504                 *ch = ' ';
505         }
506 }
507
508
509 /**
510  * \brief Append the abilities to the list.
511  * \param list  CameraAbilitiesList
512  * \param abilities  CameraAbilities
513  * \return a gphoto2 error code
514  *
515  * This function is called by a camera library on camera_abilities()
516  * in order to inform libgphoto2 about a supported camera model.
517  *
518  */
519 int
520 gp_abilities_list_append (CameraAbilitiesList *list, CameraAbilities abilities)
521 {
522         CameraAbilities *new_abilities;
523
524         CHECK_NULL (list);
525
526         if (!list->count)
527                 new_abilities = malloc (sizeof (CameraAbilities));
528         else
529                 new_abilities = realloc (list->abilities,
530                                 sizeof (CameraAbilities) * (list->count + 1));
531         CHECK_MEM (new_abilities);
532         list->abilities = new_abilities;
533
534         memcpy (&(list->abilities [list->count]), &abilities,
535                 sizeof (CameraAbilities));
536
537         /* FIXME: We replace the colon by a space in the model string
538          *        This keeps backward compatibility until we have
539          *        thought of and implemented something better.
540          */
541         remove_colon_from_string(list->abilities[list->count].model);
542
543         list->count++;
544
545         return (GP_OK);
546 }
547
548
549 /**
550  * \brief Reset the list.
551  * \param list a CameraAbilitiesList
552  * \return a gphoto2 error code
553  */
554 int
555 gp_abilities_list_reset (CameraAbilitiesList *list)
556 {
557         CHECK_NULL (list);
558
559         if (list->abilities) {
560                 free (list->abilities);
561                 list->abilities = NULL;
562         }
563         list->count = 0;
564
565         return (GP_OK);
566 }
567
568
569 /**
570  * \brief Count the entries in the supplied list.
571  * \param list a #CameraAbilitiesList
572  * \returns The number of entries or a gphoto2 error code
573  */
574 int
575 gp_abilities_list_count (CameraAbilitiesList *list)
576 {
577         CHECK_NULL (list);
578
579         return (list->count);
580 }
581
582 static int
583 cmp_abilities (const void *a, const void *b) {
584         const CameraAbilities *ca = a;
585         const CameraAbilities *cb = b;
586
587         return strcasecmp (ca->model, cb->model);
588 }
589
590 static int
591 gp_abilities_list_sort (CameraAbilitiesList *list)
592 {
593         CHECK_NULL (list);
594
595         qsort (list->abilities, list->count, sizeof(CameraAbilities), cmp_abilities);
596         return (GP_OK);
597 }
598
599
600 static int
601 gp_abilities_list_lookup_id (CameraAbilitiesList *list, const char *id)
602 {
603         int x;
604
605         CHECK_NULL (list && id);
606
607         for (x = 0; x < list->count; x++)
608                 if (!strcmp (list->abilities[x].id, id))
609                         return (x);
610
611         return (GP_ERROR);
612 }
613
614
615 /**
616  * \brief Search the list for an entry of given model name
617  * \param list a #CameraAbilitiesList
618  * \param model a camera model name
619  * \return Index of entry or gphoto2 error code
620  */
621 int
622 gp_abilities_list_lookup_model (CameraAbilitiesList *list, const char *model)
623 {
624         int x;
625
626         CHECK_NULL (list && model);
627
628         for (x = 0; x < list->count; x++) {
629                 if (!strcasecmp (list->abilities[x].model, model))
630                         return (x);
631         }
632
633         gp_log (GP_LOG_ERROR, "gphoto2-abilities-list", _("Could not find "
634                 "any driver for '%s'"), model);
635         return (GP_ERROR_MODEL_NOT_FOUND);
636 }
637
638
639 /**
640  * \brief Retrieve the camera abilities of entry with supplied index number.
641  *
642  * \param list a CameraAbilitiesList
643  * \param index index
644  * \param abilities pointer to CameraAbilities for returned data.
645  * \return a gphoto2 error code
646  *
647  * Retrieves the camera abilities of entry with supplied
648  * index number. Typically, you would call gp_camera_set_abilities()
649  * afterwards in order to prepare the initialization of a camera.
650  */
651 int
652 gp_abilities_list_get_abilities (CameraAbilitiesList *list, int index,
653                                  CameraAbilities *abilities)
654 {
655         CHECK_NULL (list && abilities);
656
657         if (index < 0 || index >= list->count)
658                 return (GP_ERROR_BAD_PARAMETERS);
659
660         memcpy (abilities, &list->abilities[index], sizeof (CameraAbilities));
661
662         return (GP_OK);
663 }
664
665
666 #ifdef _GPHOTO2_INTERNAL_CODE
667
668 /* enum CameraOperation */
669 const StringFlagItem gpi_camera_operation_map[] = {
670         { "none",            GP_OPERATION_NONE },
671         { "capture_image",   GP_OPERATION_CAPTURE_IMAGE },
672         { "capture_video",   GP_OPERATION_CAPTURE_VIDEO },
673         { "capture_audio",   GP_OPERATION_CAPTURE_AUDIO },
674         { "capture_preview", GP_OPERATION_CAPTURE_PREVIEW },
675         { "config",          GP_OPERATION_CONFIG },
676         { NULL, 0 },
677 };
678
679 /* enum CameraFileOperation */
680 const StringFlagItem gpi_file_operation_map[] = {
681         { "none",    GP_FILE_OPERATION_NONE },
682         { "delete",  GP_FILE_OPERATION_DELETE },
683         { "preview", GP_FILE_OPERATION_PREVIEW },
684         { "raw",     GP_FILE_OPERATION_RAW },
685         { "audio",   GP_FILE_OPERATION_AUDIO },
686         { "exif",    GP_FILE_OPERATION_EXIF },
687         { NULL, 0 },
688 };
689
690 /* enum CameraFolderOperation */
691 const StringFlagItem gpi_folder_operation_map[] = {
692         { "none",       GP_FOLDER_OPERATION_NONE },
693         { "delete_all", GP_FOLDER_OPERATION_DELETE_ALL },
694         { "put_file",   GP_FOLDER_OPERATION_PUT_FILE },
695         { "make_dir",   GP_FOLDER_OPERATION_MAKE_DIR },
696         { "remove_dir", GP_FOLDER_OPERATION_REMOVE_DIR },
697         { NULL, 0 },
698 };
699
700 /* enum GphotoDeviceType */
701 const StringFlagItem gpi_gphoto_device_type_map[] = {
702         { "still_camera", GP_DEVICE_STILL_CAMERA },
703         { "audio_player", GP_DEVICE_AUDIO_PLAYER },
704         { NULL, 0 },
705 };
706
707 /* enum CameraDriverStatus */
708 const StringFlagItem gpi_camera_driver_status_map[] = {
709         { "production",   GP_DRIVER_STATUS_PRODUCTION },
710         { "testing",      GP_DRIVER_STATUS_TESTING },
711         { "experimental", GP_DRIVER_STATUS_EXPERIMENTAL },
712         { "deprecated",   GP_DRIVER_STATUS_DEPRECATED },
713         { NULL, 0 },
714 };
715
716 #endif /* _GPHOTO2_INTERNAL_CODE */
717
718
719 /*
720  * Local Variables:
721  * c-file-style:"linux"
722  * indent-tabs-mode:t
723  * End:
724  */