1 /****************************************************************/
2 /* library.c - Gphoto2 library for cameras with sunplus spca50x */
4 /* Copyright (C) 2002, 2003 Till Adam */
6 /* Author: Till Adam <till@adam-lilienthal.de> */
8 /* This library is free software; you can redistribute it */
9 /* and/or modify it under the terms of the GNU Library General */
10 /* Public License as published by the Free Software Foundation; */
11 /* either version 2 of the License, or (at your option) any */
14 /* This library is distributed in the hope that it will be */
15 /* useful, but WITHOUT ANY WARRANTY; without even the implied */
16 /* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU Library General Public License for */
20 /* You should have received a copy of the GNU Library General */
21 /* Public License along with this library; if not, write to the */
22 /* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */
23 /* Boston, MA 02111-1307, USA. */
24 /****************************************************************/
33 #include <gphoto2/gphoto2.h>
34 #include <gphoto2/gphoto2-port.h>
39 # define _(String) dgettext (GETTEXT_PACKAGE, String)
41 # define N_(String) gettext_noop (String)
43 # define N_(String) (String)
46 # define _(String) (String)
47 # define N_(String) (String)
51 #include "spca50x-flash.h"
52 #include "spca50x-sdram.h"
54 #define GP_MODULE "spca50x"
57 #define SPCA50X_VERSION "0.1"
58 #define SPCA50X_LAST_MOD "02/09/03 - 23:14:11"
60 /* forward declarations */
61 static int file_list_func (CameraFilesystem *fs, const char *folder,
62 CameraList *list, void *data, GPContext *context);
63 static int get_file_func (CameraFilesystem *fs, const char *folder,
64 const char *filename, CameraFileType type,
65 CameraFile *file, void *user_data,
68 static int delete_file_func (CameraFilesystem *fs, const char *folder,
69 const char *filename, void *data,
71 static int delete_all_func (CameraFilesystem *fs, const char *folder,
72 void *data, GPContext *context);
73 static int get_info_func (CameraFilesystem *fs, const char *folder,
74 const char *filename, CameraFileInfo *info,
75 void *data, GPContext *context);
76 static int cam_has_sdram (CameraPrivateLibrary *pl);
77 static int cam_has_flash (CameraPrivateLibrary *pl);
78 static int cam_has_card (CameraPrivateLibrary *pl);
80 /* define what cameras we support */
86 SPCA50xBridgeChip bridge;
87 int storage_media_mask;
91 /* firmware version 1 cams. */
92 {"Mustek:gSmart mini", 0x055f, 0xc220,
93 BRIDGE_SPCA500, SPCA50X_SDRAM },
94 {"Mustek:gSmart mini 2", 0x055f, 0xc420,
95 BRIDGE_SPCA504, SPCA50X_SDRAM },
96 {"Mustek:gSmart mini 3", 0x055f, 0xc520,
97 BRIDGE_SPCA504, SPCA50X_SDRAM },
98 {"So.:Show 301", 0x0ec7, 0x1008,
99 BRIDGE_SPCA504, SPCA50X_SDRAM },
100 {"Aiptek:Pencam", 0x04fc, 0x504a,
101 BRIDGE_SPCA504, SPCA50X_SDRAM | SPCA50X_FLASH },
102 /* same ids, different cam *sigh* */
103 {"Aiptek:Pencam without flash", 0x04fc, 0x504a,
104 BRIDGE_SPCA504, SPCA50X_SDRAM },
105 {"Medion:MD 5319", 0x04fc, 0x504a,
106 BRIDGE_SPCA504, SPCA50X_SDRAM | SPCA50X_FLASH },
107 {"nisis:Quickpix Qp3", 0x04fc, 0x504a,
108 BRIDGE_SPCA504, SPCA50X_SDRAM | SPCA50X_FLASH },
109 {"Trust:Spyc@m 500F FLASH", 0x04fc, 0x504a,
110 BRIDGE_SPCA504, SPCA50X_SDRAM | SPCA50X_FLASH },
111 /* The firmware 2 cams. Those can autodetect their storage type */
112 {"Aiptek:1.3 mega PocketCam", 0x04fc, 0x504b,
114 {"Maxell:Max Pocket", 0x04fc, 0x504b,
116 {"Aiptek:Smart Megacam", 0x04fc, 0x504b,
118 {"Benq:DC1300", 0x04a5, 0x3003,
120 /* Some other 500a cams with flash */
121 {"Trust:Familycam 300", 0x084d, 0x0003,
122 BRIDGE_SPCA500, SPCA50X_FLASH},
123 {"D-Link:DSC 350+", 0x084d, 0x0003,
124 BRIDGE_SPCA500, SPCA50X_FLASH},
125 {"Minton:S-Cam F5", 0x084d, 0x0003,
126 BRIDGE_SPCA500, SPCA50X_FLASH},
127 {"PureDigital:Ritz Disposable", 0x04fc, 0xffff,
128 BRIDGE_SPCA504B_PD, SPCA50X_FLASH},
133 camera_id (CameraText *id)
135 strcpy (id->text, "spca50x");
140 camera_abilities (CameraAbilitiesList *list)
146 ptr = models[x].model;
148 memset (&a, 0, sizeof (a));
149 strcpy (a.model, ptr);
150 a.port = GP_PORT_USB;
152 a.status = GP_DRIVER_STATUS_TESTING;
154 a.file_operations = GP_FILE_OPERATION_PREVIEW
155 | GP_FILE_OPERATION_DELETE;
157 a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL;
159 a.usb_vendor = models[x].usb_vendor;
160 a.usb_product = models[x].usb_product;
162 if (models[x].bridge == BRIDGE_SPCA504) {
163 /* FIXME which cams can do it? */
164 if (a.usb_product == 0xc420
165 || a.usb_product == 0xc520)
166 a.operations = GP_OPERATION_CAPTURE_IMAGE;
168 if (models[x].bridge == BRIDGE_SPCA504B_PD) {
169 a.operations = GP_OPERATION_CAPTURE_IMAGE;
171 if (models[x].bridge == BRIDGE_SPCA500) {
172 /* TEST enable capture for the DSC-350 style cams */
173 if (a.usb_vendor == 0x084d) {
174 a.operations = GP_OPERATION_CAPTURE_IMAGE;
177 gp_abilities_list_append (list, a);
179 ptr = models[++x].model;
186 camera_capture (Camera *camera, CameraCaptureType type,
187 CameraFilePath * path, GPContext *context)
189 struct SPCA50xFile *file;
192 /* Not all our cameras support capture */
193 gp_camera_get_abilities (camera, &a);
194 if (!a.operations & GP_OPERATION_CAPTURE_IMAGE)
195 return GP_ERROR_NOT_SUPPORTED;
197 if (cam_has_flash(camera->pl))
202 CHECK(spca500_flash_capture (camera->pl));
203 CHECK(spca50x_flash_get_TOC (camera->pl, &fc));
204 /* assume new pic is the last one in the cam...*/
205 CHECK(spca50x_flash_get_file_name (camera->pl, (fc - 1), tmp));
207 /* Add new image name to file list */
208 /* NOTE: these lines moved from below */
209 strncpy (path->name, tmp, sizeof (path->name) - 1);
210 path->name[sizeof (path->name) - 1] = '\0';
214 CHECK (spca50x_capture (camera->pl));
215 CHECK (spca50x_sdram_get_info (camera->pl));
216 CHECK (spca50x_sdram_get_file_info
217 (camera->pl, camera->pl->num_files_on_sdram - 1, &file));
219 /* Add new image name to file list */
220 /* NOTE: these lines moved from below */
221 strncpy (path->name, file->name, sizeof (path->name) - 1);
222 path->name[sizeof (path->name) - 1] = '\0';
224 /* Now tell the frontend where to look for the image */
225 strncpy (path->folder, "/", sizeof (path->folder) - 1);
226 path->folder[sizeof (path->folder) - 1] = '\0';
228 CHECK (gp_filesystem_append
229 (camera->fs, path->folder, path->name, context));
235 camera_exit (Camera *camera, GPContext *context)
238 if (cam_has_flash (camera->pl) || cam_has_card (camera->pl))
239 spca50x_flash_close (camera->pl, context);
241 if (camera->pl->fats) {
242 free (camera->pl->fats);
243 camera->pl->fats = NULL;
245 if (camera->pl->files) {
246 free (camera->pl->files);
247 camera->pl->files = NULL;
249 if (camera->pl->flash_toc) {
250 free (camera->pl->flash_toc);
251 camera->pl->flash_toc = NULL;
261 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
264 int flash_file_count;
266 if (cam_has_flash(camera->pl) || cam_has_card (camera->pl)) {
267 spca50x_flash_get_filecount(camera->pl, &flash_file_count);
268 snprintf (tmp, sizeof (tmp),
269 _("FLASH:\n Files: %d\n"), flash_file_count);
270 strcat (summary->text, tmp);
273 /* possibly get # pics, mem free, etc. if needed */
274 if (cam_has_sdram(camera->pl) && camera->pl->dirty_sdram) {
275 CHECK (spca50x_sdram_get_info (camera->pl));
277 snprintf (tmp, sizeof (tmp),
278 _("SDRAM:\n Files: %d\n Images: %4d\n Movies: %4d\nSpace used: %8d\nSpace free: %8d\n"),
279 camera->pl->num_files_on_sdram,
280 camera->pl->num_images,
281 camera->pl->num_movies,
282 camera->pl->size_used,
283 camera->pl->size_free);
284 strcat (summary->text, tmp);
291 camera_about (Camera *camera, CameraText *about, GPContext *context)
294 _("spca50x library v" SPCA50X_VERSION
295 " " SPCA50X_LAST_MOD "\n"
296 "Till Adam <till@adam-lilienthal.de>\n"
297 "Support for digital cameras with a sunplus spca50x chip "
298 "based on several other gphoto2 camlib modules and "
299 "the information kindly provided by Mustek.\n"
309 file_list_func (CameraFilesystem *fs, const char *folder,
310 CameraList *list, void *data, GPContext *context)
313 Camera *camera = data;
314 int i = 0, filecount = 0;
317 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) )
319 CHECK (spca50x_flash_get_TOC(camera->pl, &filecount));
320 for (i=0; i<filecount; i++)
322 CHECK(spca50x_flash_get_file_name (camera->pl, i,
324 gp_list_append (list, temp_file, NULL);
327 if (cam_has_sdram(camera->pl)) {
328 if (camera->pl->dirty_sdram)
329 CHECK (spca50x_sdram_get_info (camera->pl));
331 for (i = 0; i < camera->pl->num_files_on_sdram; i++) {
332 strncpy (temp_file, camera->pl->files[i].name, 12);
334 gp_list_append (list, temp_file, NULL);
343 get_file_func (CameraFilesystem *fs, const char *folder,
344 const char *filename, CameraFileType type,
345 CameraFile *file, void *user_data, GPContext *context)
348 Camera *camera = user_data;
349 unsigned char *data = NULL;
350 int size, number, filetype, flash_file_count = 0;
353 gp_filesystem_number (camera->fs, folder, filename, context));
355 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) ) {
356 CHECK (spca50x_flash_get_filecount
357 (camera->pl, &flash_file_count));
361 case GP_FILE_TYPE_NORMAL:
362 if ( number < flash_file_count) {
363 CHECK (spca50x_flash_get_file (camera->pl,
364 context, &data, &size,
366 CHECK (gp_file_set_mime_type
367 (file, GP_MIME_JPEG));
370 CHECK (spca50x_sdram_request_file
371 (camera->pl, &data, &size,
372 number-flash_file_count,
374 if (filetype == SPCA50X_FILE_TYPE_IMAGE) {
375 CHECK (gp_file_set_mime_type
376 (file, GP_MIME_JPEG));
377 } else if (filetype == SPCA50X_FILE_TYPE_AVI) {
378 CHECK (gp_file_set_mime_type
379 (file, GP_MIME_AVI));
383 case GP_FILE_TYPE_PREVIEW:
384 if ( number < flash_file_count) {
385 CHECK (spca50x_flash_get_file (camera->pl,
386 context, &data, &size,
388 CHECK (gp_file_set_mime_type (file,
392 CHECK (spca50x_sdram_request_thumbnail
393 (camera->pl, &data, &size,
394 number-flash_file_count,
396 if (filetype == SPCA50X_FILE_TYPE_IMAGE) {
397 CHECK (gp_file_set_mime_type
398 (file, GP_MIME_BMP));
399 } else if (filetype == SPCA50X_FILE_TYPE_AVI) {
400 CHECK (gp_file_set_mime_type
401 (file, GP_MIME_JPEG));
407 return GP_ERROR_NOT_SUPPORTED;
413 CHECK (gp_file_set_data_and_size (file, data, size));
414 CHECK (gp_file_set_name (file, filename));
420 get_info_func (CameraFilesystem *fs, const char *folder,
421 const char *filename, CameraFileInfo *info, void *data,
424 Camera *camera = data;
425 int n, flash_file_count = 0;
426 struct SPCA50xFile *file;
430 /* Get the file number from the CameraFileSystem */
432 gp_filesystem_number (camera->fs, folder, filename, context));
434 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) ) {
435 CHECK (spca50x_flash_get_TOC(camera->pl,
438 if (n < flash_file_count) {
439 CHECK (spca50x_flash_get_file_name(camera->pl,
441 strncpy (info->file.name, name,
442 sizeof (info->file.name));
444 CHECK (spca50x_flash_get_file_dimensions(
445 camera->pl, n, &w, &h));
446 strcpy (info->file.type, GP_MIME_JPEG);
447 info->file.width = w;
448 info->file.height = h;
449 info->preview.width = w/8;
450 info->preview.height = h/8;
452 if (cam_has_sdram (camera->pl) && n >= flash_file_count ){
453 CHECK (spca50x_sdram_get_file_info (camera->pl,
454 n-flash_file_count, &file));
455 strncpy (info->file.name, filename, sizeof (info->file.name));
456 if (file->mime_type == SPCA50X_FILE_TYPE_IMAGE) {
457 strcpy (info->file.type, GP_MIME_JPEG);
458 info->preview.width = 160;
459 info->preview.height = 120;
460 } else if (file->mime_type == SPCA50X_FILE_TYPE_AVI) {
461 strcpy (info->file.type, GP_MIME_AVI);
462 info->preview.width = 320;
463 info->preview.height = 240;
465 info->file.width = file->width;
466 info->file.height = file->height;
470 GP_FILE_INFO_NAME | GP_FILE_INFO_TYPE
471 | GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT;
473 info->file.mtime = 0;
474 info->file.fields |= GP_FILE_INFO_MTIME;
476 info->preview.fields =
477 GP_FILE_INFO_TYPE | GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT;
478 strcpy (info->preview.type, GP_MIME_BMP);
483 delete_file_func (CameraFilesystem *fs, const char *folder,
484 const char *filename, void *data, GPContext *context)
486 Camera *camera = data;
487 int n, c, flash_file_count;
489 /* FIXME deleting a single file for flash/card cams should work */
490 /* Get the file number from the CameraFileSystem */
492 gp_filesystem_number (camera->fs, folder, filename, context));
494 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) ) {
495 CHECK (spca50x_flash_get_filecount
496 (camera->pl, &flash_file_count));
498 /* should not happen really */
501 if (n < flash_file_count) {
502 return spca500_flash_delete_file (camera->pl, n);
505 CHECK (c = gp_filesystem_count (camera->fs, folder, context));
509 gp_filesystem_name (fs, "/", c - 1, &name, context);
510 gp_context_error (context,
511 _("Your camera only supports deleting the "
512 "last file on the camera. In this case, this "
513 "is file '%s'."), name);
516 CHECK (spca50x_sdram_delete_file (camera->pl, n));
521 delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
524 Camera *camera = data;
526 if (cam_has_sdram (camera->pl))
527 CHECK (spca50x_sdram_delete_all (camera->pl));
528 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) )
529 CHECK (spca50x_flash_delete_all (camera->pl, context));
534 static CameraFilesystemFuncs fsfuncs = {
535 .file_list_func = file_list_func,
536 .get_file_func = get_file_func,
537 .get_info_func = get_info_func,
538 .del_file_func = delete_file_func,
539 .delete_all_func = delete_all_func,
543 camera_init (Camera *camera, GPContext *context)
549 GPPortSettings settings;
550 CameraAbilities abilities;
552 /* First, set up all the function pointers */
553 camera->functions->exit = camera_exit;
554 camera->functions->summary = camera_summary;
555 camera->functions->about = camera_about;
556 camera->functions->capture = camera_capture;
558 CHECK (gp_port_get_settings (camera->port, &settings));
559 switch (camera->port->type) {
561 settings.usb.inep = 0x82;
562 settings.usb.outep = 0x03;
563 settings.usb.config = 1;
564 settings.usb.interface = 0;
565 settings.usb.altsetting = 0;
567 CHECK (gp_port_set_settings (camera->port, settings));
568 CHECK (gp_port_set_timeout (camera->port, TIMEOUT));
572 gp_context_error (context,
573 _("Unsupported port type: %d. "
574 "This driver only works with USB "
575 "cameras.\n"), camera->port->type);
580 camera->pl = malloc (sizeof (CameraPrivateLibrary));
582 return (GP_ERROR_NO_MEMORY);
583 memset (camera->pl, 0, sizeof (CameraPrivateLibrary));
584 camera->pl->gpdev = camera->port;
585 camera->pl->dirty_sdram = 1;
586 camera->pl->dirty_flash = 1;
588 /* What bridge chip is inside the camera? The gsmart mini is spca500
589 * based, while the others have a spca50xa */
590 gp_camera_get_abilities (camera, &abilities);
591 model = models[x].model;
593 if (abilities.usb_vendor == models[x].usb_vendor
594 && abilities.usb_product == models[x].usb_product) {
596 char *m = strdup( models[x].model );
597 char *p = strchr (m, ':' );
600 same = !strcmp (m, abilities.model);
603 camera->pl->bridge = models[x].bridge;
604 camera->pl->storage_media_mask =
605 models[x].storage_media_mask;
609 model = models[++x].model;
612 CHECK (spca50x_get_firmware_revision (camera->pl));
613 if (camera->pl->fw_rev > 1) {
614 CHECK (spca50x_detect_storage_type (camera->pl));
617 if (cam_has_flash(camera->pl) || cam_has_card(camera->pl) ) {
618 if ((camera->pl->bridge == BRIDGE_SPCA504) ||
619 (camera->pl->bridge == BRIDGE_SPCA504B_PD))
620 CHECK (spca50x_flash_init (camera->pl, context));
623 if ((camera->pl->bridge == BRIDGE_SPCA504) ||
624 (camera->pl->bridge == BRIDGE_SPCA504B_PD)) {
625 /* if (abilities.usb_vendor != 0x04fc && abilities.usb_product != 0x504a ) */
626 if (!(abilities.usb_vendor == 0x04fc && abilities.usb_product == 0x504a ))
627 ret = spca50x_reset (camera->pl);
631 gp_context_error (context, _("Could not reset camera.\n"));
638 /* Set up the CameraFilesystem */
639 return gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
643 cam_has_sdram (CameraPrivateLibrary *pl)
645 return pl->storage_media_mask & SPCA50X_SDRAM;
649 cam_has_flash (CameraPrivateLibrary *pl)
651 return pl->storage_media_mask & SPCA50X_FLASH;
655 cam_has_card (CameraPrivateLibrary *pl)
657 return pl->storage_media_mask & SPCA50X_CARD;