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