1 /* library.c:
2 *
3 * Copyright 2002 Lutz Mueller <lutz@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 */
20
21 #define _DEFAULT_SOURCE
22
23 #include "config.h"
24
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <time.h>
29
30 #include <gphoto2/gphoto2-library.h>
31 #include <gphoto2/gphoto2-port.h>
32 #include <gphoto2/gphoto2-port-log.h>
33
34 #include "fuji.h"
35
36 #ifdef ENABLE_NLS
37 # include <libintl.h>
38 # undef _
39 # define _(String) dgettext (GETTEXT_PACKAGE, String)
40 # ifdef gettext_noop
41 # define N_(String) gettext_noop (String)
42 # else
43 # define N_(String) (String)
44 # endif
45 #else
46 # define textdomain(String) (String)
47 # define gettext(String) (String)
48 # define dgettext(Domain,Message) (Message)
49 # define dcgettext(Domain,Message,Type) (Message)
50 # define bindtextdomain(Domain,Directory) (Domain)
51 # define _(String) (String)
52 # define N_(String) (String)
53 #endif
54
55 #define GP_MODULE "fuji"
56
57 #define CR(result) {int __r = (result); if (__r < 0) return (__r);}
58
59 static const struct {
60 const char *model;
61 } models[] = {
62 {"Apple:QuickTake 200"},
63 {"Fuji:DS-7"},
64 {"Fuji:DX-5"},
65 {"Fuji:DX-7"},
66 {"Fuji:DX-10"},
67 {"Fuji:MX-500"},
68 {"Fuji:MX-600"},
69 {"Fuji:MX-700"},
70 {"Fuji:MX-1200"},
71 {"Fuji:MX-1700"},
72 {"Fuji:MX-2700"},
73 {"Fuji:MX-2900"},
74 {"Leica:Digilux Zoom"},
75 {"Samsung:Kenox SSC-350N"},
76 {"Toshiba:PDR-M1"},
77 {NULL}
78 };
79
80 struct _CameraPrivateLibrary {
81 unsigned long speed;
82 unsigned char cmds[0xff];
83 };
84
85 static const struct {
86 FujiCmd command;
87 const char *name;
88 } Commands[] = {
89 {FUJI_CMD_PIC_GET, "get picture"},
90 {FUJI_CMD_PIC_GET_THUMB, "get thumbnail"},
91 {FUJI_CMD_SPEED, "change speed"},
92 {FUJI_CMD_VERSION, "get version"},
93 {FUJI_CMD_PIC_NAME, "get name of picture"},
94 {FUJI_CMD_PIC_COUNT, "get number of pictures"},
95 {FUJI_CMD_PIC_SIZE, "get size of picture"},
96 {FUJI_CMD_PIC_DEL, "delete picture"},
97 {FUJI_CMD_TAKE, "capture picture"},
98 {FUJI_CMD_FLASH_GET, "get flash mode"},
99 {FUJI_CMD_FLASH_SET, "set flash mode"},
100 {FUJI_CMD_FLASH_CHARGE, "charge flash"},
101 {FUJI_CMD_CMDS_VALID, "list valid commands"},
102 {FUJI_CMD_PREVIEW, "capture preview"},
103 {FUJI_CMD_DATE_GET, "get date"},
104 {0, NULL}};
105
106 static const char *
cmd_get_name(FujiCmd command)107 cmd_get_name (FujiCmd command)
108 {
109 unsigned int i;
110
111 for (i = 0; Commands[i].name; i++)
112 if (Commands[i].command == command)
113 break;
114
115 return (Commands[i].name);
116 }
117
118 int
camera_id(CameraText * id)119 camera_id (CameraText *id)
120 {
121 strcpy (id->text, "Fuji");
122
123 return (GP_OK);
124 }
125
126 int
camera_abilities(CameraAbilitiesList * list)127 camera_abilities (CameraAbilitiesList *list)
128 {
129 CameraAbilities a;
130 int i;
131
132 memset (&a, 0, sizeof (a));
133 for (i = 0; models[i].model; i++) {
134 strcpy (a.model, models[i].model);
135 a.port = GP_PORT_SERIAL;
136 a.speed[0] = 9600;
137 a.speed[1] = 19200;
138 a.speed[2] = 38400;
139 a.speed[3] = 56700;
140 a.speed[4] = 115200;
141 a.speed[5] = 0;
142 a.folder_operations = GP_FOLDER_OPERATION_PUT_FILE;
143 a.file_operations = GP_FILE_OPERATION_PREVIEW |
144 GP_FILE_OPERATION_DELETE;
145 a.operations = GP_OPERATION_CONFIG;
146 CR (gp_abilities_list_append (list, a));
147 }
148
149 return (GP_OK);
150 }
151
152 static int
camera_about(Camera * camera,CameraText * about,GPContext * context)153 camera_about (Camera *camera, CameraText *about, GPContext *context)
154 {
155 strcpy (about->text, _(""
156 "Matthew G. Martin\n"
157 "Based on fujiplay by Thierry Bousch "
158 "<bousch@topo.math.u-psud.fr>\n"));
159
160 return (GP_OK);
161 }
162
163 static int
file_list_func(CameraFilesystem * fs,const char * folder,CameraList * list,void * data,GPContext * context)164 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
165 void *data, GPContext *context)
166 {
167 Camera *camera = data;
168 unsigned int i;
169 unsigned int n;
170 const char *name;
171
172 /*
173 * Count the pictures on the camera. If we don't have any, we
174 * are done.
175 */
176 CR (fuji_pic_count (camera, &n, context));
177 if (!n)
178 return (GP_OK);
179
180 /*
181 * Try to get the name of the first file. If we can't, just come
182 * up with some dummy names.
183 */
184 if (fuji_pic_name (camera, 1, &name, context) < 0) {
185 CR (gp_list_populate (list, "DSCF%04i.JPG", n));
186 return (GP_OK);
187 }
188 CR (gp_list_append (list, name, NULL));
189
190 /* Get the names of the remaining files. */
191 for (i = 2; i <= n; i++) {
192 CR (fuji_pic_name (camera, i, &name, context));
193 CR (gp_list_append (list, name, NULL));
194 }
195
196 return (GP_OK);
197 }
198
199 static int
get_file_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileType type,CameraFile * file,void * data,GPContext * context)200 get_file_func (CameraFilesystem *fs, const char *folder,
201 const char *filename, CameraFileType type,
202 CameraFile *file, void *data, GPContext *context)
203 {
204 Camera *camera = data;
205 int n;
206 unsigned char *d;
207 unsigned int size;
208
209 /* We need file numbers starting with 1 */
210 CR (n = gp_filesystem_number (camera->fs, folder, filename, context));
211 n++;
212
213 switch (type) {
214 case GP_FILE_TYPE_NORMAL:
215 CR (fuji_pic_get (camera, n, &d, &size, context));
216 break;
217 case GP_FILE_TYPE_PREVIEW:
218 CR (fuji_pic_get_thumb (camera, n, &d, &size, context));
219 break;
220 default:
221 return (GP_ERROR_NOT_SUPPORTED);
222 }
223 CR (gp_file_set_data_and_size (file, (char *)d, size));
224 CR (gp_file_set_mime_type (file, GP_MIME_JPEG));
225
226 return (GP_OK);
227 }
228
229 static int
put_file_func(CameraFilesystem * fs,const char * folder,const char * name,CameraFileType type,CameraFile * file,void * data,GPContext * context)230 put_file_func (CameraFilesystem *fs, const char *folder, const char *name,
231 CameraFileType type, CameraFile *file, void *data, GPContext *context)
232 {
233 Camera *camera = data;
234 const char *d;
235 unsigned long int d_len;
236
237 if (type != GP_FILE_TYPE_NORMAL)
238 return GP_ERROR_BAD_PARAMETERS;
239 CR (gp_file_get_data_and_size (file, &d, &d_len));
240 CR (fuji_upload_init (camera, name, context));
241 return fuji_upload (camera, (unsigned char *)d, d_len, context);
242 }
243
244 static int
del_file_func(CameraFilesystem * fs,const char * folder,const char * filename,void * data,GPContext * context)245 del_file_func (CameraFilesystem *fs, const char *folder,
246 const char *filename, void *data, GPContext *context)
247 {
248 Camera *camera = data;
249 int n;
250
251 /* We need file numbers starting with 1 */
252 CR (n = gp_filesystem_number (camera->fs, folder, filename, context));
253 n++;
254
255 CR (fuji_pic_del (camera, n, context));
256
257 return (GP_OK);
258 }
259
260 static const struct {
261 FujiSpeed speed;
262 unsigned int bit_rate;
263 } Speeds[] = {
264 {FUJI_SPEED_115200, 115200},
265 {FUJI_SPEED_57600, 57600},
266 {FUJI_SPEED_38400, 38400},
267 {FUJI_SPEED_19200, 19200},
268 {FUJI_SPEED_9600, 9600},
269 {FUJI_SPEED_9600, 0}
270 };
271
272 static int
camera_exit(Camera * camera,GPContext * context)273 camera_exit (Camera *camera, GPContext *context)
274 {
275 if (camera->pl) {
276 free (camera->pl);
277 camera->pl = NULL;
278 }
279
280 return (GP_OK);
281 }
282
283 static int
pre_func(Camera * camera,GPContext * context)284 pre_func (Camera *camera, GPContext *context)
285 {
286 int r;
287 unsigned int i;
288 GPPortSettings settings;
289
290 GP_DEBUG ("Initializing connection...");
291
292 CR (gp_port_get_settings (camera->port, &settings));
293 CR (fuji_ping (camera, context));
294 if (!camera->pl->speed) {
295
296 /* Set to the highest possible speed. */
297 for (i = 0; Speeds[i].bit_rate; i++) {
298 r = fuji_set_speed (camera, Speeds[i].speed, NULL);
299 if (r >= 0)
300 break;
301 }
302
303 /*
304 * Change the port's speed and check if the camera is
305 * still there.
306 */
307 settings.serial.speed = Speeds[i].bit_rate;
308 CR (gp_port_set_settings (camera->port, settings));
309 GP_DEBUG("Pinging to check new speed %i.", Speeds[i].bit_rate);
310 CR (fuji_ping (camera, context));
311
312 } else {
313
314 /* User specified a speed. Check if the speed is possible */
315 for (i = 0; Speeds[i].bit_rate; i++)
316 if (Speeds[i].bit_rate == camera->pl->speed)
317 break;
318 if (!Speeds[i].bit_rate) {
319 gp_context_error (context, _("Bit rate %ld is not "
320 "supported."), camera->pl->speed);
321 return (GP_ERROR_NOT_SUPPORTED);
322 }
323
324 /* Change the speed if necessary. */
325 if (camera->pl->speed != Speeds[i].bit_rate) {
326 CR (fuji_set_speed (camera, Speeds[i].speed, context));
327
328 /*
329 * Change the port's speed and check if the camera is
330 * still there.
331 */
332 settings.serial.speed = Speeds[i].bit_rate;
333 CR (gp_port_set_settings (camera->port, settings));
334 CR (fuji_ping (camera, context));
335 }
336 }
337
338 return (GP_OK);
339 }
340
341 static int
post_func(Camera * camera,GPContext * context)342 post_func (Camera *camera, GPContext *context)
343 {
344 GPPortSettings settings;
345 GP_DEBUG ("Terminating connection...");
346
347 /* Put the camera back to 9600 bps if necessary. */
348 CR (gp_port_get_settings (camera->port, &settings));
349 if (settings.serial.speed != 9600) {
350 CR (fuji_set_speed (camera, FUJI_SPEED_9600, context));
351 settings.serial.speed = 9600;
352 CR (gp_port_set_settings (camera->port, settings));
353 }
354
355 return (GP_OK);
356 }
357
358 static int
camera_get_config(Camera * camera,CameraWidget ** window,GPContext * context)359 camera_get_config (Camera *camera, CameraWidget **window, GPContext *context)
360 {
361 CameraWidget *widget;
362 struct tm tm;
363 time_t t;
364 FujiDate date;
365 const char *id;
366
367 CR (gp_widget_new (GP_WIDGET_WINDOW, _("Configuration for "
368 "your FUJI camera"), window));
369
370 /* Date & Time */
371 if (fuji_date_get (camera, &date, context) >= 0) {
372 CR (gp_widget_new (GP_WIDGET_DATE, _("Date & Time"), &widget));
373 CR (gp_widget_append (*window, widget));
374 memset (&tm, 0, sizeof (struct tm));
375 tm.tm_year = date.year;
376 tm.tm_mon = date.month;
377 tm.tm_mday = date.day;
378 tm.tm_hour = date.hour;
379 tm.tm_min = date.min;
380 tm.tm_sec = date.sec;
381 t = mktime (&tm);
382 CR (gp_widget_set_value (widget, &t));
383 }
384
385 /* ID */
386 if (fuji_id_get (camera, &id, context) >= 0) {
387 CR (gp_widget_new (GP_WIDGET_TEXT, _("ID"), &widget));
388 CR (gp_widget_append (*window, widget));
389 CR (gp_widget_set_value (widget, (void *) id));
390 }
391
392 return (GP_OK);
393 }
394
395 static int
camera_set_config(Camera * camera,CameraWidget * window,GPContext * context)396 camera_set_config (Camera *camera, CameraWidget *window, GPContext *context)
397 {
398 CameraWidget *widget;
399 FujiDate date;
400 time_t t;
401 struct tm *tm;
402 const char *id;
403
404 /* Date & Time */
405 if ((gp_widget_get_child_by_label (window, _("Date & Time"),
406 &widget) >= 0) &&
407 gp_widget_changed (widget)) {
408 gp_widget_set_changed (widget, 0);
409 CR (gp_widget_get_value (widget, &t));
410 tm = localtime (&t);
411 date.year = tm->tm_year;
412 date.month = tm->tm_mon;
413 date.day = tm->tm_mday;
414 date.hour = tm->tm_hour;
415 date.min = tm->tm_min;
416 date.sec = tm->tm_sec;
417 CR (fuji_date_set (camera, date, context));
418 }
419
420 /* ID */
421 if ((gp_widget_get_child_by_label (window, _("ID"), &widget) >= 0) &&
422 gp_widget_changed (widget)) {
423 gp_widget_set_changed (widget, 0);
424 CR (gp_widget_get_value (widget, &id));
425 CR (fuji_id_set (camera, id, context));
426 }
427
428
429 return (GP_OK);
430 }
431
432 static int
camera_summary(Camera * camera,CameraText * text,GPContext * context)433 camera_summary (Camera *camera, CameraText *text, GPContext *context)
434 {
435 const char *string;
436 char buf[1024];
437 unsigned int avail_mem;
438
439 memset (text->text, 0, sizeof (text->text));
440
441 if (fuji_version (camera, &string, context) >= 0) {
442 strcat (text->text, _("Version: "));
443 strcat (text->text, string);
444 strcat (text->text, "\n");
445 }
446
447 if (fuji_model (camera, &string, context) >= 0) {
448 strcat (text->text, _("Model: "));
449 strcat (text->text, string);
450 strcat (text->text, "\n");
451 }
452
453 if (fuji_avail_mem (camera, &avail_mem, context) >= 0) {
454 snprintf (buf, sizeof (buf), "%d", avail_mem);
455 strcat (text->text, _("Available memory: "));
456 strcat (text->text, buf);
457 strcat (text->text, "\n");
458 }
459
460 return (GP_OK);
461 }
462
463 static int
get_info_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileInfo * info,void * data,GPContext * context)464 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
465 CameraFileInfo *info, void *data, GPContext *context)
466 {
467 Camera *camera = data;
468 int n;
469 unsigned int size;
470
471 info->file.fields = GP_FILE_INFO_NONE;
472 info->preview.fields = GP_FILE_INFO_NONE;
473 info->audio.fields = GP_FILE_INFO_NONE;
474
475 /* We need file numbers starting with 1 */
476 CR (n = gp_filesystem_number (camera->fs, folder, filename, context));
477 n++;
478
479 /* Size */
480 if (fuji_pic_size (camera, n, &size, context) >= 0) {
481 info->file.fields |= GP_FILE_INFO_SIZE;
482 info->file.size = size;
483 }
484
485 return (GP_OK);
486 }
487
488 static CameraFilesystemFuncs fsfuncs = {
489 .file_list_func = file_list_func,
490 .get_file_func = get_file_func,
491 .del_file_func = del_file_func,
492 .put_file_func = put_file_func,
493 .get_info_func = get_info_func
494 };
495
496 int
camera_init(Camera * camera,GPContext * context)497 camera_init (Camera *camera, GPContext *context)
498 {
499 GPPortSettings settings;
500 unsigned int i;
501
502 /* Setup all function pointers */
503 camera->functions->pre_func = pre_func;
504 camera->functions->post_func = post_func;
505 camera->functions->about = camera_about;
506 camera->functions->exit = camera_exit;
507 camera->functions->get_config = camera_get_config;
508 camera->functions->set_config = camera_set_config;
509 camera->functions->summary = camera_summary;
510
511 /* We need to store some data */
512 camera->pl = malloc (sizeof (CameraPrivateLibrary));
513 if (!camera->pl)
514 return (GP_ERROR_NO_MEMORY);
515 memset (camera->pl, 0, sizeof (CameraPrivateLibrary));
516
517 /* Set up the port, but remember the current speed. */
518 CR (gp_port_set_timeout (camera->port, 1000));
519 CR (gp_port_get_settings (camera->port, &settings));
520 camera->pl->speed = settings.serial.speed;
521 settings.serial.speed = 9600;
522 settings.serial.bits = 8;
523 settings.serial.parity = GP_PORT_SERIAL_PARITY_EVEN;
524 settings.serial.stopbits = 1;
525 CR (gp_port_set_settings (camera->port, settings));
526
527 /* Set up the filesystem. */
528 CR (gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera));
529
530 /* Initialize the connection */
531 CR (pre_func (camera, context));
532 /*
533 * What commands does this camera support? The question is not
534 * easy to answer, as "One issue the DS7 has is that the
535 * supported command list command is not supported"
536 * (Matt Martin <matt.martin@ieee.org>).
537 */
538 if (fuji_get_cmds (camera, camera->pl->cmds, context) >= 0) {
539 GP_DEBUG ("Your camera supports the following command(s):");
540 for (i = 0; i < 0xff; i++)
541 if (camera->pl->cmds[i])
542 GP_DEBUG (" - 0x%02x: '%s'", i,
543 cmd_get_name (i));
544 }
545
546 return (GP_OK);
547 }
548