1 /* library.c for libgphoto2/camlibs/digigr8
2  *
3  * Copyright (C) 2005 - 2010 Theodore Kilgore <kilgota@auburn.edu>
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 <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include <bayer.h>
30 #include <gamma.h>
31 
32 
33 #include <gphoto2/gphoto2.h>
34 
35 #ifdef ENABLE_NLS
36 #  include <libintl.h>
37 #  undef _
38 #  define _(String) dgettext (PACKAGE, String)
39 #  ifdef gettext_noop
40 #    define N_(String) gettext_noop (String)
41 #  else
42 #    define N_(String) (String)
43 #  endif
44 #else
45 #  define _(String) (String)
46 #  define N_(String) (String)
47 #endif
48 
49 #include "digigr8.h"
50 
51 #include <gphoto2/gphoto2-port.h>
52 
53 #define GP_MODULE "digigr8"
54 
55 static const struct {
56 	char *name;
57 	CameraDriverStatus status;
58 	unsigned short idVendor;
59 	unsigned short idProduct;
60 } models[] = {
61 	{"Digigr8",		GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
62 	{"Cobra Digital Camera DC150", GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770,
63 									0x905c},
64 	{"Che-Ez Snap SNAP-U",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
65 	{"DC-N130t",		GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905C},
66 	{"Soundstar TDC-35",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
67 	{"Nexxtech Mini Digital Camera", GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770,
68 									0x905c},
69 	{"Vivitar Vivicam35",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
70 	{"Praktica Slimpix",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
71 	{"ZINA Mini Digital Keychain Camera",	GP_DRIVER_STATUS_EXPERIMENTAL,
72 							    0x2770, 0x905c},
73 	{"Pixie Princess Jelly-Soft",		GP_DRIVER_STATUS_EXPERIMENTAL,
74 							    0x2770, 0x905c},
75 	{"Sakar Micro Digital 2428x",		GP_DRIVER_STATUS_EXPERIMENTAL,
76 							    0x2770, 0x905c},
77 	{"Stop & Shop 87096",		GP_DRIVER_STATUS_EXPERIMENTAL,
78 							    0x2770, 0x905c},
79 	{"Jazz JDC9",		GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
80 	{"Aries Digital Keychain Camera, ITEM 128986",
81 				GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x905c},
82 	{"Disney pix micro",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x9050},
83 	{"Lego Bionicle",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x9051},
84 	{"Barbie Camera (Digital Blue)",	GP_DRIVER_STATUS_EXPERIMENTAL,
85 							    0x2770, 0x9051},
86 	/* from IRC reporter, adam@piggz.co.uk */
87 	{"Disney pix micro 2",	GP_DRIVER_STATUS_EXPERIMENTAL, 0x2770, 0x9052},
88 	{"Suprema Digital Keychain Camera",	GP_DRIVER_STATUS_EXPERIMENTAL,
89 								0x2770, 0x913d},
90 	{"Sakar 28290 and 28292  Digital Concepts Styleshot",
91 						GP_DRIVER_STATUS_EXPERIMENTAL,
92 							0x2770, 0x913d},
93 	{"Sakar 23070  Crayola Digital Camera", GP_DRIVER_STATUS_EXPERIMENTAL,
94 							0x2770, 0x913d},
95 	{"Sakar 92045  Spiderman",    GP_DRIVER_STATUS_EXPERIMENTAL,
96 							0x2770, 0x913d},
97 	{NULL,0,0,0}
98 };
99 
100 int
camera_id(CameraText * id)101 camera_id(CameraText *id)
102 {
103 	strncpy (id->text, "SQ905C chipset camera",32);
104 	return GP_OK;
105 }
106 
107 
108 int
camera_abilities(CameraAbilitiesList * list)109 camera_abilities(CameraAbilitiesList *list)
110 {
111 	int i;
112 	CameraAbilities a;
113 
114 	for (i = 0; models[i].name; i++) {
115 		memset (&a, 0, sizeof(a));
116 		strncpy (a.model, models[i].name,32);
117 		a.status = models[i].status;
118 		a.port   = GP_PORT_USB;
119 		a.speed[0] = 0;
120 		a.usb_vendor = models[i].idVendor;
121 		a.usb_product= models[i].idProduct;
122 		if (a.status == GP_DRIVER_STATUS_EXPERIMENTAL)
123 			a.operations = GP_OPERATION_NONE;
124 		else
125 			a.operations = GP_OPERATION_CAPTURE_PREVIEW;
126 		a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL;
127 		a.file_operations   = GP_FILE_OPERATION_PREVIEW
128 					+ GP_FILE_OPERATION_RAW;
129 		gp_abilities_list_append (list, a);
130 	}
131 	return GP_OK;
132 }
133 
134 static int
camera_summary(Camera * camera,CameraText * summary,GPContext * context)135 camera_summary(Camera *camera, CameraText *summary, GPContext *context)
136 {
137 	if (!camera->pl->init_done)
138 		digi_init (camera->port, camera->pl);
139 	snprintf (summary->text, 100,
140 			("Your USB camera seems to have an SQ905C chipset.\n"
141 			"The total number of pictures in it is %i\n"),
142 				camera->pl->nb_entries);
143 	return GP_OK;
144 }
145 
camera_manual(Camera * camera,CameraText * manual,GPContext * context)146 static int camera_manual(Camera *camera, CameraText *manual,
147 							GPContext *context)
148 {
149 	strncpy(manual->text,
150 	_(
151 	"For cameras with insides from S&Q Technologies, which have the \n"
152 	"USB Vendor ID 0x2770 and Product ID 0x905C, 0x9050, 0x9051,\n"
153 	"0x9052, or 0x913D.  Photos are saved in PPM format.\n\n"
154 	"Some of these cameras allow software deletion of all photos.\n"
155 	"Others do not. No supported camera can do capture-image. All\n"
156 	"can do capture-preview (image captured and sent to computer).\n"
157 	"If delete-all does work for your camera, then capture-preview will\n"
158 	"have the side-effect that it also deletes what is on the camera.\n\n"
159 	"File uploading is not supported for these cameras. Also, none of the\n"
160 	"supported cameras allow deletion of individual photos by use of a\n"
161 	"software command.\n"
162 	), 700);
163 	return (GP_OK);
164 }
165 
166 
167 static int
camera_about(Camera * camera,CameraText * about,GPContext * context)168 camera_about(Camera *camera, CameraText *about, GPContext *context)
169 {
170 	strncpy (about->text, _("sq905C generic driver\n"
171 			    "Theodore Kilgore <kilgota@auburn.edu>\n"),64);
172 	return GP_OK;
173 }
174 
175 /*************** File and Downloading Functions *******************/
176 
177 
178 static int
file_list_func(CameraFilesystem * fs,const char * folder,CameraList * list,void * data,GPContext * context)179 file_list_func(CameraFilesystem *fs, const char *folder, CameraList *list,
180                 void *data, GPContext *context)
181 {
182 	Camera *camera = data;
183 	int n;
184 	if(!camera->pl->init_done)
185 		digi_init (camera->port, camera->pl);
186 	GP_DEBUG ("List files in %s\n", folder);
187 	n = camera->pl->nb_entries;
188 	gp_list_populate(list, "pict%03i.ppm", n);
189 	return GP_OK;
190 }
191 
192 
193 static int
get_file_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileType type,CameraFile * file,void * user_data,GPContext * context)194 get_file_func(CameraFilesystem *fs, const char *folder, const char *filename,
195 		CameraFileType type, CameraFile *file, void *user_data,
196 							GPContext *context)
197 {
198 	int status = GP_OK;
199 	Camera *camera = user_data;
200 	unsigned int b;
201 	unsigned int w, h;
202 	int k, next;
203 	unsigned char comp_ratio;
204 	unsigned char lighting;
205 	unsigned char *data = NULL;
206 	unsigned char *p_data = NULL;
207 	unsigned char *ppm;
208 	unsigned char *ptr;
209 	unsigned char gtable[256];
210 	int size;
211 
212 	if (!camera->pl->init_done)
213 		digi_init (camera->port, camera->pl);
214 
215 	/* Get the entry number of the photo on the camera */
216 	k = gp_filesystem_number (camera->fs, "/", filename, context);
217 
218 	if (GP_FILE_TYPE_EXIF ==type) return GP_ERROR_FILE_EXISTS;
219 
220 	if (GP_FILE_TYPE_RAW!=type && GP_FILE_TYPE_NORMAL
221 				    != type && GP_FILE_TYPE_PREVIEW != type) {
222 		return GP_ERROR_NOT_SUPPORTED;
223 	}
224 
225 	next = camera->pl->last_fetched_entry +1;
226 	while (next < k) {
227 		b = digi_get_data_size (camera->pl, next);
228 		data = malloc(b);
229 		if(!data) return GP_ERROR_NO_MEMORY;
230 		digi_read_picture_data (camera->port, data, b, next);
231 		free(data);
232 		next ++;
233 	}
234 
235 	comp_ratio = digi_get_comp_ratio (camera->pl, k);
236 	w = digi_get_picture_width (camera->pl, k);
237 	switch (w) {
238 	case 176: h = 144; break;
239 	case 640: h = 480; break;
240 	case 320: h = 240; break;
241 	default:  h = 288; break;
242 	}
243 	lighting = camera->pl->catalog[k*0x10+0x0b];
244 	b = digi_get_data_size (camera->pl, k);
245 	if (!b) {
246 		GP_DEBUG("Photo number %i deleted?\n",k+1);
247 		camera->pl->last_fetched_entry = k;
248 		return GP_OK;
249 	}
250 	if (b < w*h) {
251 		GP_DEBUG("need %d bytes, supposed to read only %d", w*h, b);
252 		return GP_ERROR;
253 	}
254 	data = malloc (b);
255 	if(!data) return GP_ERROR_NO_MEMORY;
256 
257 	GP_DEBUG("Fetch entry %i\n", k);
258 	digi_read_picture_data (camera->port, data, b, k);
259 	camera->pl->last_fetched_entry = k;
260 
261 	if (GP_FILE_TYPE_RAW == type) {	/* type is GP_FILE_TYPE_RAW */
262 		size = b;
263 		gp_file_set_mime_type (file, GP_MIME_RAW);
264 		gp_file_append(file, (char *)data, size);
265 		/* Save photo's catalog entry as a footer for the raw file */
266 		gp_file_append(file, (char *)camera->pl->catalog
267 						+ k * 0x10, 0x10);
268 		/* Reset camera when done, for more graceful exit. */
269 		if (k +1 == camera->pl->nb_entries) {
270 			digi_rewind (camera->port, camera->pl);
271 		}
272 		free(data);
273 		return(GP_OK);
274 	}
275 
276 	/*
277 	 * Now put the data into a PPM image file.
278 	 */
279 
280 	ppm = malloc (w * h * 3 + 256); /* room for data + header */
281 	if (!ppm) {
282 		status = GP_ERROR_NO_MEMORY;
283 		goto end;
284 	}
285 	snprintf ((char *)ppm, 64,
286 			"P6\n"
287 			"# CREATOR: gphoto2, SQ905C library\n"
288 			"%d %d\n"
289 			"255\n", w, h);
290 	size = strlen ((char *)ppm);
291 	ptr = ppm + size;
292 	size = size + (w * h * 3);
293 	GP_DEBUG ("size = %i\n", size);
294 	p_data = malloc(w * h);
295 	if (!p_data) {
296 		status =  GP_ERROR_NO_MEMORY;
297 		free (ppm);
298 		goto end;
299 	}
300 	if(comp_ratio) {
301 		digi_decompress (p_data, data, w, h);
302 	} else
303 		memcpy(p_data, data, w * h);
304 	GP_DEBUG("w %d, h %d, size %d", w, h, size);
305 	gp_ahd_decode(p_data, w , h , ptr, BAYER_TILE_BGGR);
306 	free(p_data);
307 	digi_postprocess(w, h, ptr);
308 	if (lighting < 0x40) {
309 	GP_DEBUG(
310 		"Low light condition. Using default gamma. \
311 						No white balance.\n");
312 		gp_gamma_fill_table (gtable, .65);
313 		gp_gamma_correct_single(gtable,ptr,w*h);
314 	} else
315 		white_balance (ptr, w*h, 1.1);
316 	gp_file_set_mime_type (file, GP_MIME_PPM);
317 	gp_file_set_data_and_size (file, (char *)ppm, size);
318 	/* Reset camera when done, for more graceful exit. */
319 	if (k + 1 == camera->pl->nb_entries) {
320 		digi_rewind (camera->port, camera->pl);
321 	}
322  end:
323 	free(data);
324 	return status;
325 }
326 
327 static int
delete_all_func(CameraFilesystem * fs,const char * folder,void * data,GPContext * context)328 delete_all_func(CameraFilesystem *fs, const char *folder, void *data,
329 						GPContext *context)
330 {
331 	Camera *camera = data;
332 	if (!camera->pl->delete_all)
333 		return GP_ERROR_NOT_SUPPORTED;
334 	if (!camera->pl->init_done)
335 		digi_init(camera->port, camera->pl);
336 	digi_delete_all(camera->port, camera->pl);
337 	return GP_OK;
338 }
339 
340 static int
camera_capture_preview(Camera * camera,CameraFile * file,GPContext * context)341 camera_capture_preview(Camera *camera, CameraFile *file, GPContext *context)
342 
343 {
344 	unsigned char get_size[0x50];
345 	unsigned char *raw_data;
346 	unsigned char *frame_data;
347 	unsigned char *ppm, *ptr;
348 	char lighting;
349 	unsigned char gtable[256];
350 	int size;
351 	int w = 320;
352 	int h = 240;
353 	int b;
354 
355 	digi_reset (camera->port);
356 	gp_port_usb_msg_write (camera->port, 0x0c, 0x1440, 0x110f, NULL, 0);
357 	gp_port_read(camera->port, (char *)get_size, 0x50);
358 	GP_DEBUG("get_size[0x40] = 0x%x\n", get_size[0x40]);
359 	lighting = get_size[0x48];
360         b = get_size[0x40] | get_size[0x41] << 8 | get_size[0x42] << 16
361 						| get_size[0x43]<<24;
362 	GP_DEBUG("b = 0x%x\n", b);
363 	raw_data = malloc(b);
364 
365 	if(!raw_data)
366 		return GP_ERROR_NO_MEMORY;
367 
368 	if (!((gp_port_read(camera->port, (char *)raw_data, b)==b))) {
369 		free(raw_data);
370 		GP_DEBUG("Error in reading data\n");
371 		return GP_ERROR;
372 	}
373 	frame_data = malloc(w * h);
374 	if (!frame_data) {
375 		free(raw_data);
376 		return GP_ERROR_NO_MEMORY;
377 	}
378 	digi_decompress (frame_data, raw_data, w, h);
379 	free(raw_data);
380 	/* Now put the data into a PPM image file. */
381 	ppm = malloc (w * h * 3 + 256);
382 	if (!ppm) {
383 		free(frame_data);
384 		return GP_ERROR_NO_MEMORY;
385 	}
386 	snprintf ((char *)ppm, 64,
387 		"P6\n"
388 		"# CREATOR: gphoto2, SQ905C library\n"
389 		"%d %d\n"
390 		"255\n", w, h);
391 	ptr = ppm + strlen ((char*)ppm);
392 	size = strlen ((char*)ppm) + (w * h * 3);
393 	GP_DEBUG ("size = %i\n", size);
394 	gp_ahd_decode (frame_data, w , h , ptr, BAYER_TILE_BGGR);
395 	free(frame_data);
396         if (lighting < 0x40) {
397 		GP_DEBUG(
398 		"Low light condition. Default gamma. No white balance.\n");
399 		gp_gamma_fill_table (gtable, .65);
400 		gp_gamma_correct_single(gtable,ptr,w*h);
401 	} else
402 		white_balance(ptr, w * h, 1.1);
403 	gp_file_set_mime_type(file, GP_MIME_PPM);
404 	gp_file_set_data_and_size(file, (char *)ppm, size);
405 	digi_reset(camera->port);
406 	return(GP_OK);
407 }
408 
409 /*************** Exit and Initialization Functions ******************/
410 
411 static int
camera_exit(Camera * camera,GPContext * context)412 camera_exit (Camera *camera, GPContext *context)
413 {
414 	GP_DEBUG("SQ camera_exit");
415 	digi_reset(camera->port);
416 
417 	if (camera->pl) {
418 		free (camera->pl->catalog);
419 		free (camera->pl);
420 		camera->pl = NULL;
421 	}
422 	return GP_OK;
423 }
424 
425 static CameraFilesystemFuncs fsfuncs = {
426 	.file_list_func = file_list_func,
427 	.get_file_func = get_file_func,
428 	.delete_all_func = delete_all_func
429 };
430 
431 int
camera_init(Camera * camera,GPContext * context)432 camera_init(Camera *camera, GPContext *context)
433 {
434 	GPPortSettings settings;
435 	CameraAbilities abilities;
436 	int ret = 0;
437 
438 	ret = gp_camera_get_abilities(camera,&abilities);
439 	if (ret < 0) return ret;
440 	GP_DEBUG("product number is 0x%x\n", abilities.usb_product);
441 
442 	/* Now, set up all the function pointers */
443 	camera->functions->summary	= camera_summary;
444         camera->functions->manual	= camera_manual;
445 	camera->functions->about	= camera_about;
446 	camera->functions->capture_preview
447 					= camera_capture_preview;
448 	camera->functions->exit		= camera_exit;
449 
450 	GP_DEBUG ("Initializing the camera\n");
451 
452 	ret = gp_port_get_settings(camera->port,&settings);
453 	if (ret < 0)
454 		return ret;
455 	ret = gp_port_set_settings(camera->port,settings);
456 	if (ret < 0)
457 		return ret;
458 
459 	/* Tell the CameraFilesystem where to get lists from */
460 	gp_filesystem_set_funcs(camera->fs, &fsfuncs, camera);
461 	camera->pl = malloc(sizeof (CameraPrivateLibrary));
462 	if (!camera->pl)
463 		return GP_ERROR_NO_MEMORY;
464 	camera->pl->catalog = NULL;
465 	camera->pl->nb_entries = 0;
466 	switch (abilities.usb_product) {
467 	case 0x9050:
468 	case 0x9051:
469 	case 0x9052:
470 		camera->pl->delete_all = 1;
471 		break;
472 	default:
473 		camera->pl->delete_all = 0;
474 	}
475 	camera->pl->init_done=0;
476 
477 	/* Do digi_init() only if needed for the requested operation. */
478 
479 	return GP_OK;
480 }
481