diff options
author | Marcus Meissner <marcus@jet.franken.de> | 2001-10-28 11:26:04 +0000 |
---|---|---|
committer | Marcus Meissner <marcus@jet.franken.de> | 2001-10-28 11:26:04 +0000 |
commit | 14f3866e30b878c548a9bece246068a86d37ddd7 (patch) | |
tree | cfb4136f41ef3d21be8b380935df91ba38e7c524 /camlibs/jd11 | |
parent | 7ebc0343f0b053309d5fab613d12fafe4a929b76 (diff) | |
download | libgphoto2-14f3866e30b878c548a9bece246068a86d37ddd7.tar.gz |
JD11 cam driver for gphoto2.
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@2826 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'camlibs/jd11')
-rw-r--r-- | camlibs/jd11/Makefile.am | 14 | ||||
-rw-r--r-- | camlibs/jd11/decomp.c | 149 | ||||
-rw-r--r-- | camlibs/jd11/decomp.h | 5 | ||||
-rw-r--r-- | camlibs/jd11/jd11.c | 197 | ||||
-rw-r--r-- | camlibs/jd11/jd11.html | 303 | ||||
-rw-r--r-- | camlibs/jd11/serial.c | 381 | ||||
-rw-r--r-- | camlibs/jd11/serial.h | 15 |
7 files changed, 1064 insertions, 0 deletions
diff --git a/camlibs/jd11/Makefile.am b/camlibs/jd11/Makefile.am new file mode 100644 index 000000000..a5e88bd39 --- /dev/null +++ b/camlibs/jd11/Makefile.am @@ -0,0 +1,14 @@ +EXTRA_DIST = jd11.html + +camlibdir = $(prefix)/lib/gphoto2 +camlib_LTLIBRARIES = libgphoto2_jd11.la + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/libgphoto2 + +libgphoto2_jd11_la_SOURCES = jd11.c serial.h serial.c decomp.h decomp.c + +libgphoto2_jd11_la_LDFLAGS =\ + -avoid-version\ + -L$(shell pwd)/$(top_builddir)/libgphoto2/@dotlibs@ + +libgphoto2_jd11_la_LIBADD = -lgphoto2 diff --git a/camlibs/jd11/decomp.c b/camlibs/jd11/decomp.c new file mode 100644 index 000000000..34272e6c8 --- /dev/null +++ b/camlibs/jd11/decomp.c @@ -0,0 +1,149 @@ +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> + +#include "decomp.h" + +struct chain { int left,val,right; }; +struct compstate { + unsigned char curmask; + unsigned char bytebuf; + unsigned char *byteptr; + + struct chain cl[200]; + int stackstart; +}; + +/* FLOAT_QUERY: 0.860000 1.030000 1.150000. */ + +/********************************************************/ +/* The bit stage */ +/********************************************************/ +static inline int +jd11_getbit(struct compstate *cs) { + int ret; + if (cs->curmask == 0x80) + cs->bytebuf = *cs->byteptr++; + ret = cs->curmask & cs->bytebuf; + cs->curmask >>=1; + if (!cs->curmask) cs->curmask = 0x80; + return !!ret; +} + +/********************************************************/ +/* The huffman compressor stage */ +/********************************************************/ +static int +decomp_1byte(struct compstate *cs) { + int xcs = cs->stackstart; + int xbit; + + while ((cs->cl[xcs].left>=0) && (cs->cl[xcs].right>=0)) { + xbit = jd11_getbit(cs); + if (xbit) + xcs = cs->cl[xcs].left; + else + xcs = cs->cl[xcs].right; + } + return cs->cl[xcs].val; +} + +static void +build_huffmann_tree(struct compstate *cs) { + int xstack[200]; + int i,curcl=0,curstack=0; + const int df[] = { + -180,180,1000,-90,1000,90,1000,-45,1000,45,1000,-20,1000, + 20,1000,-11,1000,11,1000,-6,1000,2,1000,6,-2,1000,1000 + }; + for (i=0;i<sizeof(df)/sizeof(df[0]);i++) { + if (df[i]!=1000) { + cs->cl[curcl].left = -1; + cs->cl[curcl].right = -1; + cs->cl[curcl].val = df[i]; + } else { + cs->cl[curcl].right = xstack[--curstack]; + cs->cl[curcl].left = xstack[--curstack]; + } + xstack[curstack++] = curcl++; + } + cs->stackstart = xstack[0]; +} + +#define F1 0.5 +#define F2 0.0 +#define F3 0.5 +#define F4 0.0 + +void +picture_decomp_v1(char *compressed,char *uncompressed,int width,int height) { + struct compstate cs; + unsigned char xbyte; + int i=0,curbyte=0,diff,lastval; + int *line,*lastline; + + cs.curmask = 0x80; cs.bytebuf = 0; cs.byteptr = compressed; + + build_huffmann_tree(&cs); + + line = (int*)malloc(sizeof(int)*width); + lastline= (int*)malloc(sizeof(int)*width); + curbyte=0; + memset(line,0,sizeof(line)); + memset(lastline,0,sizeof(line)); + for (i=0;i<width;i++) { + diff = decomp_1byte(&cs); + curbyte+=diff; + xbyte = curbyte; + if (curbyte>255) xbyte = 255; + if (curbyte<0) xbyte = 0; + + *uncompressed++=xbyte; + + line[i] = curbyte; + } + height--; + while (height--) { + lastval = line[0]; /* just before the copy */ + memcpy(lastline,line,width*sizeof(int)); + memset(line,0,width*sizeof(int)); + for (i=0;i<width;i++) { + diff = decomp_1byte(&cs); + line[i]=diff+lastval; + if (i<width-2) { + lastval = (int)((lastline[i+2]*F4+lastline[i]*F2+lastline[i+1]*F1+line[i]*F3)); + } else { + if (i==width-2) + lastval = (int)(lastline[i]*F2+lastline[i+1]*F1+line[i]*F3); + else + lastval = line[i]; + } + + xbyte = line[i]; + if (line[i]>255) xbyte = 255; + if (line[i]<0) xbyte = 0; + *uncompressed++=xbyte; + } + } +} + +/* Just blow up the picture from 6 bit uncompressed to 8 bit uncompressed */ +void +picture_decomp_v2(char *compressed,char *uncompressed,int width,int height) { + struct compstate cs; + int i,j; + unsigned char xbyte; + + cs.curmask = 0x80; cs.bytebuf = 0; cs.byteptr = compressed; + for (i=width*height;i--;) { + unsigned char xmask = 0x80; + xbyte = 0; + for (j=6;j--;) { + if (jd11_getbit(&cs)) + xbyte|=xmask; + xmask>>=1; + } + *uncompressed++=xbyte; + } +} diff --git a/camlibs/jd11/decomp.h b/camlibs/jd11/decomp.h new file mode 100644 index 000000000..30de7e597 --- /dev/null +++ b/camlibs/jd11/decomp.h @@ -0,0 +1,5 @@ +#ifndef _JD11_DECOMP_H +#define _JD11_DECOMP_H +extern void picture_decomp_v1(char *compressed,char *uncompressed,int width,int height); +extern void picture_decomp_v2(char *compressed,char *uncompressed,int width,int height); +#endif diff --git a/camlibs/jd11/jd11.c b/camlibs/jd11/jd11.c new file mode 100644 index 000000000..c6c93aa17 --- /dev/null +++ b/camlibs/jd11/jd11.c @@ -0,0 +1,197 @@ +/* + * Jenoptik JD11 Driver + * Copyright (C) 1999-2001 Marcus Meissner <marcus@jet.franken.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <gphoto2.h> +#include <gphoto2-port.h> + +#ifdef ENABLE_NLS +# include <libintl.h> +# undef _ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define _(String) (String) +# define N_(String) (String) +# endif +#else +# define _(String) (String) +# define N_(String) (String) +#endif + +#include "serial.h" + +int camera_id (CameraText *id) +{ + strcpy(id->text, "JD11"); + return (GP_OK); +} + +int camera_abilities (CameraAbilitiesList *list) +{ + CameraAbilities a; + + strcpy(a.model, "Jenoptik JD11"); + a.status = GP_DRIVER_STATUS_TESTING; + a.port = GP_PORT_SERIAL; + a.speed[0] = 115200; + a.speed[1] = 0; + a.operations = GP_OPERATION_NONE ; + a.file_operations = GP_FILE_OPERATION_PREVIEW; + a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL; + + gp_abilities_list_append(list, a); + + return (GP_OK); +} + +static int file_list_func (CameraFilesystem *fs, const char *folder, + CameraList *list, void *data) +{ + Camera *camera = data; + int count, result; + + result = jd11_file_count(camera->port, &count); + if (result != GP_OK) + return result; + + gp_list_populate(list, "image%02i.pnm", count); + + return (GP_OK); +} + +static int get_file_func (CameraFilesystem *fs, const char *folder, + const char *filename, CameraFileType type, + CameraFile *file, void *user_data) +{ + Camera *camera = user_data; + int image_no, result; + char *data; + long int size; + + image_no = gp_filesystem_number(fs, folder, filename); + + if(image_no < 0) + return image_no; + + switch (type) { + /* + case GP_FILE_TYPE_RAW: + result = jd11_get_image_raw (camera->port, image_no, &data, + (int*) &size); + break; + */ + case GP_FILE_TYPE_NORMAL: + result = jd11_get_image_full (camera->port, image_no, &data, + (int*) &size); + break; + case GP_FILE_TYPE_PREVIEW: + result = jd11_get_image_preview (camera->port, image_no, + &data, (int*) &size); + break; + default: + return (GP_ERROR_NOT_SUPPORTED); + } + + if (result < 0) + return result; + + gp_file_set_name (file, filename); + gp_file_set_mime_type (file, "image/pnm"); + gp_file_set_data_and_size (file, data, size); + + return (GP_OK); +} + +static int +delete_all_func (CameraFilesystem *fs, const char* folder, void *data) +{ + Camera *camera = data; + if (strcmp (folder, "/")) + return (GP_ERROR_DIRECTORY_NOT_FOUND); + return jd11_erase_all(camera->port); +} + +static int camera_summary (Camera *camera, CameraText *summary) +{ + strcpy(summary->text, _("No summary available.")); + return (GP_OK); +} + +static int camera_manual (Camera *camera, CameraText *manual) +{ + strcpy(manual->text, + _( + "The JD11 camera works rather well with this driver.\n" + "It gets a bit confused if some data is left over on the serial line,\n" + "and will report I/O errors on startup. Just switch the camera off and\n" + "on again and it will no longer do that.\n" + "An RS232 interface @ 115 kbit is required for image transfer.\n" + "The driver allows you to get\n\n" + " - thumbnails (64x48 PGM format)\n" + " - full images (640x480 PPM format)\n" + "No color correction and no interpolation is applied as of this time.\n" + "\n\n")); + + return (GP_OK); +} + +static int camera_about (Camera *camera, CameraText *about) +{ + strcpy (about->text, + _("JD11\n" + "Marcus Meissner <marcus@jet.franken.de>\n" + "Driver for the Jenoptik JD11 camera.\n" + "Protocol reverse engineered using WINE and IDA.")); + + return (GP_OK); +} + +int camera_init (Camera *camera) +{ + gp_port_settings settings; + int ret; + + /* First, set up all the function pointers */ + camera->functions->summary = camera_summary; + camera->functions->manual = camera_manual; + camera->functions->about = camera_about; + + /* Configure port */ + gp_port_set_timeout(camera->port, 1000); + gp_port_get_settings(camera->port, &settings); + settings.serial.speed = 115200; + settings.serial.bits = 8; + settings.serial.parity = 0; + settings.serial.stopbits= 1; + gp_port_set_settings(camera->port, settings); + + /* Set up the filesystem */ + gp_filesystem_set_list_funcs(camera->fs, file_list_func, NULL,camera); + gp_filesystem_set_file_funcs(camera->fs,get_file_func,NULL,camera); + gp_filesystem_set_folder_funcs(camera->fs,NULL,delete_all_func, NULL,NULL,camera); + /* test camera */ + ret = jd11_ping(camera->port); + return (ret); +} + diff --git a/camlibs/jd11/jd11.html b/camlibs/jd11/jd11.html new file mode 100644 index 000000000..34f3e4c67 --- /dev/null +++ b/camlibs/jd11/jd11.html @@ -0,0 +1,303 @@ +<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <meta name="GENERATOR" content="Mozilla/4.61C-CCK-MCD Caldera Systems OpenLinux [en] (X11; I; Linux 2.2.14 i586) [Netscape]"> + <title>Reverse Engineering the JD11 (JenCam 11) </title> +</head> +<body> + +<h1> +Reverse Engineering the JD11 - A drama in n chapters</h1> + +<hr> +<h2> +What is the JD11?</h2> +The JD11 is a cheap digital camera produced by JenOptik (apparenly a subsidary +of Zeiss Jena). It was at the time of writing this available for around +250 DM. +<p>There is software available, a windows program called <i>FotoBee</i> +which is a huge heap of MFC dung. It can be made to run with a small hack +under the free Windows Emulator <a href="http://winehq.com">WINE</a>. +<p>WINE was crucial in reverse engineering the camera at all stages of +the project. +<h2> +Chapter 1:The lowlevel serial protocol</h2> +The JD11 comes with a serial cable with a DB9 connector on the computerside +and a small klinkenstecker at the camera end. So just 2 lines (RX, TX) +and GND. +<p>There is no documenation on the serial protocol in the inlaying documentation. +<p>The FotoBee.exe program uses the Windows serial communications interface, +which is fortunately implemented in WINE. By snooping the setup calls, +the serial parameters are: +<pre> 115200 Baud, no parity, 1 stopbit, 8 bit data, no flow control + (neither crtscts, nor XON/XOFF)</pre> +It also setting some large timeouts, since the camera sometimes needs like +2 seconds to think about something. +<p>That was rather trivial. +<h2> +Chapter 2: The serial data transfer protocol</h2> +The next step was to find out how the FotoBee software talks to the camera. +<p> Luckily the program still uses the standard Win32 serial communication +functions for that, so I added some code to the <tt>ReadFile</tt> and +<tt>WriteFile</tt> +functions, which dumps the read/written buffer if it is writing to a serial +device. +<p>In general, the program sends a command down the serial line (with optional +arguments) and receives data back. Every command starts with a <tt>0xFF</tt> +character. + +<table BORDER COLS=4 WIDTH="100%" > +<tr><td width=10%>Command</td><td width=15%>Arguments</td><td>Returns</td><td width=55%>Description</td></tr> + +<tr> + <td>0xff 0x08</td> + <td>None</td> + <td>0xff 0xf1</td> + <td>Ping!s the camera.</td> +</tr> + +<tr> + <td>0xff 0xa7</td> + <td>None</td> + <td>A packet with between 10 and 20 bytes is returned.</td> + <td> + 3 float factors are returned, they are calculated:<br> + <code> + f1=r[1]+r[2]*0.1+r[3]*0.01;<br> + f2=r[4]+r[5]*0.1+r[6]*0.01;<br> + f3=r[7]+r[8]*0.1+r[9]*0.01;<br> + </code><br> + Their use is still unknown to me. + </td> +</tr> + +<tr> + <td>0xff 0xa4</td> + <td>None</td> + <td>0xff 0x01</td> + <td>Select Index Picture to transfer, is followed by packetreading</td> +</tr> + +<tr> + <td>0xff 0xa1</td> + <td>0xff <i>picturenr</i></td> + <td>0xff 0x01</td> + <td>Selects Image <i>picturenr</i> to transfer. 3 Bytestreams follow (packetreading has to be called 3 times)</td> +</tr> + +<tr> + <td>0xff 0xa8</td> + <td>10 bytes of data ... Set floats</td> + <td>0xff 0x01</td> + <td>Sets the 3 floats. Sends 10 bytes to the camera.<br> + The bytestream looks: + </code>0xFF A B C D E F G H I</code><br> + The floats are: A.BC D.EF G.HI<br> + A...I are binary values between 0 and 9. + </td> +</tr> +<tr> + <td>0xff 0xa6</td> + <td>None</td> + <td>No return expected.</td> + <td>Delete all images.</td> +</tr> + +<tr> + <td>0xff 0xf0</td> + <td>None</td> + <td>Returns an ASCII hexnumber (starting with two 0x66 ('f'))</td> + <td> + Returns the size (in bytes) of the image to transfer next. This + command is issued after "select index" or "select image" + and is followed by (multiple) packet reads. + </td> +</tr> + +<tr> + <td>0xff 0xf1</td> + <td>None</td> + <td>Returns 201 or less bytes</td> + <td> + The packetreader. It is called in a loop after querying the + imagesize. If 201 bytes are read, the 201th byte is the checksum + (sum of 0-199)&0xff, otherwise there is no checksum.<br> + Returns the 200 bytes read. + </td> +</tr> +<tr> + <td>0xff 0xf3</td> + <td>None</td> + <td>Returns 201 or less bytes</td> + <td> + The packet resend command. Works exactly like the + <tt>0xff 0xf1</tt> packetread command, but the last packet is + retransmitted. This is useful for corrupted transmissions. + </td> +</tr> +<tr> + <td>0xff 0x72</td> + <td>None</td> + <td>0xff 0x71</td> + <td>Function unknown.</td> +</tr> +<tr> + <td>0xff 0x73</td> + <td>None</td> + <td>0xff 0x70</td> + <td>Function unknown</td> +</tr> +<tr> + <td>0xff 0x75</td> + <td>None</td> + <td>0xff70 or 0xff71 or something else.</td> + <td>Function unknown. Returns bool?</td> +</tr> +<tr> + <td>0xff 0x78</td> + <td>None.</td> + <td>Some data.</td> + <td>Appears to take a snapshot and then it should read N packets of + data. Strange. There is some text generated and printed into + a dialog. I only get back 0xff.</td> +</tr> +<tr> + <td>0xff 0x79</td> + <td>None</td> + <td>0xff 0xXX</td> + <td>Function unknown, seems to open/close the shutter in rapid succession.</td> +</tr> +<tr> + <td>0xff 0x7a</td> + <td>None.</td> + <td>Some data.</td> + <td>Unclear. GUI enables wait cursor before call and disabled it after.</td> +</tr> +<tr> + <td>0xff 0x7b</td> + <td>0xff value.</td> + <td>0xff 0xf1</td> + <td>Unclear.</td> +</tr> +<tr> + <td>0xff 0xa9</td> + <td>0xff value.</td> + <td>0xff 0xf1</td> + <td>Set Bulb Mode. Apparently values between 1 and 9.</td> +</tr> +</table> +<p> +Following commands can be used on toplevel: +<ul> +<li>PING +<li>FLOAT FACTORS +<li>SELECT INDEX +<li>SELECT IMAGE +<li>... +</ul> +For reading the INDEX picture following sequence appears: +<ul> +<li>SELECT INDEX +<li>IMAGE SIZE +<li>READ PACKET until packets exhausted. +</ul> +For reading the IMAGE picture following sequence appears: +<ul> +<li>SELECT IMAGE <i>n</i> +<li>Do 3 times: +<ul> + <li>IMAGE SIZE + <li>READ PACKET until packets exhausted. +</ul> +</ul> +Thats all we needed to know about the serial commands. + +<h2>Chapter 3: The image compression, stage 1</h2> + +Judging from other cameras it is easy to suspect the camera uses the JPEG +format. It does not unfortunately. + +<h3>Chapter 3.1: The format of the index picture</h3> +Casting a closer look at the raw data of the index picture shows a a stream +of 8 bit values. Some experimentation later it turns out to a stream of +64x48 grayscale pictures, upside down. +Using:<pre> + rawtopgm <index 64 (sizeof_index/64) |pnmflip -tb > index.ppm +</pre> +we can convert it into a .PPM and convert it further using standard UNIX tools. +<p> + +The size of the index picture also tells us the number of pictures that are +currently stored in the camera. Just divide the size of the indeximage by 64*48. + +<h3>Chapter 3.2: The low quality compressed image format</h3> + The camera has two modes to store pictures, either in low or + high quality. This is marked by the letters "L" and "H" on the + cameras LCD. Default is the low quality format.<p> + It is still unclear to me how to detect high/low quality formats, + except with the size of the returned image. + +<p> +At first look the data returned from the camera looks like junknoise. So I had +to peek into the diassembly, which shows a slightly inefficient huffman decompressor, with following bitpatterns: +<table border> +<tr><td>Bits</td><td>Value</td></tr> +<tr><td>00</td> <td>-2</td></tr> +<tr><td>01</td> <td>6</td></tr> +<tr><td>10</td> <td>2</td></tr> +<tr><td>110</td> <td>6</td></tr> +<tr><td>1110</td> <td>11</td></tr> +<tr><td>1111 0</td> <td>-11</td></tr> +<tr><td>1111 10</td> <td>20</td></tr> +<tr><td>1111 110</td> <td>-20</td></tr> +<tr><td>1111 1110</td> <td>45</td></tr> +<tr><td>1111 1111 0</td> <td>-45</td></tr> +<tr><td>1111 1111 10</td> <td>90</td></tr> +<tr><td>1111 1111 110</td> <td>-90</td></tr> +<tr><td>1111 1111 1110</td> <td>180</td></tr> +<tr><td>1111 1111 1111 0</td> <td>-180</td></tr> +</table> +The result of this decompression run is 320*480 bytes for the first image, +and 320*240 for the second and third image. But these are not the pixel values, +these are differences! +<br> +So I had another look. Pixels are 8bit unsigned values and computed like this: +<ul> +<li>In the first row of the image: <code>pixel[i] = lastpixel+decompressed_diff;</code><br> + The first pixel is based on 0. +<li>In all other rows (starting lastvalue is pixel[0] of the previous line): +<ul> + <li><code>newpixelvalue = lastvalue + decompressed_diff;</code><br> + <li><code>lastvalue = newpixelvalue*0.5 + pixel_lastline[i+1]*0.5;</code> + <pre> + 0 X 0 + X N 0 + </pre> + The 'X' pixels influence the 'N' pixel (together with the uncompressed diff) +</ul> +</ul> +Tada. We now have 3 uncompressed images. To view each of them we can use: +<pre> + rawtopgm 320 480 <image0 | pnmflip -tb > image0.ppm + rawtopgm 320 240 <image1 | pnmflip -tb > image1.ppm + rawtopgm 320 240 <image2 | pnmflip -tb > image2.ppm +</pre> +where image0 is the first image read, and image1,image2 the next two images.<p> +Both give pretty grayscale pictures, but are not yet in color. + +<h3>Chapter 3.2: The high quality compressed image format</h3> +The high quality format uses a very different compression.<p> +It just compacts all 8bit values to 6bit by shortening out the 2 least +significant bits. +So we just restore them (AAAA AABB BBBB CCCC CCDD DDDD -> AAAAAA00, BBBBBB00, +CCCCCC00, DDDDDD00) and get the same grayscale images as with the lowquality +compression. + +<h3>Chapter 3.3: Getting colors</h3> +I am still working on that. They are not RGB. +<hr> +<h2>Download</h2> +The package is available as .tar.gz file: <a href="http://www.franken.de/users/jet/jd11-1.0.tar.gz">jd11-1.0.tar.gz</a>. +</body> +</html> diff --git a/camlibs/jd11/serial.c b/camlibs/jd11/serial.c new file mode 100644 index 000000000..7a5ee6a88 --- /dev/null +++ b/camlibs/jd11/serial.c @@ -0,0 +1,381 @@ +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> + +#include <gphoto2.h> +#include <gphoto2-port.h> + +#include "serial.h" +#include "decomp.h" + +#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 == -1) { + 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) { + int res; + unsigned char buf[2]; + buf[0] = cmd>>8; + buf[1] = cmd&0xff; + res = WRITE(port,buf,2); + if (res!=2) + return GP_ERROR_IO; + return GP_OK; +} + +static void _read_cmd(GPPort *port,unsigned short *xcmd) { + unsigned char buf[2]; + int i = 0; + *xcmd = 0x4242; + do { + if (2==READ(port,buf,2)) { + if (buf[0]==0xff) + break; + continue; + } + /*usleep(100);*/ i=10; + } while (i++<10); + *xcmd = (buf[0]<<8)|buf[1]; +} + +#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 + +int jd11_ping(GPPort *port) { + unsigned short xcmd; + char buf[1]; + + while (1==READ(port,buf,1)) + /* drain input queue before PING */; + _send_cmd(port,0xff08); + _read_cmd(port,&xcmd); + if (xcmd==0xfff1) + return GP_OK; + return GP_ERROR_IO; +} + +int +jd11_float_query(GPPort *port) { + char buf[20]; + int ret,curread=0; + float f1,f2,f3; + + _send_cmd(port,0xffa7); + while (curread<10) { + ret=READ(port,buf+curread,20-curread); + switch (ret) { + case -1: assert(0); + default: + curread+=ret; + break; + } + } + f1 = buf[1]+buf[2]*0.1+buf[3]*0.01; + f2 = buf[4]+buf[5]*0.1+buf[6]*0.01; + f3 = buf[7]+buf[8]*0.1+buf[9]*0.01; + if(curread<10) { + fprintf(stderr,"%d returned bytes on float query.\n",ret); + return GP_ERROR_IO; + } + /*fprintf(stderr,"FLOAT_QUERY: %f %f %f.\n",f1,f2,f3);*/ + return GP_OK; +} + +int +jd11_select_index(GPPort *port) { /* select index */ + unsigned short xcmd; + + _send_cmd(port,0xffa4); + _read_cmd(port,&xcmd); + 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; +} + +#if 0 +static void cmd_75(GPPort *port) { + unsigned short xcmd; + + _send_cmd(port,0xff75); + _read_cmd(port,&xcmd); + fprintf(stderr,"75: done, xcmd=%x\n",xcmd); +} +static void cmd_72(GPPort *port) { + unsigned short xcmd; + + _send_cmd(port,0xff72); + _read_cmd(port,&xcmd); + assert(xcmd==0xff01); /* this seems to be the OK value or Go Ahead */ + fprintf(stderr,"72: done.\n"); +} +static void cmd_73(GPPort *port) { + unsigned short xcmd; + + _send_cmd(port,0xff73); + _read_cmd(port,&xcmd); + fprintf(stderr,"73: 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,ret);*/ + ret=strtol(&buf[2],NULL,16); + /*fprintf(stderr,"IMGSIZE: %d (%d images)\n",ret,ret/64/48);*/ + 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) + return 0; + /*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"); + exit(1); + /* not reached */ +} + +int +jd11_erase_all(GPPort *port) { + _send_cmd(port,0xffa6); + return GP_OK; +} + +int +jd11_get_image_preview(GPPort *port,int nr, char **data, int *size) { + int xsize,packets=0,curread=0,ret=0; + char *indexbuf,*src,*dst; + int y; + char header[200]; + + if (nr < 0) return GP_ERROR_BAD_PARAMETERS; + jd11_select_index(port); + xsize = jd11_imgsize(port); + if (nr > xsize/(64*48)) { + fprintf(stderr,"ERROR: nr %d is larger than %d\n",nr,xsize/64/48); + return GP_ERROR_BAD_PARAMETERS; + } + xsize = (xsize / (64*48)) * (64*48); + indexbuf = malloc(xsize+400); + if (!indexbuf) return GP_ERROR_NO_MEMORY; + _send_cmd(port,0xfff1); + while (1) { + int readsize = xsize-curread; + if (readsize>200) readsize = 200; + ret=getpacket(port,indexbuf+curread,readsize); + if (ret==0) + break; + curread+=ret; + packets++; + if (ret<200) + break; + _send_cmd(port,0xfff1); + } + strcpy(header,"P5\n# gPhoto2 JD11 thumbnail image\n64 48 255\n"); + *size = 64*48+strlen(header); + *data = malloc(*size); + if (!*data) return GP_ERROR_NO_MEMORY; + strcpy(*data,header); + src = indexbuf+(nr*64*48); + dst = (*data)+strlen(header); + + for (y=0;y<48;y++) { + int x,off = 64*y; + for (x=0;x<64;x++) + dst[47*64-off+(63-x)] = src[off+x]; + } + free(indexbuf); + return GP_OK; +} + +int +jd11_file_count(GPPort *port,int *count) { + int xsize,packets=0,curread=0,ret=0; + char tmpbuf[202]; + + jd11_select_index(port); + xsize = jd11_imgsize(port); xsize = (xsize / (64*48)) * (64*48); + _send_cmd(port,0xfff1); + while (curread <= xsize) { + int readsize = xsize-curread; + if (readsize>200) readsize = 200; + ret=getpacket(port,tmpbuf,readsize); + if (ret==0) + break; + curread+=ret; + packets++; + if (ret<200) + break; + _send_cmd(port,0xfff1); + } + *count = curread/64/48+1; + return GP_OK; +} + +static int +serial_image_reader(GPPort *port,int nr,unsigned char ***imagebufs,int *sizes) { + int picnum,packets,curread,ret=0; + + jd11_select_image(port,nr); + *imagebufs = (unsigned char**)malloc(3*sizeof(char**)); + for (picnum=0;picnum<3;picnum++) { + packets=0; + curread=0; + sizes[picnum] = jd11_imgsize(port); + (*imagebufs)[picnum]=(unsigned char*)malloc(sizes[picnum]+400); + _send_cmd(port,0xfff1); + 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; + packets++; + if (ret<200) + break; + _send_cmd(port,0xfff1); + } + } + return GP_OK; +} + + +int +jd11_get_image_full(GPPort *port,int nr, char **data, int *size) { + unsigned char *s,*uncomp[3],**imagebufs; + int ret,sizes[3]; + char header[200]; + int h; + + ret = serial_image_reader(port,nr,&imagebufs,sizes); + 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); + } + + strcpy(header,"P6\n# gPhoto2 JD11 thumbnail image\n640 480 255\n"); + *size = 640*480*3+strlen(header); + *data = malloc(*size); + strcpy(*data,header); + s=(*data)+strlen(header); + 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); + return GP_OK; +} diff --git a/camlibs/jd11/serial.h b/camlibs/jd11/serial.h new file mode 100644 index 000000000..ce5dab6d0 --- /dev/null +++ b/camlibs/jd11/serial.h @@ -0,0 +1,15 @@ +#ifndef _JD11_SERIAL_H +#define _JD11_SERIAL_H + +#include <gphoto2.h> +#include <gphoto2-port.h> + +extern int jd11_file_count(GPPort *port, int *count); +extern int jd11_get_image_preview(GPPort *port,int nr, char **data, int *size); +extern int jd11_get_image_full(GPPort *port,int nr, char **data, int *size); +extern int jd11_erase_all(GPPort *port); +extern int jd11_ping(GPPort *port); +extern int jd11_float_query(GPPort *port); +extern int jd11_select_index(GPPort *port); +extern int jd11_select_image(GPPort *port, int nr); +#endif |