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