1 /* Sitronix st2205 picframe access library
3 * Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; either version 2.1 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 # include <langinfo.h>
30 #include <gphoto2/gphoto2-library.h>
31 #include <gphoto2/gphoto2-result.h>
32 #include <gphoto2/gphoto2-port.h>
33 #include <gphoto2/gphoto2-setting.h>
39 # define _(String) dgettext (GETTEXT_PACKAGE, String)
41 # define N_(String) gettext_noop (String)
43 # define N_(String) (String)
46 # define _(String) (String)
47 # define N_(String) (String)
51 camera_id (CameraText *id)
53 strcpy (id->text, "ST2205 USB picture frame");
59 camera_abilities (CameraAbilitiesList *list)
63 memset (&a, 0, sizeof(a));
64 strcpy (a.model, "ST2205 USB picture frame");
65 a.status = GP_DRIVER_STATUS_TESTING;
66 a.port = GP_PORT_USB_DISK_DIRECT;
68 a.usb_vendor = 0x1403;
69 a.usb_product= 0x0001;
70 a.operations = GP_OPERATION_NONE;
71 a.folder_operations = GP_FOLDER_OPERATION_PUT_FILE |
72 GP_FOLDER_OPERATION_DELETE_ALL;
73 a.file_operations = GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_RAW;
74 return gp_abilities_list_append (list, a);
78 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
80 sprintf (summary->text,
81 _("Your USB picture frame has a ST2205 chipset\n"));
86 camera_manual (Camera *camera, CameraText *manual, GPContext *context)
90 "ST2205 based picture frames come with a variety of resolutions.\n"
91 "The gphoto driver for these devices allows you to download,\n"
92 "upload and delete pictures from the picture frame."
99 camera_about (Camera *camera, CameraText *about, GPContext *context)
103 "ST2205 USB picture frame driver\n"
104 "Hans de Goede <hdegoede@redhat.com>\n"
105 "This driver allows you to download, upload and delete pictures\n"
106 "from the picture frame."
112 static int get_file_idx(CameraPrivateLibrary *pl, const char *folder,
113 const char *filename)
117 if (strcmp (folder, "/"))
118 return GP_ERROR_DIRECTORY_NOT_FOUND;
120 for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
121 if (!strcmp (filename, pl->filenames[i]))
125 if (i == ST2205_MAX_NO_FILES)
126 return GP_ERROR_FILE_NOT_FOUND;
133 rotate90 (gdImagePtr src, gdImagePtr dest)
137 for (y = 0; y < dest->sy; y++)
138 for (x = 0; x < dest->sx; x++)
139 dest->tpixels[y][x] =
140 src->tpixels[src->sy - x - 1][y];
144 rotate270 (gdImagePtr src, gdImagePtr dest)
148 for (y = 0; y < dest->sy; y++)
149 for (x = 0; x < dest->sx; x++)
150 dest->tpixels[y][x] =
151 src->tpixels[x][src->sx - y - 1];
155 needs_rotation (Camera *camera)
157 int display_orientation, user_orientation = camera->pl->orientation;
159 if (camera->pl->width > camera->pl->height)
160 display_orientation = ORIENTATION_LANDSCAPE;
162 display_orientation = ORIENTATION_PORTRAIT;
164 if (user_orientation == ORIENTATION_AUTO) {
165 if (camera->pl->width == 240 && camera->pl->height == 320)
166 user_orientation = ORIENTATION_LANDSCAPE;
168 user_orientation = display_orientation;
171 return display_orientation != user_orientation;
176 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
177 CameraFileType type, CameraFile *file, void *data,
180 Camera *camera = data;
184 gdImagePtr im, rotated;
188 idx = get_file_idx(camera->pl, folder, filename);
192 if (type == GP_FILE_TYPE_RAW) {
195 size = st2205_read_raw_file (camera, idx, &raw);
196 if (size < 0) return size;
198 gp_file_set_mime_type (file, GP_MIME_RAW);
199 gp_file_set_name (file, filename);
200 gp_file_set_data_and_size (file, (char *)raw, size);
206 if (type != GP_FILE_TYPE_NORMAL)
207 return GP_ERROR_NOT_SUPPORTED;
209 im = gdImageCreateTrueColor(camera->pl->width, camera->pl->height);
211 return GP_ERROR_NO_MEMORY;
213 ret = st2205_read_file(camera, idx, im->tpixels);
219 if (needs_rotation (camera)) {
220 rotated = gdImageCreateTrueColor (im->sy, im->sx);
221 if (rotated == NULL) {
223 return GP_ERROR_NO_MEMORY;
225 rotate270 (im, rotated);
230 gdpng = gdImagePngPtr(im, &size);
233 return GP_ERROR_NO_MEMORY;
235 ret = gp_file_set_mime_type (file, GP_MIME_PNG);
236 if (ret < 0) { gdFree (gdpng); return ret; }
238 ret = gp_file_set_name (file, filename);
239 if (ret < 0) { gdFree (gdpng); return ret; }
241 ret = gp_file_append (file, gdpng, size);
245 gp_log(GP_LOG_ERROR,"st2205", "GD decompression not supported - no libGD present during build");
246 return GP_ERROR_NOT_SUPPORTED;
251 put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file,
252 void *data, GPContext *context)
255 Camera *camera = data;
256 char *c, *in_name, *out_name, *filedata = NULL;
258 int ret, in_width, in_height, in_x, in_y;
260 double aspect_in, aspect_out;
264 unsigned long filesize = 0;
265 gdImagePtr rotated, im_out, im_in = NULL;
267 if (strcmp (folder, "/"))
268 return GP_ERROR_DIRECTORY_NOT_FOUND;
270 gp_file_get_name (file, &name);
273 in_name = strdup (name);
275 out_name = malloc (outc + 1);
276 if (!in_name || !out_name) {
279 return GP_ERROR_NO_MEMORY;
282 /* Convert name to ASCII */
286 if (iconv (camera->pl->cd, &in, &inc, &out, &outc) == -1) {
289 gp_log (GP_LOG_ERROR, "iconv",
290 "Failed to convert filename to ASCII");
291 return GP_ERROR_OS_FAILURE;
293 outc = out - out_name;
296 for (i = 0; i < inc; i++) {
297 if ((uint8_t)in_name[i] < 0x20 || (uint8_t)in_name[i] > 0x7E)
300 out_name[i] = in_name[i];
306 /* Remove file extension, and if necessary truncate the name */
307 c = strrchr (out_name, '.');
310 if (outc > ST2205_FILENAME_LENGTH)
311 out_name[ST2205_FILENAME_LENGTH] = 0;
313 ret = gp_file_get_data_and_size (file, (const char **)&filedata,
315 if (ret < 0) { free (out_name); return ret; }
317 /* Try loading the file using gd, starting with the most often
320 /* gdImageCreateFromJpegPtr is chatty on error, don't call it on
323 (uint8_t)filedata[0] == 0xff && (uint8_t)filedata[1] == 0xd8)
324 im_in = gdImageCreateFromJpegPtr(filesize, filedata);
326 im_in = gdImageCreateFromPngPtr(filesize, filedata);
328 im_in = gdImageCreateFromGifPtr(filesize, filedata);
330 im_in = gdImageCreateFromWBMPPtr(filesize, filedata);
332 gp_log (GP_LOG_ERROR, "st2205",
333 "Unrecognized file format for file: %s%s",
336 return GP_ERROR_BAD_PARAMETERS;
339 if (needs_rotation (camera)) {
340 rotated = gdImageCreateTrueColor (im_in->sy, im_in->sx);
341 if (rotated == NULL) {
342 gdImageDestroy (im_in);
344 return GP_ERROR_NO_MEMORY;
346 rotate90 (im_in, rotated);
347 gdImageDestroy (im_in);
351 im_out = gdImageCreateTrueColor(camera->pl->width, camera->pl->height);
352 if (im_out == NULL) {
353 gdImageDestroy (im_in);
355 return GP_ERROR_NO_MEMORY;
359 aspect_in = (double)im_in->sx / im_in->sy;
360 aspect_out = (double)im_out->sx / im_out->sy;
361 if (aspect_in > aspect_out) {
362 /* Reduce in width (crop left and right) */
363 in_width = (im_in->sx / aspect_in) * aspect_out;
364 in_x = (im_in->sx - in_width) / 2;
365 in_height = im_in->sy;
368 /* Reduce in height (crop top and bottom) */
369 in_width = im_in->sx;
371 in_height = (im_in->sy * aspect_in) / aspect_out;
372 in_y = (im_in->sy - in_height) / 2;
375 gdImageCopyResampled (im_out, im_in, 0, 0, in_x, in_y,
376 im_out->sx, im_out->sy,
377 in_width, in_height);
379 if (im_in->sx != im_out->sx ||
380 im_in->sy != im_out->sy)
381 gdImageSharpen(im_out, 100);
383 ret = st2205_write_file (camera, out_name, im_out->tpixels);
385 /* Add to our filenames list */
386 ST2205_SET_FILENAME(camera->pl->filenames[ret], out_name, ret);
387 /* And commit the changes to the device */
388 ret = st2205_commit(camera);
391 gdImageDestroy (im_in);
392 gdImageDestroy (im_out);
396 gp_log(GP_LOG_ERROR,"st2205", "GD compression not supported - no libGD present during build");
397 return GP_ERROR_NOT_SUPPORTED;
402 delete_file_func (CameraFilesystem *fs, const char *folder,
403 const char *filename, void *data, GPContext *context)
405 Camera *camera = data;
408 idx = get_file_idx(camera->pl, folder, filename);
412 ret = st2205_delete_file(camera, idx);
413 if (ret < 0) return ret;
415 /* Also remove the file from our cached filelist */
416 camera->pl->filenames[idx][0] = 0;
418 return st2205_commit(camera);
422 delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
425 Camera *camera = data;
427 CHECK (st2205_delete_all (camera))
429 return st2205_commit (camera);
433 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
434 CameraFileInfo *info, void *data, GPContext *context)
436 memset (info, 0, sizeof(CameraFileInfo));
437 /* FIXME: fill in some stuff? */
442 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
443 void *data, GPContext *context)
445 Camera *camera = data;
448 for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
449 if (camera->pl->filenames[i][0]) {
450 ret = gp_list_append (list, camera->pl->filenames[i],
452 if (ret < 0) return ret;
460 storage_info_func (CameraFilesystem *fs,
461 CameraStorageInformation **sinfos,
463 void *data, GPContext *context)
465 Camera *camera = (Camera*)data;
466 CameraStorageInformation *sinfo;
469 free = st2205_get_free_mem_size (camera);
470 if (free < 0) return free;
472 sinfo = malloc(sizeof(CameraStorageInformation));
473 if (!sinfo) return GP_ERROR_NO_MEMORY;
478 sinfo->fields = GP_STORAGEINFO_BASE;
479 strcpy(sinfo->basedir, "/");
481 sinfo->fields |= GP_STORAGEINFO_ACCESS;
482 sinfo->access = GP_STORAGEINFO_AC_READWRITE;
483 sinfo->fields |= GP_STORAGEINFO_STORAGETYPE;
484 sinfo->type = GP_STORAGEINFO_ST_FIXED_RAM;
485 sinfo->fields |= GP_STORAGEINFO_FILESYSTEMTYPE;
486 sinfo->fstype = GP_STORAGEINFO_FST_GENERICFLAT;
487 sinfo->fields |= GP_STORAGEINFO_MAXCAPACITY;
488 sinfo->capacitykbytes = st2205_get_mem_size (camera) / 1024;
489 sinfo->fields |= GP_STORAGEINFO_FREESPACEKBYTES;
490 sinfo->freekbytes = free / 1024;
495 static CameraFilesystemFuncs fsfuncs = {
496 .file_list_func = file_list_func,
497 .get_info_func = get_info_func,
498 .get_file_func = get_file_func,
499 .del_file_func = delete_file_func,
500 .put_file_func = put_file_func,
501 .delete_all_func = delete_all_func,
502 .storage_info_func = storage_info_func
506 orientation_to_string (int orientation)
508 switch (orientation) {
509 case ORIENTATION_AUTO:
511 case ORIENTATION_LANDSCAPE:
512 return _("Landscape");
513 case ORIENTATION_PORTRAIT:
514 return _("Portrait");
521 string_to_orientation (const char *str)
523 if (strcmp (str, _("Auto")) == 0)
524 return ORIENTATION_AUTO;
525 else if (strcmp (str, _("Landscape")) == 0)
526 return ORIENTATION_LANDSCAPE;
527 else if (strcmp (str, _("Portrait")) == 0)
528 return ORIENTATION_PORTRAIT;
530 return GP_ERROR_NOT_SUPPORTED;
534 camera_get_config (Camera *camera, CameraWidget **window, GPContext *context)
538 GP_DEBUG ("*** camera_get_config");
540 gp_widget_new (GP_WIDGET_WINDOW,
541 _("Picture Frame Configuration"), window);
543 gp_widget_new (GP_WIDGET_TOGGLE,
544 _("Synchronize frame data and time with PC"), &child);
545 gp_widget_set_value (child, &camera->pl->syncdatetime);
546 gp_widget_append (*window, child);
548 gp_widget_new (GP_WIDGET_RADIO, _("Orientation"), &child);
549 gp_widget_add_choice (child, orientation_to_string (0));
550 gp_widget_add_choice (child, orientation_to_string (1));
551 gp_widget_add_choice (child, orientation_to_string (2));
552 gp_widget_set_value (child,
553 orientation_to_string (camera->pl->orientation));
554 gp_widget_append (*window, child);
560 camera_set_config (Camera *camera, CameraWidget *window, GPContext *context)
565 GP_DEBUG ("*** camera_set_config");
567 ret = gp_widget_get_child_by_label (window,
568 _("Synchronize frame data and time with PC"), &child);
570 gp_widget_get_value (child, &camera->pl->syncdatetime);
572 ret = gp_widget_get_child_by_label (window, _("Orientation"), &child);
576 gp_widget_get_value (child, &value);
577 orientation = string_to_orientation (value);
578 if (orientation < 0) return orientation;
579 camera->pl->orientation = orientation;
586 camera_exit (Camera *camera, GPContext *context)
590 if (camera->pl != NULL) {
591 buf[0] = '0' + camera->pl->syncdatetime;
593 gp_setting_set ("st2205", "syncdatetime", buf);
594 gp_setting_set ("st2205", "orientation", orientation_to_string
595 (camera->pl->orientation));
597 if (camera->pl->cd != (iconv_t) -1)
598 iconv_close (camera->pl->cd);
600 st2205_close (camera);
608 camera_init (Camera *camera, GPContext *context)
615 st2205_filename clean_name;
617 /* First, set up all the function pointers */
618 camera->functions->exit = camera_exit;
619 camera->functions->summary = camera_summary;
620 camera->functions->manual = camera_manual;
621 camera->functions->about = camera_about;
622 camera->functions->get_config = camera_get_config;
623 camera->functions->set_config = camera_set_config;
624 /* FIXME add gp_camera_get_storageinfo support */
626 /* Tell the CameraFilesystem where to get lists from */
627 gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
629 camera->pl = calloc (1, sizeof(CameraPrivateLibrary));
630 if (!camera->pl) return GP_ERROR_NO_MEMORY;
632 ret = gp_setting_get("st2205", "syncdatetime", buf);
634 camera->pl->syncdatetime = buf[0] == '1';
636 camera->pl->syncdatetime = 1;
638 ret = gp_setting_get("st2205", "orientation", buf);
640 ret = string_to_orientation (buf);
642 camera->pl->orientation = ret;
646 curloc = nl_langinfo (CODESET);
649 camera->pl->cd = iconv_open("ASCII", curloc);
650 if (camera->pl->cd == (iconv_t) -1) {
651 gp_log (GP_LOG_ERROR, "iconv",
652 "Failed to create iconv converter");
653 camera_exit (camera, context);
654 return GP_ERROR_OS_FAILURE;
659 ret = st2205_open_device (camera);
661 ret = st2205_open_dump (camera,
662 "/home/hans/st2205tool/memdump.bin", 128, 128);
665 camera_exit (camera, context);
669 GP_DEBUG ("st2205 memory size: %d, free: %d",
670 st2205_get_mem_size (camera),
671 st2205_get_free_mem_size (camera));
673 /* Get the filenames from the picframe */
674 ret = st2205_get_filenames (camera, camera->pl->filenames);
676 camera_exit (camera, context);
680 /* And clean them up and make them unique */
681 for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
682 if (!camera->pl->filenames[i][0])
685 /* Filter out non ASCII chars (some frames ship
686 with sample photo's with garbage in the names) */
687 for (j = 0; camera->pl->filenames[i][j]; j++) {
688 if ((uint8_t)camera->pl->filenames[i][j] < 0x20 ||
689 (uint8_t)camera->pl->filenames[i][j] > 0x7E)
692 clean_name[j] = camera->pl->filenames[i][j];
696 ST2205_SET_FILENAME(camera->pl->filenames[i], clean_name, i);
699 /* Sync time if requested */
700 if (camera->pl->syncdatetime) {
705 if (localtime_r (&t , &tm)) {
706 ret = st2205_set_time_and_date (camera, &tm);
708 camera_exit (camera, context);