/* * Jenopt JD11 Camera Driver * Copyright © 1999-2001 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 <assert.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <gphoto2.h> #include <gphoto2-port.h> #include <bayer.h> #include "serial.h" #include "decomp.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 textdomain(String) (String) # define gettext(String) (String) # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) # define _(String) (String) # define N_(String) (String) #endif #if 0 static int dread(GPPort *port, caddr_t buf, int xsize) { int i; int ret = gp_port_read(port,buf,xsize); if (ret == -1) { perror("dread"); return -1; } fprintf(stderr,"dread[%d]:",ret); for (i=0;i<ret;i++) fprintf(stderr,"%02x,",buf[i]); fprintf(stderr,"\n"); return ret; } static int dwrite(GPPort*port, caddr_t buf, int xsize) { int i; int ret = gp_port_write(port,buf,xsize); if (ret < GP_OK) { perror("dwrite"); return -1; } fprintf(stderr,"dwrite[%d/%d]:",xsize,ret); for (i=0;i<xsize;i++) fprintf(stderr,"%02x,",buf[i]); fprintf(stderr,"\n"); return ret; } #endif #define READ gp_port_read #define WRITE gp_port_write static int _send_cmd(GPPort *port,unsigned short cmd) { unsigned char buf[2]; buf[0] = cmd>>8; buf[1] = cmd&0xff; return WRITE(port,buf,2); } static int _read_cmd(GPPort *port,unsigned short *xcmd) { unsigned char buf[2]; int i = 0,ret; *xcmd = 0x4242; do { if (1==(ret=READ(port,buf,1))) { if (buf[0]==0xff) { if (1==READ(port,buf+1,1)) { *xcmd = (buf[0] << 8)| buf[1]; return GP_OK; } } } else { return ret; } } while (i++<10); return GP_ERROR_IO; } #if 0 static void _dump_buf(unsigned char *buf,int size) { int i; fprintf(stderr,"["); for (i=0;i<size;i++) fprintf(stderr,"%02x ",buf[i]); fprintf(stderr,"]\n"); } #endif static int _send_cmd_2(GPPort *port,unsigned short cmd, unsigned short *xcmd) { unsigned char buf[2]; int ret, tries = 3; *xcmd = 0x4242; while (tries--) { int i = 0; buf[0] = cmd>>8; buf[1] = cmd&0xff; ret = WRITE(port,buf,2); do { if (1==(ret=READ(port,buf,1))) { if (buf[0]==0xff) { if (1==READ(port,buf+1,1)) { *xcmd = (buf[0] << 8)| buf[1]; return GP_OK; } } } else { return ret; } } while (i++<3); } return GP_ERROR_IO; } int jd11_ping(GPPort *port) { unsigned short xcmd; char buf[1]; int ret,tries = 3; while (tries--) { ret = GP_ERROR_IO; while (1==READ(port,buf,1)) /* drain input queue before PING */; ret=_send_cmd_2(port,0xff08,&xcmd); if ((ret>=GP_OK) && (xcmd==0xfff1)) return GP_OK; } return ret; } int jd11_get_rgb(GPPort *port,float *red, float *green, float *blue) { char buf[10]; int ret=GP_OK,tries=0,curread=0; _send_cmd(port,0xffa7); while ((curread<10) && (tries++<30)) { ret=READ(port,buf+curread,sizeof(buf)-curread); if (ret < 0) continue; if (ret == 0) break; curread+=ret; } if(curread<10) { fprintf(stderr,"%d returned bytes on float query.\n",ret); return GP_ERROR_IO; } /*_dump_buf(buf,10);*/ *green = buf[1]+buf[2]*0.1+buf[3]*0.01; *red = buf[4]+buf[5]*0.1+buf[6]*0.01; *blue = buf[7]+buf[8]*0.1+buf[9]*0.01; return GP_OK; } int jd11_set_rgb(GPPort *port,float red, float green, float blue) { unsigned char buf[20]; _send_cmd(port,0xffa7); buf[0] = 0xff; buf[1] = (int)green; buf[2] = ((int)(green*10))%10; buf[3] = ((int)(green*100))%10; buf[4] = (int)red; buf[5] = ((int)(red*10))%10; buf[6] = ((int)(red*100))%10; buf[7] = (int)blue; buf[8] = ((int)(blue*10))%10; buf[9] = ((int)(blue*100))%10; /*_dump_buf(buf,10);*/ return WRITE(port,buf,10); } int jd11_select_index(GPPort *port) { /* select index */ unsigned short xcmd; int ret; ret = _send_cmd_2(port,0xffa4,&xcmd); if (ret < GP_OK) return ret; if (xcmd!=0xff01) return GP_ERROR_IO; return GP_OK; } int jd11_select_image(GPPort *port,int nr) { /* select image <nr> */ unsigned short xcmd; _send_cmd(port,0xffa1); _send_cmd(port,0xff00|nr); _read_cmd(port,&xcmd); if (xcmd != 0xff01) return GP_ERROR_IO; return GP_OK; } int jd11_set_bulb_exposure(GPPort *port,int i) { unsigned short xcmd; if ((i<1) || (i>9)) return GP_ERROR_BAD_PARAMETERS; _send_cmd(port,0xffa9); _send_cmd(port,0xff00|i); _read_cmd(port,&xcmd); return GP_OK; } #if 0 static void jd11_TestADC(GPPort *port) { unsigned short xcmd; _send_cmd(port,0xff75); _read_cmd(port,&xcmd); fprintf(stderr,"TestADC: done, xcmd=%x\n",xcmd); } static void cmd_TestDRAM(GPPort *port) { unsigned short xcmd; _send_cmd(port,0xff72); _read_cmd(port,&xcmd); fprintf(stderr,"TestDRAM: done.\n"); } /* doesn't actually flash */ static void cmd_TestFLASH(GPPort *port) { unsigned short xcmd; _send_cmd(port,0xff73); _read_cmd(port,&xcmd); fprintf(stderr,"TestFLASH: xcmd = %x.\n",xcmd); } /* some kind of selftest ... shuts the shutters, beeps... only stops by * powercycle. */ static void cmd_79(GPPort *port) { unsigned short xcmd; _send_cmd(port,0xff79); _read_cmd(port,&xcmd); fprintf(stderr,"79: done, xcmd =%x\n",xcmd); } #endif static int jd11_imgsize(GPPort *port) { char buf[20]; int ret; int i=0,curread=0; _send_cmd(port,0xfff0); do { ret=READ(port,&buf[curread],10-curread); if (ret>0) curread+=ret; usleep(1000); } while ((i++<20) && (curread<10)); /*_dump_buf(buf,curread);*/ if (!curread) /* We get 0 bytes return for 0 images. */ return 0; ret=strtol(&buf[2],NULL,16); return ret; } static int getpacket(GPPort *port,unsigned char *buf, int expect) { int curread = 0, csum = 0; int tries = 0; if (expect == 200) expect++; while (tries++<5) { int i=0,ret; do { ret=READ(port,buf+curread,expect-curread); if (ret>0) { curread+=ret; i=0; continue; } usleep(100); } while ((i++<2) && (curread<expect)); if (curread!=expect) { if (!curread) return 0; _send_cmd(port,0xfff3); curread = csum = 0; continue; } /*printf("curread is %d\n",curread);*/ /*printf("PACKET:");_dump_buf(buf,curread);*/ for (i=0;i<curread-1;i++) csum+=buf[i]; if (buf[curread-1]==(csum&0xff) || (curread!=201)) return curread-1; fprintf(stderr,"BAD CHECKSUM %x vs %x, trying resend...\n",buf[curread-1],csum&0xff); _send_cmd(port,0xfff3); curread = csum = 0; /*return curread-1;*/ } fprintf(stderr,"Giving up after 5 tries.\n"); return 0; } int jd11_erase_all(GPPort *port) { return _send_cmd(port,0xffa6); } /* This function reads all thumbnails at once and initializes the whole * camera filesystem. This can be done, because finding out how much * pictures are on the camera is done by reading the whole preview picture * stream anyway. * And since the file infos are static mostly, why not just set them too at * the same time. */ int jd11_index_reader(GPPort *port, CameraFilesystem *fs, GPContext *context) { int i, id, count, xsize, curread=0, ret=0; char *indexbuf; ret = jd11_select_index(port); if (ret != GP_OK) return ret; xsize = jd11_imgsize(port); if (!xsize) { /* shortcut, no reading needed */ return GP_OK; } count = xsize/(64*48); xsize = count * (64*48); indexbuf = malloc(xsize); if (!indexbuf) return GP_ERROR_NO_MEMORY; id = gp_context_progress_start (context, xsize, _("Downloading thumbnail...")); _send_cmd(port,0xfff1); while (curread < xsize) { int readsize = xsize-curread; if (readsize>200) readsize = 200; ret=getpacket(port,indexbuf+curread,readsize); if (ret==0) break; curread+=ret; if (ret<200) break; gp_context_progress_update (context, id, curread); if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL) { /* What to do...Just free the stuff we allocated for now.*/ free(indexbuf); return GP_ERROR_CANCEL; } _send_cmd(port,0xfff1); } gp_context_progress_stop (context, id); for (i=0;i<count;i++) { CameraFile *file; char *src, fn[20]; unsigned char thumb[64*48]; int y; CameraFileInfo info; ret = gp_file_new(&file); if (ret!=GP_OK) return ret; sprintf(fn,"image%02i.pgm",i); gp_file_set_type (file, GP_FILE_TYPE_PREVIEW); gp_file_set_name(file, fn); gp_file_set_mime_type(file, GP_MIME_PGM); gp_file_append(file, THUMBHEADER, strlen(THUMBHEADER)); src = indexbuf+(i*64*48); for (y=0;y<48;y++) { int x,off = 64*y; for (x=0;x<64;x++) thumb[47*64-off+(63-x)] = src[off+x]; } ret = gp_file_append(file,thumb,sizeof(thumb)); if (ret != GP_OK) return ret; ret = gp_filesystem_append(fs, "/", fn, context); if (ret != GP_OK) return ret; ret = gp_filesystem_set_file_noop(fs, "/", file, context); if (ret != GP_OK) return ret; /* we also get the fs info for free, so just set it */ info.file.fields = GP_FILE_INFO_TYPE | GP_FILE_INFO_NAME | GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT | GP_FILE_INFO_SIZE; strcpy(info.file.type,GP_MIME_PNM); strcpy(info.file.name,fn); info.file.width = 640; info.file.height = 480; info.file.size = 640*480*3+strlen(IMGHEADER); info.preview.fields = GP_FILE_INFO_TYPE | GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT | GP_FILE_INFO_SIZE; strcpy(info.preview.type,GP_MIME_PGM); info.preview.width = 64; info.preview.height = 48; info.preview.size = 64*48+strlen(THUMBHEADER); ret = gp_filesystem_set_info_noop(fs, "/", info, context); } free(indexbuf); return GP_OK; } static int serial_image_reader(Camera *camera,CameraFile *file,int nr,unsigned char ***imagebufs,int *sizes, GPContext *context) { int picnum,curread,ret=0; GPPort *port = camera->port; unsigned int id; jd11_select_image(port,nr); *imagebufs = (unsigned char**)malloc(3*sizeof(char**)); for (picnum=0;picnum<3;picnum++) { curread=0; sizes[picnum] = jd11_imgsize(port); (*imagebufs)[picnum]=(unsigned char*)malloc(sizes[picnum]+400); _send_cmd(port,0xfff1); id = gp_context_progress_start (context, sizes[picnum], _("Downloading data...")); while (curread<sizes[picnum]) { int readsize = sizes[picnum]-curread; if (readsize > 200) readsize = 200; ret=getpacket(port,(*imagebufs)[picnum]+curread,readsize); if (ret==0) break; curread+=ret; if (ret<200) break; gp_context_progress_update (context, id, curread); if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL) { int j; /* What to do ... Just free the stuff we allocated for now. */ for (j=0;j<picnum;j++) free((*imagebufs)[picnum]); free(*imagebufs); return GP_ERROR_CANCEL; } _send_cmd(port,0xfff1); } gp_context_progress_stop (context, id); } return GP_OK; } int jd11_get_image_full( Camera *camera, CameraFile*file, int nr, int raw, GPContext *context ) { unsigned char *s,*uncomp[3],**imagebufs; int ret,sizes[3]; char *data; int h; ret = serial_image_reader(camera,file,nr,&imagebufs,sizes, context); if (ret!=GP_OK) return ret; uncomp[0] = malloc(320*480); uncomp[1] = malloc(320*480/2); uncomp[2] = malloc(320*480/2); if (sizes[0]!=115200) { picture_decomp_v1(imagebufs[0],uncomp[0],320,480); picture_decomp_v1(imagebufs[1],uncomp[1],320,480/2); picture_decomp_v1(imagebufs[2],uncomp[2],320,480/2); } else { picture_decomp_v2(imagebufs[0],uncomp[0],320,480); picture_decomp_v2(imagebufs[1],uncomp[1],320,480/2); picture_decomp_v2(imagebufs[2],uncomp[2],320,480/2); } gp_file_append(file, IMGHEADER, strlen(IMGHEADER)); data = malloc(640*480*3); if (!raw) { unsigned char *bayerpre; s = bayerpre = malloc(640*480); /* note that picture is upside down and left<->right mirrored */ for (h=480;h--;) { int w; for (w=320;w--;) { if (h&1) { /* G B G B G B G B G */ *s++ = uncomp[2][(h/2)*320+w]; *s++ = uncomp[0][h*320+w]; } else { /* R G R G R G R G R */ *s++ = uncomp[0][h*320+w]; *s++ = uncomp[1][(h/2)*320+w]; } } } gp_bayer_decode(bayerpre,640,480,data,BAYER_TILE_RGGB); free(bayerpre); } else { s=data; for (h=480;h--;) { /* upside down */ int w; for (w=640;w--;) { /* right to left */ /* and images are in green red blue */ *s++=uncomp[1][(h/2)*320+(w/2)]; *s++=uncomp[0][h*320+(w/2)]; *s++=uncomp[2][(h/2)*320+(w/2)]; } } } free(uncomp[0]);free(uncomp[1]);free(uncomp[2]); free(imagebufs[0]);free(imagebufs[1]);free(imagebufs[2]);free(imagebufs); gp_file_append(file, data, 640*480*3); free(data); return GP_OK; }