1 /*
2 * STV0680 Vision Camera Chipset Driver
3 * Copyright 2000 Adam Harrison <adam@antispin.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <gphoto2/gphoto2.h>
28 #include <gphoto2/gphoto2-port.h>
29
30 #include "stv0680.h"
31 #include "library.h"
32 #include "sharpen.h"
33 #include "bayer.h"
34 #include "saturate.h"
35 #include "../../libgphoto2/bayer.h"
36 #include "demosaic_sharpen.h"
37
38 #ifdef ENABLE_NLS
39 # include <libintl.h>
40 # undef _
41 # define _(String) dgettext (GETTEXT_PACKAGE, String)
42 # ifdef gettext_noop
43 # define N_(String) gettext_noop (String)
44 # else
45 # define N_(String) (String)
46 # endif
47 #else
48 # define _(String) (String)
49 # define N_(String) (String)
50 #endif
51
52 #define CMD_RETRIES 0x03
53
54 #define STV0680_QCIF_WIDTH 178
55 #define STV0680_QCIF_HEIGHT 146
56 #define STV0680_CIF_WIDTH 356
57 #define STV0680_CIF_HEIGHT 292
58 #define STV0680_QVGA_WIDTH 324
59 #define STV0680_QVGA_HEIGHT 244
60 #define STV0680_VGA_WIDTH 644
61 #define STV0680_VGA_HEIGHT 484
62
63 static unsigned char
stv0680_checksum(const unsigned char * data,int start,int end)64 stv0680_checksum(const unsigned char *data, int start, int end)
65 {
66 unsigned char sum = 0;
67 int i;
68
69 for(i = start; i <= end; ++i) {
70 sum += data[i];
71 sum &= 0xff;
72 }
73 return sum;
74 }
75
stv0680_cmd(GPPort * port,unsigned char cmd,unsigned char data1,unsigned char data2,unsigned char data3,unsigned char * response,unsigned char response_len)76 static int stv0680_cmd(GPPort *port, unsigned char cmd,
77 unsigned char data1, unsigned char data2, unsigned char data3,
78 unsigned char *response, unsigned char response_len)
79 {
80 unsigned char packet[] = { 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x03 };
81 unsigned char rhdr[6];
82 int ret;
83
84 /* build command packet */
85 packet[1] = cmd;
86 packet[2] = response_len;
87 packet[3] = data1;
88 packet[4] = data2;
89 packet[5] = data3;
90 packet[6] = stv0680_checksum(packet, 1, 5);
91
92 /* write to port */
93 printf("Writing packet to port\n");
94 if((ret = gp_port_write(port, (char *)packet, 8)) < GP_OK)
95 return ret;
96
97 printf("Reading response header\n");
98 /* read response header */
99 if((ret = gp_port_read(port, (char *)rhdr, 6)) != 6)
100 return ret;
101
102 printf("Read response\n");
103 /* read response */
104 if((ret = gp_port_read(port, (char *)response, response_len)) != response_len)
105 return ret;
106
107 printf("Validating packet [0x%X,0x%X,0x%X,0x%X,0x%X,0x%X]\n",
108 rhdr[0], rhdr[1], rhdr[2], rhdr[3], rhdr[4], rhdr[5]);
109 /* validate response */
110 if(rhdr[0] != 0x02 || rhdr[1] != cmd ||
111 rhdr[2] != response_len ||
112 rhdr[3] != stv0680_checksum(response, 0, response_len - 1) ||
113 rhdr[4] != stv0680_checksum(rhdr, 1, 3) ||
114 rhdr[5] != 0x03)
115 return GP_ERROR_BAD_PARAMETERS;
116
117 printf("Packet OK\n");
118 return GP_OK;
119 }
120
stv0680_try_cmd(GPPort * port,unsigned char cmd,unsigned short data,unsigned char * response,unsigned char response_len)121 int stv0680_try_cmd(GPPort *port, unsigned char cmd,
122 unsigned short data,
123 unsigned char *response, unsigned char response_len
124 ) {
125 int ret;
126 switch (port->type) {
127 case GP_PORT_USB:
128 /* Most significant bit set, data flows from camera->host */
129 if (cmd & 0x80)
130 ret=gp_port_usb_msg_read(port,cmd,data,0,(char *)response,response_len);
131 else
132 ret=gp_port_usb_msg_write(port,cmd,data,0,(char *)response,response_len);
133
134 if (ret == response_len)
135 return GP_OK;
136 return ret;
137 break;
138 case GP_PORT_SERIAL: {
139 int retries = CMD_RETRIES;
140 while(retries--) {
141 /* data3 was always 0 */
142 switch(ret=stv0680_cmd(port,cmd,(data>>8)&0xff,data&0xff,0,response,response_len)) {
143 case GP_ERROR_TIMEOUT:
144 case GP_ERROR_BAD_PARAMETERS:
145 break;
146 default:
147 return ret;
148 }
149 }
150 break;
151 }
152 default:
153 return GP_ERROR_NOT_SUPPORTED;
154 }
155 return GP_ERROR_IO;
156 }
157
158 static int
stv0680_last_error(GPPort * port,struct stv680_error_info * errinf)159 stv0680_last_error(GPPort *port, struct stv680_error_info *errinf) {
160 int ret;
161
162 ret = stv0680_try_cmd(port, CMDID_CLEAR_COMMS_ERROR, 0,
163 (void*)errinf, sizeof(*errinf));
164 if (ret != GP_OK)
165 return ret;
166 return GP_OK;
167 }
168
stv0680_ping(GPPort * port)169 int stv0680_ping(GPPort *port)
170 {
171 unsigned char pong[2];
172 int ret;
173
174 ret=stv0680_try_cmd(port, CMDID_PING, 0x55AA, pong, sizeof(pong));
175 if (ret != GP_OK)
176 return ret;
177 if ((pong[0]!=0x55) || (pong[1]!=0xAA)) {
178 printf("CMDID_PING successful, but returned bad values?\n");
179 return GP_ERROR_IO;
180 }
181 return GP_OK;
182 }
183
stv0680_file_count(GPPort * port,int * count)184 int stv0680_file_count(GPPort *port, int *count)
185 {
186 struct stv680_image_info imginfo;
187 int ret;
188
189 ret = stv0680_try_cmd(port,CMDID_GET_IMAGE_INFO,0,
190 (void*)&imginfo,sizeof(imginfo));
191 if (ret != GP_OK)
192 return ret;
193 *count = (imginfo.index[0]<<8)|imginfo.index[1];
194 return GP_OK;
195 }
196
197 /**
198 * Get image, with just bayer applied.
199 */
stv0680_get_image_raw(GPPort * port,int image_no,CameraFile * file)200 int stv0680_get_image_raw(GPPort *port, int image_no, CameraFile *file)
201 {
202 struct stv680_image_header imghdr;
203 char header[80];
204 unsigned char *raw, *data;
205 int h,w,ret,size;
206
207 ret = stv0680_try_cmd(port, CMDID_UPLOAD_IMAGE, image_no,
208 (void*)&imghdr, sizeof(imghdr));
209 if(ret != GP_OK)
210 return ret;
211
212 w = (imghdr.width[0] << 8) | imghdr.width[1];
213 h = (imghdr.height[0] << 8) | imghdr.height[1];
214 size = (imghdr.size[0] << 24) | (imghdr.size[1] << 16) |
215 (imghdr.size[2]<<8) | imghdr.size[3];
216 raw = malloc(size);
217 if (!raw)
218 return GP_ERROR_NO_MEMORY;
219 if ((ret=gp_port_read(port, (char *)raw, size))<0) {
220 free (raw);
221 return ret;
222 }
223
224 sprintf(header, "P6\n# gPhoto2 stv0680 image\n%d %d\n255\n", w, h);
225 gp_file_append(file, header, strlen(header));
226 data = malloc(size * 3);
227 if (!data) {
228 free (raw);
229 return GP_ERROR_NO_MEMORY;
230 }
231 gp_bayer_decode(raw,w,h,data,BAYER_TILE_GBRG_INTERLACED);
232 free(raw);
233 gp_file_append(file, (char *)data, size*3);
234 free(data);
235 return GP_OK;
236 }
237
stv0680_get_image(GPPort * port,int image_no,CameraFile * file)238 int stv0680_get_image(GPPort *port, int image_no, CameraFile *file)
239 {
240 struct stv680_image_header imghdr;
241 char header[200];
242 unsigned char buf[16];
243 unsigned char *raw, *tmpdata1, *tmpdata2, *data;
244 int h,w,ret,coarse,fine,size;
245
246 /* Despite the documentation saying so, CMDID_UPLOAD_IMAGE does not
247 * return an image_header. The first 8 byte are correct, but the
248 * next 8 appear strange. So we call CMDID_GET_IMAGE_HEADER before.
249 */
250 ret = stv0680_try_cmd(port, CMDID_GET_IMAGE_HEADER, image_no,
251 (void*)&imghdr, sizeof(imghdr));
252 if(ret != GP_OK)
253 return ret;
254 ret = stv0680_try_cmd(port, CMDID_UPLOAD_IMAGE, image_no,
255 (void*)buf, sizeof(buf));
256 if(ret != GP_OK)
257 return ret;
258 w = (imghdr.width[0] << 8) | imghdr.width[1];
259 h = (imghdr.height[0] << 8) | imghdr.height[1];
260 size = (imghdr.size[0] << 24) | (imghdr.size[1] << 16) |
261 (imghdr.size[2]<<8) | imghdr.size[3];
262 fine = (imghdr.fine_exposure[0]<<8)|imghdr.fine_exposure[1];
263 coarse = (imghdr.coarse_exposure[0]<<8)|imghdr.coarse_exposure[1];
264 raw = malloc(size);
265 if (!raw)
266 return GP_ERROR_NO_MEMORY;
267 sprintf(header, "P6\n# gPhoto2 stv0680 image\n#flags %x sgain %d sclkdiv %d avgpix %d fine %d coarse %d\n%d %d\n255\n", imghdr.flags, imghdr.sensor_gain, imghdr.sensor_clkdiv, imghdr.avg_pixel_value, fine, coarse , w, h);
268
269 gp_file_append(file, header, strlen(header));
270 if ((ret=gp_port_read(port, (char *)raw, size))<0) {
271 free (raw);
272 return ret;
273 }
274
275 data = malloc(size * 3);
276 if (!data) {
277 free (raw);
278 return GP_ERROR_NO_MEMORY;
279 }
280 tmpdata1 = malloc(size * 3);
281 if (!tmpdata1) {
282 free (raw);
283 free (data);
284 return GP_ERROR_NO_MEMORY;
285 }
286 tmpdata2 = malloc(size * 3);
287 if (!tmpdata2) {
288 free (raw);
289 free (data);
290 free (tmpdata1);
291 return GP_ERROR_NO_MEMORY;
292 }
293 gp_bayer_expand (raw, w, h, tmpdata1, BAYER_TILE_GBRG_INTERLACED);
294 light_enhance(w,h,coarse,imghdr.avg_pixel_value,fine,tmpdata1);
295 /* gp_bayer_interpolate (tmpdata1, w, h, BAYER_TILE_GBRG_INTERLACED); */
296 stv680_hue_saturation (w, h, tmpdata1, tmpdata2 );
297 demosaic_sharpen (w, h, tmpdata2, tmpdata1, 2, BAYER_TILE_GBRG_INTERLACED);
298 sharpen (w, h, tmpdata1, data, 16);
299 free(tmpdata2);
300 free(tmpdata1);
301 free(raw);
302 gp_file_append(file, (char *)data, 3*size);
303 free(data);
304 return GP_OK;
305 }
306
stv0680_get_image_preview(GPPort * port,int image_no,CameraFile * file)307 int stv0680_get_image_preview(GPPort *port, int image_no, CameraFile *file)
308 {
309 struct stv680_image_header imghdr;
310 char header[64];
311 unsigned char *raw, *data;
312 unsigned int h,w,rh,rw,scale,rsize,size;
313 int ret;
314
315 switch (port->type) {
316 case GP_PORT_USB:
317 if ((ret = stv0680_try_cmd(port, CMDID_UPLOAD_IMAGE,
318 image_no, (void*)&imghdr, sizeof(imghdr)) < 0)) {
319 return ret;
320 }
321 rw = (imghdr.width[0] << 8) | imghdr.width[1];
322 rh = (imghdr.height[0] << 8) | imghdr.height[1];
323 rsize = (imghdr.size[0] << 24) | (imghdr.size[1] << 16) |
324 (imghdr.size[2]<<8) | imghdr.size[3];
325 scale = (rw>>8)+1;
326 break;
327 default:
328 case GP_PORT_SERIAL:
329 ret = stv0680_try_cmd(port, CMDID_UPLOAD_THUMBNAIL, image_no,
330 (void*)&imghdr, sizeof(imghdr));
331 if(ret != GP_OK)
332 return ret;
333 rw = (imghdr.width[0] << 8) | imghdr.width[1];
334 rh = (imghdr.height[0] << 8) | imghdr.height[1];
335 rsize = (imghdr.size[0] << 24) | (imghdr.size[1] << 16) |
336 (imghdr.size[2]<<8) | imghdr.size[3];
337 scale = 0;
338 break;
339 }
340 raw = calloc(1, rsize);
341 if (!raw) return GP_ERROR_NO_MEMORY;
342 if ((ret=gp_port_read(port, (char *)raw, rsize))<0) {
343 free(raw);
344 return ret;
345 }
346 w = rw >> scale;
347 h = rh >> scale;
348 size = w * h;
349
350 sprintf(header, "P6\n# gPhoto2 stv0680 image\n%d %d\n255\n", w, h);
351 gp_file_append(file, header, strlen(header));
352 data = calloc(1,size * 3);
353
354 if (!scale)
355 gp_bayer_decode(raw, rw, rh, data, BAYER_TILE_GBRG_INTERLACED);
356 else
357 bayer_unshuffle_preview(rw, rh, scale, raw, data);
358 free(raw);
359 gp_file_append(file, (char *)data, size*3);
360 free(data);
361 return GP_OK;
362 }
363
stv0680_capture(GPPort * port)364 int stv0680_capture(GPPort *port)
365 {
366 int ret;
367 struct stv680_error_info errinf;
368
369 ret = stv0680_try_cmd(port, CMDID_GRAB_IMAGE, GRAB_UPDATE_INDEX|GRAB_USE_CAMERA_INDEX, NULL, 0);
370 if (ret!=GP_OK)
371 return ret;
372 /* wait until it is done */
373 do {
374 ret = stv0680_last_error(port, &errinf);
375 if (ret != GP_OK)
376 return ret;
377 if (errinf.error == CAMERR_BAD_EXPOSURE) {
378 gp_port_set_error(port,_("Bad exposure (not enough light probably)"));
379 return GP_ERROR;
380 }
381 if (errinf.error != CAMERR_BUSY)
382 fprintf(stderr,"stv680_capture: error was %d.%d\n",errinf.error,errinf.info);
383 } while (errinf.error == CAMERR_BUSY);
384 return GP_OK;
385 }
386
387 #if 0
388 /* this somehow terminates after some images. timeouts due to low exposure?
389 * I haven't found out yet. But this would be the right way to do it.
390 */
391 int stv0680_capture_preview(GPPort *port, char **data, int *size)
392 {
393 struct stv680_image_header imghdr;
394 struct stv680_image_info imginfo;
395 struct stv680_error_info errinf;
396 char header[64];
397 unsigned char *raw, *bayerpre;
398 int fine,coarse,h,w,ret;
399
400 ret = stv0680_last_error(port, &errinf);
401 if (ret != GP_OK)
402 return ret;
403
404 ret = stv0680_try_cmd(port, CMDID_GRAB_IMAGE, GRAB_USE_CAMERA_INDEX, NULL,0);
405 if(ret != GP_OK)
406 return ret;
407 do {
408 ret = stv0680_try_cmd(port, CMDID_CLEAR_COMMS_ERROR, 0, (void*)&errinf, sizeof(errinf));
409 if (ret != GP_OK)
410 return ret;
411 if (errinf.error == CAMERR_BAD_EXPOSURE) {
412 gp_port_set_error(port,_("Bad exposure (not enough light probably)"));
413 return GP_ERROR;
414 }
415 if (errinf.error != CAMERR_BUSY)
416 fprintf(stderr,"stv680_capture: error was %d.%d\n",errinf.error,errinf.info);
417 } while (errinf.error == CAMERR_BUSY);
418
419 #if 0
420 fprintf(stderr,"image flag %x\n",imghdr.flags);
421 fprintf(stderr,"sensor gain %d\n",imghdr.sensor_gain);
422 fprintf(stderr,"sensor clkdiv %d\n",imghdr.sensor_clkdiv);
423 fprintf(stderr,"avg pixel value %d\n",imghdr.avg_pixel_value);
424 #endif
425 ret = stv0680_try_cmd(port, CMDID_GET_IMAGE_INFO, 0, (void*)&imginfo, sizeof(imginfo));
426 if (ret!=GP_OK)
427 return ret;
428 ret = stv0680_try_cmd(port, CMDID_UPLOAD_IMAGE, (imginfo.index[0]<<8)|imginfo.index[1], (void*)&imghdr, sizeof(imghdr));
429 if(ret != GP_OK)
430 return ret;
431 fine = (imghdr.fine_exposure[0]<<8)|imghdr.fine_exposure[1];
432 coarse = (imghdr.coarse_exposure[0]<<8)|imghdr.coarse_exposure[1];
433 w = (imghdr.width[0] << 8) | imghdr.width[1];
434 h = (imghdr.height[0] << 8) | imghdr.height[1];
435 *size = (imghdr.size[0] << 24) | (imghdr.size[1] << 16) |
436 (imghdr.size[2]<<8) | imghdr.size[3];
437
438 raw = malloc(*size);
439 if ((ret=gp_port_read(port, raw, *size))<0)
440 return ret;
441
442 sprintf(header, "P6\n# gPhoto2 stv0680 image\n%d %d\n255\n", w, h);
443 *data = malloc(((*size) * 3) + strlen(header));
444 strcpy(*data, header);
445 bayerpre = malloc(((*size)*3));
446 gp_bayer_expand (raw, w, h, bayerpre, BAYER_TILE_GBRG_INTERLACED);
447 light_enhance(w,h,coarse,fine,imghdr.avg_pixel_value,bayerpre);
448 /* gp_bayer_interpolate (bayerpre, w, h, BAYER_TILE_GBRG_INTERLACED); */
449 demosaic_sharpen (w, h, bayerpre, *data + strlen(header), 2, BAYER_TILE_GBRG_INTERLACED);
450 /* sharpen (w, h, bayerpre,*data + strlen(header), 20); */
451 free(bayerpre);
452 free(raw);
453 *size *= 3;
454 *size += strlen(header);
455 return GP_OK;
456 }
457 #endif
458
stv0680_capture_preview(GPPort * port,char ** data,int * size)459 int stv0680_capture_preview(GPPort *port, char **data, int *size)
460 {
461 char header[64];
462 struct stv680_camera_info caminfo;
463 unsigned char *raw,*bayerpre;
464 int xsize,h,w,i;
465 int ret;
466 struct capmode {
467 int mask,w,h,mode;
468 } capmodes[4] = {
469 { 1, STV0680_CIF_WIDTH, STV0680_CIF_HEIGHT, 0x000 },
470 { 2, STV0680_VGA_WIDTH, STV0680_VGA_HEIGHT, 0x100 },
471 { 4, STV0680_QCIF_WIDTH, STV0680_QCIF_HEIGHT, 0x200 },
472 { 8, STV0680_QVGA_WIDTH, STV0680_QVGA_HEIGHT, 0x300 },
473 };
474
475 /* Get Camera Information */
476 if ((ret = stv0680_try_cmd(port, CMDID_GET_CAMERA_INFO, 0,
477 (void*)&caminfo, sizeof(caminfo)) < 0))
478 return ret;
479
480 /* serial cameras don't have video... Too slow. */
481 if (!(caminfo.hardware_config & HWCONFIG_HAS_VIDEO))
482 return GP_ERROR_NOT_SUPPORTED;
483
484 /* Look for the first supported mode, with decreasing size */
485 for (i=0;i<4;i++)
486 if (caminfo.capabilities & capmodes[i].mask)
487 break;
488 if (i==4) {
489 fprintf(stderr,"Neither CIF, QCIF, QVGA nor VGA supported?\n");
490 return GP_ERROR;
491 }
492 w = capmodes[i].w;
493 h = capmodes[i].h;
494
495 xsize = (w+2)*(h+2);
496
497 /* Send parameter according to mode */
498 ret = stv0680_try_cmd(port,CMDID_START_VIDEO,capmodes[i].mode,NULL,0x0);
499
500 if (ret!=GP_OK)
501 return ret;
502
503 *size= xsize;
504 raw = malloc(*size);
505 switch(gp_port_read(port, (char *)raw, *size)) {
506 case GP_ERROR_TIMEOUT:
507 printf("read timeout\n");
508 break;
509 case GP_ERROR:
510 printf("IO error\n");
511 break;
512 default:break;
513 }
514 if ((ret = stv0680_try_cmd(port, CMDID_STOP_VIDEO, 0, NULL, 0)!=GP_OK)) {
515 free (raw);
516 return ret;
517 }
518 sprintf(header, "P6\n# gPhoto2 stv0680 image\n%d %d\n255\n", w, h);
519 *data = malloc((*size * 3) + strlen(header));
520 strcpy(*data, header);
521 bayerpre = malloc(((*size)*3));
522 /* no light enhancement here, we do not get the exposure values? */
523 gp_bayer_decode (raw, w, h, bayerpre, BAYER_TILE_GBRG_INTERLACED);
524 demosaic_sharpen (w, h, bayerpre, (unsigned char *)*data + strlen(header), 2, BAYER_TILE_GBRG_INTERLACED);
525 /* sharpen (w, h, bayerpre,*data + strlen(header), 20); */
526 free(raw);
527 free(bayerpre);
528 *size *= 3;
529 *size += strlen(header);
530 return GP_OK;
531 }
532
533
stv0680_delete_all(GPPort * port)534 int stv0680_delete_all(GPPort *port) {
535 return stv0680_try_cmd(port,CMDID_SET_IMAGE_INDEX,0,NULL,0);
536 }
537
stv0680_summary(GPPort * port,char * txt)538 int stv0680_summary(GPPort *port, char *txt)
539 {
540 struct stv680_camera_info caminfo;
541 struct stv680_image_info imginfo;
542 int ret;
543
544 strcpy(txt,_("Information on STV0680-based camera:\n"));
545 /* Get Camera Information */
546 if ((ret = stv0680_try_cmd(port, CMDID_GET_CAMERA_INFO,
547 0, (void*)&caminfo, sizeof(caminfo)) < 0))
548 return ret;
549 sprintf(txt+strlen(txt),_("Firmware Revision: %d.%d\n"),
550 caminfo.firmware_revision[0],
551 caminfo.firmware_revision[1]
552 );
553 sprintf(txt+strlen(txt),_("ASIC Revision: %d.%d\n"),
554 caminfo.asic_revision[0],
555 caminfo.asic_revision[1]
556 );
557 sprintf(txt+strlen(txt),_("Sensor ID: %d.%d\n"),
558 caminfo.sensor_id[0],
559 caminfo.sensor_id[1]
560 );
561 /* HWCONFIG_COMMSLINK_MASK ... not really needed, the user knows, he
562 * plugged it in. */
563 sprintf(txt+strlen(txt),_("Camera is configured for lights flickering by %dHz.\n"),
564 (caminfo.hardware_config & HWCONFIG_FLICKERFREQ_60HZ)?60:50
565 );
566 sprintf(txt+strlen(txt),_("Memory in camera: %d Mbit.\n"),
567 (caminfo.hardware_config & HWCONFIG_MEMSIZE_16MBIT)?16:64
568 );
569 if (caminfo.hardware_config & HWCONFIG_HAS_THUMBNAILS)
570 strcat(txt,_("Camera supports Thumbnails.\n"));
571 if (caminfo.hardware_config & HWCONFIG_HAS_VIDEO)
572 strcat(txt,_("Camera supports Video.\n"));
573 /* HWCONFIG_STARTUP_COMPLETED ... Would the camera even answer if not ? */
574 if (caminfo.hardware_config & HWCONFIG_IS_MONOCHROME)
575 strcat(txt,_("Camera pictures are monochrome.\n"));
576 if (caminfo.hardware_config & HWCONFIG_MEM_FITTED) /* Is this useful? */
577 strcat(txt,_("Camera has memory.\n"));
578
579 strcat(txt,_("Camera supports videoformats: "));
580 if (caminfo.capabilities & CAP_CIF) strcat(txt,"CIF ");
581 if (caminfo.capabilities & CAP_VGA) strcat(txt,"VGA ");
582 if (caminfo.capabilities & CAP_QCIF) strcat(txt,"QCIF ");
583 if (caminfo.capabilities & CAP_QVGA) strcat(txt,"QVGA ");
584 strcat(txt,"\n");
585 sprintf(txt+strlen(txt),_("Vendor ID: %02x%02x\n"),
586 caminfo.vendor_id[0],
587 caminfo.vendor_id[1]
588 );
589 sprintf(txt+strlen(txt),_("Product ID: %02x%02x\n"),
590 caminfo.product_id[0],
591 caminfo.product_id[1]
592 );
593 if ((ret = stv0680_try_cmd(port, CMDID_GET_IMAGE_INFO, 0,
594 (void*)&imginfo, sizeof(imginfo))!=GP_OK))
595 return ret;
596 sprintf(txt+strlen(txt),_("Number of Images: %d\n"),
597 (imginfo.index[0]<<8)|imginfo.index[1]
598 );
599 sprintf(txt+strlen(txt),_("Maximum number of Images: %d\n"),
600 (imginfo.maximages[0]<<8)|imginfo.maximages[1]
601 );
602 sprintf(txt+strlen(txt),_("Image width: %d\n"),
603 (imginfo.width[0]<<8)|imginfo.width[1]
604 );
605 sprintf(txt+strlen(txt),_("Image height: %d\n"),
606 (imginfo.height[0]<<8)|imginfo.height[1]
607 );
608 sprintf(txt+strlen(txt),_("Image size: %d\n"),
609 (imginfo.size[0]<<24)|(imginfo.size[1]<<16)|(imginfo.size[2]<<8)|
610 imginfo.size[3]
611 );
612 sprintf(txt+strlen(txt),_("Thumbnail width: %d\n"),imginfo.thumb_width);
613 sprintf(txt+strlen(txt),_("Thumbnail height: %d\n"),imginfo.thumb_height);
614 sprintf(txt+strlen(txt),_("Thumbnail size: %d\n"),
615 (imginfo.thumb_size[0]<<8)|imginfo.thumb_size[1]
616 );
617 return GP_OK;
618 }
619