1 /* library.c
2
3 *
4 * Copyright (C) 2006-2010 Theodore Kilgore <kilgota@auburn.edu>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 */
21
22 #include <config.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "jl2005bcd_decompress.h"
29 #include <gphoto2/gphoto2.h>
30
31
32 #ifdef ENABLE_NLS
33 # include <libintl.h>
34 # undef _
35 # define _(String) dgettext (PACKAGE, String)
36 # ifdef gettext_noop
37 # define N_(String) gettext_noop (String)
38 # else
39 # define N_(String) (String)
40 # endif
41 #else
42 # define _(String) (String)
43 # define N_(String) (String)
44 #endif
45
46 #include "jl2005c.h"
47 #include <gphoto2/gphoto2-port.h>
48
49 #define GP_MODULE "jl2005c"
50
51 struct {
52 char *name;
53 CameraDriverStatus status;
54 unsigned short idVendor;
55 unsigned short idProduct;
56 } models[] = {
57 {" JL2005B/C/D camera", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
58 {"Argus DC1512e", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
59 {"Amazing Spiderman", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
60 {"Aries ATC-0017", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
61 {"Sakar no. 75379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
62 {"Sakar no. 81890", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
63 {"Sakar Nickelodeon iCarly no. 88061", GP_DRIVER_STATUS_EXPERIMENTAL,
64 0x0979, 0x0227},
65 {"Sakar Dora the Explorer no. 88067", GP_DRIVER_STATUS_EXPERIMENTAL,
66 0x0979, 0x0227},
67 {"Sakar no. 91379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
68 {"Sakar no. 98379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
69 {"Sakar Kidz-Cam no. 88379", GP_DRIVER_STATUS_EXPERIMENTAL,
70 0x0979, 0x0227},
71 {"Sakar clipshot no. 1169x", GP_DRIVER_STATUS_EXPERIMENTAL,
72 0x0979, 0x0227},
73 {"Sakar Sticker Wizard no. 59379", GP_DRIVER_STATUS_EXPERIMENTAL,
74 0x0979, 0x0227},
75 {"Sakar Star Wars kit no. 92022", GP_DRIVER_STATUS_EXPERIMENTAL,
76 0x0979, 0x0227},
77 {"Jazwares Star Wars no. 15256", GP_DRIVER_STATUS_EXPERIMENTAL,
78 0x0979, 0x0227},
79 {"Argus Bean Sprout", GP_DRIVER_STATUS_EXPERIMENTAL,
80 0x0979, 0x0227},
81 {"Global Point Clipster", GP_DRIVER_STATUS_EXPERIMENTAL,
82 0x0979, 0x0227},
83 {"Global Point 3 in 1 Digital Fun Graffiti 00044",
84 GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
85 {"Jazz JDK235", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
86 {"DIGITAL MID#0020509 (no-name camera)",
87 GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227},
88 {"Vivitar Freelance", GP_DRIVER_STATUS_EXPERIMENTAL,
89 0x0979, 0x0227},
90 {"Sakar Hello Kitty no. 94009", GP_DRIVER_STATUS_EXPERIMENTAL,
91 0x0979, 0x0227},
92 {NULL,0,0,0}
93 };
94
95 int
camera_id(CameraText * id)96 camera_id (CameraText *id)
97 {
98 strcpy (id->text, "JL2005B/C/D camera");
99 return GP_OK;
100 }
101
102 int
camera_abilities(CameraAbilitiesList * list)103 camera_abilities (CameraAbilitiesList *list)
104 {
105 int i;
106 CameraAbilities a;
107
108 for (i = 0; models[i].name; i++) {
109 memset (&a, 0, sizeof(a));
110 strcpy (a.model, models[i].name);
111 a.status = models[i].status;
112 a.port = GP_PORT_USB;
113 a.speed[0] = 0;
114 a.usb_vendor = models[i].idVendor;
115 a.usb_product= models[i].idProduct;
116 if (a.status == GP_DRIVER_STATUS_EXPERIMENTAL)
117 a.operations = GP_OPERATION_NONE;
118 else
119 a.operations = GP_OPERATION_CAPTURE_IMAGE;
120 a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL;
121 a.file_operations = GP_FILE_OPERATION_PREVIEW
122 + GP_FILE_OPERATION_RAW;
123 gp_abilities_list_append (list, a);
124 }
125 return GP_OK;
126 }
127
128 static int
camera_summary(Camera * camera,CameraText * summary,GPContext * context)129 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
130 {
131 int num_pics;
132 num_pics = camera->pl->nb_entries;
133 GP_DEBUG("camera->pl->nb_entries = %i\n",camera->pl->nb_entries);
134 sprintf(summary->text,
135 _("This camera contains a Jeilin JL2005%c chipset.\n"
136 "The number of photos in it is %i. \n"),
137 camera->pl->model, num_pics);
138 return GP_OK;
139 }
140
141 static int
camera_manual(Camera * camera,CameraText * manual,GPContext * context)142 camera_manual (Camera *camera, CameraText *manual, GPContext *context)
143 {
144 strcpy(manual->text,
145 _(
146 "This driver supports cameras with Jeilin JL2005B or C or D chip \n"
147 "These cameras do not support deletion of photos, nor uploading\n"
148 "of data. \n"
149 "If present on the camera, video clip frames are downloaded \n"
150 "as consecutive still photos.\n"
151 "For more details please consult libgphoto2/camlibs/README.jl2005c\n"
152 ));
153
154 return (GP_OK);
155 }
156
157
158 static int
camera_about(Camera * camera,CameraText * about,GPContext * context)159 camera_about (Camera *camera, CameraText *about, GPContext *context)
160 {
161 strcpy (about->text, _("jl2005bcd camera library\n"
162 "Theodore Kilgore <kilgota@auburn.edu>\n"));
163 return GP_OK;
164 }
165
166 /*************** File and Downloading Functions *******************/
167
168 static int
file_list_func(CameraFilesystem * fs,const char * folder,CameraList * list,void * data,GPContext * context)169 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
170 void *data, GPContext *context)
171 {
172 Camera *camera = data;
173 int n;
174 n = camera->pl->nb_entries;
175 gp_list_populate (list, "jl_%03i.ppm", n);
176 return GP_OK;
177 }
178
179 static int
get_info_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileInfo * info,void * data,GPContext * context)180 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
181 CameraFileInfo *info, void *data, GPContext *context)
182 {
183 info->file.fields = GP_FILE_INFO_TYPE;
184 strcpy (info->file.type, GP_MIME_PPM);
185
186 return (GP_OK);
187 }
188
189 static int
get_file_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileType type,CameraFile * file,void * user_data,GPContext * context)190 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
191 CameraFileType type, CameraFile *file, void *user_data,
192 GPContext *context)
193 {
194 Camera *camera = user_data;
195 int w, h = 0, b = 0, k;
196 unsigned char *pic_data, *pic_buffer, *pic_output = NULL;
197 int HEADERSIZE=16;
198 int outputsize;
199 unsigned long start_of_photo;
200 unsigned int downloadsize = 0;
201 int filled = 0;
202
203 GP_DEBUG ("Downloading pictures!\n");
204 if(!camera->pl->data_reg_opened)
205 jl2005c_open_data_reg (camera, camera->port);
206 /* These are cheap cameras. There ain't no EXIF data. */
207 if (GP_FILE_TYPE_EXIF == type) return GP_ERROR_FILE_EXISTS;
208
209 /* Get the number of the photo on the camera */
210 k = gp_filesystem_number (camera->fs, "/", filename, context);
211 h = camera->pl->table[16 * k + 4] << 3;
212 w = camera->pl->table[16 * k + 5] << 3;
213
214 GP_DEBUG ("height is %i\n", h);
215
216 b = jl2005c_get_pic_data_size(camera->pl, camera->pl->table, k);
217 GP_DEBUG("b = %i = 0x%x bytes\n", b, b);
218 start_of_photo = jl2005c_get_start_of_photo(camera->pl,
219 camera->pl->table, k);
220 GP_DEBUG("start_of_photo number %i = 0x%lx \n", k,start_of_photo);
221 pic_buffer = malloc(b + HEADERSIZE);
222 if (!pic_buffer) return GP_ERROR_NO_MEMORY;
223 memset(pic_buffer, 0, b + HEADERSIZE);
224 GP_DEBUG("buffersize b + 16 = %i = 0x%x bytes\n", b + 16, b + 16);
225 /* Copy info line for photo from allocation table, as header */
226 memcpy(pic_buffer, camera->pl->table + 16 * k, 16);
227 pic_data = pic_buffer + HEADERSIZE;
228
229 /*
230 * Camera can download in blocks of 0xfa00, with only the last block
231 * possibly smaller. So first we set up a cache of that size
232 * (if it is not set up already) to hold raw data. If one tries
233 * instead to download one photo at a time, the camera will misbehave;
234 * data will be lost or corrupted. The dog will bite you, too.
235 */
236
237 if (!(camera->pl->data_cache)) {
238 camera->pl->data_cache = malloc (MAX_DLSIZE);
239 }
240 if (!(camera->pl->data_cache)) {
241 GP_DEBUG ("no cache memory allocated!\n");
242 free (pic_buffer);
243 return GP_ERROR_NO_MEMORY;
244 }
245
246 /* Is there data in the cache, or not? If yes, read from it into the
247 * current photo, immediately. Update settings. But first two sanity
248 * checks.
249 */
250
251 if (start_of_photo < camera->pl->bytes_put_away) {
252 GP_DEBUG("photo number %i starts in a funny place!\n",k);
253 /* We need to start all over again to get this photo. */
254 jl2005c_reset (camera, camera->port);
255 jl2005c_init (camera, camera->port, camera->pl);
256 }
257 if (start_of_photo + b > camera->pl->total_data_in_camera) {
258 GP_DEBUG ("Photo runs past end of data. Exiting. \n");
259 GP_DEBUG ("Block size may be wrong for this camera\n");
260 free (pic_buffer);
261 return (GP_ERROR);
262 }
263 /*
264 * This while loop is entered if the photo number k-1 was not requested
265 * and thus has not been downloaded. The camera's rudimentary hardware
266 * obliges us to download all data consecutively and toss whatever
267 * portion of said data that we do not intend to use. The rudimentary
268 * hardware also does not like to stop downloading at the end of one
269 * photo and then to start on the next. It wants to keep getting data
270 * in size 0xfa00 increments, and only the last block can be smaller.
271 * To do otherwise will cause data to be lost or corrupted.
272 *
273 * Whoever tries to simplify this convoluted and ugly procedure is
274 * warned that the obvious simplifications, while much prettier,
275 * just won't work. A kutya harap.
276 */
277 while (camera->pl->bytes_read_from_camera <= start_of_photo) {
278 camera->pl->data_to_read = camera->pl->total_data_in_camera
279 - camera->pl->bytes_read_from_camera;
280 downloadsize = MAX_DLSIZE;
281 if (camera->pl->data_to_read < downloadsize)
282 downloadsize = camera->pl->data_to_read;
283 GP_DEBUG("downloadsize = 0x%x\n", downloadsize);
284 if (downloadsize)
285 jl2005c_read_data (
286 camera->port,
287 (char *) camera->pl->data_cache,
288 downloadsize);
289 camera->pl->bytes_read_from_camera += downloadsize;
290 }
291
292 camera->pl->bytes_put_away = start_of_photo;
293
294 if (camera->pl->bytes_read_from_camera > start_of_photo) {
295 if(start_of_photo + b <= camera->pl->bytes_read_from_camera) {
296 memcpy(pic_data, camera->pl->data_cache
297 + (start_of_photo % MAX_DLSIZE)
298 , b);
299 camera->pl->bytes_put_away += b;
300 /*
301 * Photo data is contained in what is already
302 * downloaded.
303 * Jump immediately to process the photo.
304 */
305 } else {
306 /* Photo starts in one 0xfa00-sized download and ends
307 * in another */
308 filled = camera->pl->bytes_read_from_camera
309 - start_of_photo;
310
311 memcpy(pic_data, camera->pl->data_cache
312 + (start_of_photo % MAX_DLSIZE),
313 filled);
314
315 camera->pl->bytes_put_away += filled;
316 }
317 }
318 while (camera->pl->bytes_put_away < start_of_photo + b ) {
319
320 camera->pl->data_to_read = camera->pl->total_data_in_camera
321 - camera->pl->bytes_read_from_camera;
322 downloadsize = MAX_DLSIZE;
323 if (camera->pl->data_to_read < downloadsize)
324 downloadsize = camera->pl->data_to_read;
325 GP_DEBUG("downloadsize = 0x%x\n", downloadsize);
326 if (downloadsize)
327 jl2005c_read_data (
328 camera->port,
329 (char *) camera->pl->data_cache,
330 downloadsize);
331 camera->pl->bytes_read_from_camera += downloadsize;
332
333 if (camera->pl->bytes_read_from_camera >=
334 start_of_photo + b ) {
335 GP_DEBUG("THIS ONE?\n");
336 memcpy(pic_data + filled, camera->pl->data_cache,
337 b - filled);
338 camera->pl->bytes_put_away += b - filled;
339 break;
340 } else {
341 GP_DEBUG("THIS ONE??\n");
342 if (!downloadsize)
343 break;
344 memcpy(pic_data + filled,
345 camera->pl->data_cache, downloadsize);
346 camera->pl->bytes_put_away += downloadsize;
347 filled += downloadsize;
348 }
349 }
350
351 if (type == GP_FILE_TYPE_RAW) {
352 gp_file_set_mime_type(file, GP_MIME_RAW);
353 gp_file_set_data_and_size(file, (char *)pic_buffer, b + 16);
354 return GP_OK;
355 #ifdef HAVE_LIBJPEG
356 } else if (type == GP_FILE_TYPE_PREVIEW) {
357 if (!camera->pl->can_do_capture) {
358 free (pic_buffer);
359 return GP_ERROR_NOT_SUPPORTED;
360 }
361 outputsize = (pic_buffer[9] & 0xf0) * 192 + 256;
362 GP_DEBUG("pic_buffer[9] is 0x%02x\n", pic_buffer[9]);
363 GP_DEBUG("Thumbnail outputsize = 0x%x = %d\n", outputsize,
364 outputsize);
365 if (outputsize == 256) {
366 GP_DEBUG("Frame %d has no thumbnail.\n", k);
367 free (pic_buffer);
368 return GP_OK;
369 }
370 pic_output = calloc(outputsize, 1);
371 if (!pic_output) {
372 free (pic_buffer);
373 return GP_ERROR_NO_MEMORY;
374 }
375 outputsize = jl2005bcd_decompress(pic_output, pic_buffer,
376 b + 16, 1);
377 free (pic_buffer);
378 if (outputsize < GP_OK) {
379 free (pic_output);
380 return outputsize;
381 }
382 GP_DEBUG("Thumbnail outputsize recalculated is 0x%x = %d\n",
383 outputsize, outputsize);
384 gp_file_set_mime_type(file, GP_MIME_PPM);
385 gp_file_set_data_and_size(file, (char *)pic_output,
386 outputsize);
387 } else if (type == GP_FILE_TYPE_NORMAL) {
388 outputsize = 3 * w * h + 256;
389 pic_output = calloc(outputsize, 1);
390 if (!pic_output) {
391 free (pic_buffer);
392 return GP_ERROR_NO_MEMORY;
393 }
394 outputsize = jl2005bcd_decompress(pic_output, pic_buffer,
395 b + 16, 0);
396 free (pic_buffer);
397 if (outputsize < GP_OK) {
398 free (pic_output);
399 return outputsize;
400 }
401 gp_file_set_mime_type(file, GP_MIME_PPM);
402 gp_file_set_data_and_size(file, (char *)pic_output,
403 outputsize);
404 #endif
405 } else {
406 free (pic_buffer);
407 return GP_ERROR_NOT_SUPPORTED;
408 }
409
410 return GP_OK;
411 }
412
413 static int
delete_all_func(CameraFilesystem * fs,const char * folder,void * data,GPContext * context)414 delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
415 GPContext *context)
416 {
417 Camera *camera = data;
418
419 GP_DEBUG(" * delete_all_func()");
420 jl2005c_delete_all(camera, camera->port);
421
422 return (GP_OK);
423 }
424
425
426
427 /*************** Exit and Initialization Functions ******************/
428
429 static int
camera_exit(Camera * camera,GPContext * context)430 camera_exit (Camera *camera, GPContext *context)
431 {
432 GP_DEBUG ("jl2005c camera_exit");
433 jl2005c_reset(camera, camera->port);
434 gp_port_close(camera->port);
435 if (camera->pl) {
436 free (camera->pl);
437 camera->pl = NULL;
438 }
439 return GP_OK;
440 }
441
442 static CameraFilesystemFuncs fsfuncs = {
443 .file_list_func = file_list_func,
444 .get_file_func = get_file_func,
445 .get_info_func = get_info_func,
446 .delete_all_func = delete_all_func
447 };
448
449 int
camera_init(Camera * camera,GPContext * context)450 camera_init (Camera *camera, GPContext *context)
451 {
452 GPPortSettings settings;
453 int ret = 0;
454
455 /* First, set up all the function pointers */
456 camera->functions->manual = camera_manual;
457 camera->functions->summary = camera_summary;
458 camera->functions->about = camera_about;
459 camera->functions->exit = camera_exit;
460
461 GP_DEBUG ("Initializing the camera\n");
462 ret = gp_port_get_settings(camera->port,&settings);
463 if (ret < 0) return ret;
464
465 switch (camera->port->type) {
466 case GP_PORT_SERIAL:
467 return ( GP_ERROR );
468 case GP_PORT_USB:
469 settings.usb.config = 1;
470 settings.usb.altsetting = 0;
471 settings.usb.interface = 0;
472 /* inep 0x84 used to initialize. Then switch to 0x82! */
473 settings.usb.inep = 0x84;
474 settings.usb.outep = 0x03;
475 break;
476 default:
477 return ( GP_ERROR );
478 }
479
480 ret = gp_port_set_settings(camera->port,settings);
481 if (ret < 0) return ret;
482
483 GP_DEBUG("interface = %i\n", settings.usb.interface);
484 GP_DEBUG("inep = %x\n", settings.usb.inep);
485 GP_DEBUG("outep = %x\n", settings.usb.outep);
486
487 /* Tell the CameraFilesystem where to get lists from */
488 gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
489
490 camera->pl = malloc (sizeof (CameraPrivateLibrary));
491 if (!camera->pl) return GP_ERROR_NO_MEMORY;
492 memset (camera->pl, 0, sizeof (CameraPrivateLibrary));
493 /* Connect to the camera */
494 camera->pl->total_data_in_camera=0;
495 camera->pl->data_to_read = 0;
496 camera->pl->bytes_put_away = 0;
497 camera->pl->data_reg_opened = 0;
498 camera->pl->data_cache = NULL;
499 camera->pl->init_done = 0;
500 jl2005c_init (camera, camera->port, camera->pl);
501
502 return GP_OK;
503 }
504