/* blink2.c * * Copyright © 2003 Marcus Meissner <marcus@jet.franken.de> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_LIBJPEG # include <jpeglib.h> #endif #include <gphoto2/gphoto2-library.h> #include <gphoto2/gphoto2-port-log.h> #include <gphoto2/gphoto2-result.h> #ifdef ENABLE_NLS # include <libintl.h> # undef _ # define _(String) dgettext (GETTEXT_PACKAGE, String) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define _(String) (String) # define N_(String) (String) #endif #define BLINK2_GET_NUMPICS 0x08 #define BLINK2_GET_MEMORY 0x0a #define BLINK2_GET_DIR 0x0d #define BLINK2_INIT_CAPTURE 0x0e #define BLINK2_DELETE_ALL 0x12 #define BLINK2_CHECK_CAPTURE_FINISH 0x16 #define BLINK2_SET_EXPOSURE_COUNT 0x17 #define BLINK2_GET_FIRMWARE_ID 0x18 static int blink2_getnumpics( GPPort *port, GPContext *context,int *numpics ) { char buf[6]; int ret; ret = gp_port_usb_msg_read(port, BLINK2_GET_NUMPICS, 0x03, 0, buf, 2); if (ret<GP_OK) return ret; *numpics = (buf[0]<<8) | buf[1]; return GP_OK; } #ifdef HAVE_LIBJPEG /* for the jpeg decompressor source manager. */ static void _jpeg_init_source(j_decompress_ptr cinfo) { } static boolean _jpeg_fill_input_buffer(j_decompress_ptr cinfo) { fprintf(stderr,"(), should not get here.\n"); return FALSE; } static void _jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes) { fprintf(stderr,"(%ld), should not get here.\n",num_bytes); } static boolean _jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired) { fprintf(stderr,"(desired=%d), should not get here.\n",desired); return FALSE; } static void _jpeg_term_source(j_decompress_ptr cinfo) { } #endif static int file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, void *data, GPContext *context) { Camera *camera = data; int i, ret, numpics, bytes; unsigned char *xbuf, buf[8]; ret = blink2_getnumpics( camera->port, context, &numpics ); if (ret < GP_OK) return ret; bytes = ((8*(1+numpics))+0x3f) & ~0x3f; xbuf = malloc(bytes); ret = gp_port_usb_msg_read( camera->port,BLINK2_GET_DIR,0x03,0,buf,1 ); if (ret < GP_OK) { free(xbuf); return ret; } ret = gp_port_read( camera->port, xbuf, bytes); if (ret < GP_OK) { free(xbuf); return ret; } for ( i=0; i < numpics; i++) { char name[20]; if (xbuf[8*(i+1)]) sprintf( name, "image%04d.avi", i); else sprintf( name, "image%04d.pnm", i); gp_list_append( list, name, NULL); } free(xbuf); return (GP_OK); } static int get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, CameraFileType type, CameraFile *file, void *data, GPContext *context) { Camera *camera = data; int image_no, result; int i, ret, numpics, bytes; unsigned char *xbuf, buf[8]; struct xaddr { unsigned int type, start,len; } *addrs; ret = blink2_getnumpics( camera->port, context, &numpics ); if (ret < GP_OK) return ret; gp_log(GP_LOG_DEBUG, "blink2","numpics is %d", numpics); bytes = ((8*(1+numpics))+0x3f) & ~0x3f; xbuf = malloc(bytes); if (!xbuf) return GP_ERROR_NO_MEMORY; addrs = (struct xaddr*)malloc(sizeof(struct xaddr)*numpics); if (!addrs) { free(xbuf); return GP_ERROR_NO_MEMORY; } ret = gp_port_usb_msg_read( camera->port,BLINK2_GET_DIR,0x03,0,buf,1 ); if (ret < GP_OK) { free(addrs); free(xbuf); return ret; } ret = gp_port_read (camera->port, xbuf, bytes); if (ret < GP_OK) { free(addrs); free(xbuf); return ret; } for ( i=0; i < numpics; i++) { int end, start; start = (xbuf[8*i+ 5] << 16) | (xbuf[8*i+ 6] << 8) | (xbuf[8*i+ 7]); end = (xbuf[8*i+13] << 16) | (xbuf[8*i+14] << 8) | (xbuf[8*i+15]); addrs[i].start = start; addrs[i].len = (end-start)/4; addrs[i].type = xbuf[8*(i+1)]; gp_log(GP_LOG_DEBUG, "blink2","%d - %d", start, (end-start)/4); } free(xbuf); image_no = gp_filesystem_number(fs, folder, filename, context); if(image_no < 0) { free(addrs); return image_no; } gp_file_set_name (file, filename); switch (type) { case GP_FILE_TYPE_NORMAL: #ifdef HAVE_LIBJPEG { char *convline,*convline2,*rawline; unsigned char *jpegdata; unsigned int start, len, i, pitch; int curread, ret; struct jpeg_decompress_struct dinfo; struct jpeg_source_mgr xjsm; struct jpeg_error_mgr jerr; memset( buf, 0, sizeof(buf)); if (addrs[image_no].type) gp_file_set_mime_type (file, GP_MIME_AVI); else gp_file_set_mime_type (file, GP_MIME_JPEG); start = addrs[image_no].start; len = addrs[image_no].len; jpegdata = (unsigned char*)malloc (len*8); if (!jpegdata) { result = GP_ERROR_NO_MEMORY; break; } buf[0] = (start >> 24) & 0xff; buf[1] = (start >> 16) & 0xff; buf[2] = (start >> 8) & 0xff; buf[3] = start & 0xff; buf[4] = (len >> 24) & 0xff; buf[5] = (len >> 16) & 0xff; buf[6] = (len >> 8) & 0xff; buf[7] = len & 0xff; result = gp_port_usb_msg_write (camera->port,BLINK2_GET_MEMORY,0x03,0,buf,8); if (result < GP_OK) break; len *= 8; curread = 0; do { int res; res = gp_port_read (camera->port, jpegdata+curread, len ); if (res < GP_OK) { result = GP_OK; break; } curread += res; } while (curread<=len); memset( &dinfo, 0, sizeof(dinfo)); xjsm.next_input_byte = jpegdata; xjsm.bytes_in_buffer = len; xjsm.init_source = _jpeg_init_source; xjsm.fill_input_buffer = _jpeg_fill_input_buffer; xjsm.skip_input_data = _jpeg_skip_input_data; xjsm.resync_to_restart = _jpeg_resync_to_restart; xjsm.term_source = _jpeg_term_source; dinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&dinfo); dinfo.src = &xjsm; ret = jpeg_read_header(&dinfo,TRUE); if (ret != JPEG_HEADER_OK) break; dinfo.out_color_space = JCS_RGB; jpeg_start_decompress(&dinfo); pitch = (dinfo.output_width*dinfo.output_components+3)&~3; rawline = malloc(pitch); convline = malloc(dinfo.output_width*2*3); convline2 = malloc(dinfo.output_width*2*2*3); if (ret != JPEG_HEADER_OK) return GP_ERROR; { char foo[30]; sprintf(foo,"P6\n%d %d 255\n",dinfo.output_width, dinfo.output_height*2); gp_file_append (file, foo, strlen(foo)); } for (i = 0; i < dinfo.output_height ; i++ ) { int j; JSAMPROW row[1]; JSAMPARRAY arr = row; row[0] = (JSAMPROW)rawline; jpeg_read_scanlines(&dinfo,arr,1); memcpy(convline+((dinfo.output_width/16-1)*16+8)*3, rawline+((dinfo.output_width/16-1)*16+8)*3, 8*3); memcpy(convline+pitch/2, rawline, 8*3); for (j=0;j<(dinfo.output_width/16-1);j++) { if ((j&1) == 0) memcpy(convline+((j/2)*16)*3,rawline+(j*16+8)*3,16*3); else memcpy(convline+pitch/2+((j/2)*16+8)*3,rawline+(j*16+8)*3,16*3); } for (j=0;j<dinfo.output_width*2;j++) { memcpy(convline2+ j*2*3 , convline+j*3,3); memcpy(convline2+(j*2+1)*3, convline+j*3,3); } gp_file_append (file, convline2, pitch*2); } free(convline2); free(convline); free(rawline); free(jpegdata); gp_file_set_mime_type (file, GP_MIME_PPM); jpeg_destroy_decompress(&dinfo); break; } #else /* fall through to raw mode if no libjpeg */ #endif case GP_FILE_TYPE_RAW: { char buf2[4096]; unsigned int start, len; int curread; memset( buf, 0, sizeof(buf)); if (addrs[image_no].type) gp_file_set_mime_type (file, GP_MIME_AVI); else gp_file_set_mime_type (file, GP_MIME_JPEG); start = addrs[image_no].start; len = addrs[image_no].len; buf[0] = (start >> 24) & 0xff; buf[1] = (start >> 16) & 0xff; buf[2] = (start >> 8) & 0xff; buf[3] = start & 0xff; buf[4] = (len >> 24) & 0xff; buf[5] = (len >> 16) & 0xff; buf[6] = (len >> 8) & 0xff; buf[7] = len & 0xff; result = gp_port_usb_msg_write(camera->port,BLINK2_GET_MEMORY,0x03,0,buf,8); if (result < GP_OK) break; len = len*8; do { curread = 4096; if (curread > len) curread = len; curread = gp_port_read( camera->port, buf2, curread); if (curread < GP_OK) { result = GP_OK; break; } len -= curread; result = gp_file_append( file, buf2, curread); if (result < GP_OK) break; } while (len>0); break; } default: result = GP_ERROR_NOT_SUPPORTED; break; } free(addrs); if (result < 0) return result; return (GP_OK); } static int delete_all_func (CameraFilesystem *fs, const char *folder, void *data, GPContext *context) { Camera *camera = data; int ret; char buf[1]; ret = gp_port_usb_msg_read( camera->port, BLINK2_DELETE_ALL, 0x03, 0, buf, 1); if (ret < GP_OK) return ret; return (GP_OK); } static int camera_capture (Camera *camera, CameraCaptureType type, CameraFilePath *path, GPContext *context) { int oldnumpics, numpics, ret; char buf[1]; ret = blink2_getnumpics (camera->port, context, &oldnumpics); if (ret < GP_OK) return ret; ret = gp_port_usb_msg_read( camera->port, BLINK2_INIT_CAPTURE, 0x03, 0, buf, 1); if (ret < GP_OK) return ret; do { ret = gp_port_usb_msg_read( camera->port, BLINK2_CHECK_CAPTURE_FINISH, 0x03, 0, buf, 1); if (ret < GP_OK) return ret; sleep(1); } while (buf[1] == 0x00); ret = blink2_getnumpics (camera->port, context, &numpics); if (ret < GP_OK) return ret; if (numpics == oldnumpics) return (GP_ERROR); strcpy (path->folder,"/"); sprintf (path->name,"image%04d.pnm",numpics-1); return (GP_OK); } int camera_abilities (CameraAbilitiesList *list) { CameraAbilities a; memset(&a, 0, sizeof(a)); strcpy(a.model, "SiPix:Blink 2"); a.status = GP_DRIVER_STATUS_EXPERIMENTAL; a.port = GP_PORT_USB; a.speed[0] = 0; a.usb_vendor = 0x0c77; a.usb_product = 0x1011; a.operations = GP_OPERATION_CAPTURE_IMAGE; a.file_operations = GP_FILE_OPERATION_NONE; a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL; gp_abilities_list_append(list, a); a.usb_product = 0x1010; strcpy(a.model, "SiPix:Snap"); gp_abilities_list_append(list, a); a.usb_product = 0x1015; strcpy(a.model, "SiPix:CAMeleon"); gp_abilities_list_append(list, a); return (GP_OK); } int camera_id (CameraText *id) { strcpy(id->text, "SiPix Blink2"); return (GP_OK); } static CameraFilesystemFuncs fsfuncs = { .file_list_func = file_list_func, .get_file_func = get_file_func, .delete_all_func = delete_all_func }; int camera_init (Camera *camera, GPContext *context) { char buf[6]; int ret; GPPortSettings settings; camera->functions->capture = camera_capture; gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera); gp_port_get_settings( camera->port, &settings); settings.usb.interface = 0; settings.usb.altsetting = 0; ret = gp_port_set_settings (camera->port, settings); if (ret < GP_OK) return ret; ret = gp_port_usb_msg_read( camera->port, BLINK2_GET_FIRMWARE_ID, 0x03, 0, buf, 6); if (ret < GP_OK) return ret; ret = gp_port_usb_msg_read( camera->port, 0x04, 0x03, 0, buf, 1); if (ret < GP_OK) return ret; return GP_OK; }