1 /* library.c
2  *
3  * Copyright (C) 2006 Theodore Kilgore <kilgota@auburn.edu>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; 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 <bayer.h>
28 #include <gamma.h>
29 
30 #include <gphoto2/gphoto2.h>
31 
32 
33 
34 #ifdef ENABLE_NLS
35 #  include <libintl.h>
36 #  undef _
37 #  define _(String) dgettext (PACKAGE, String)
38 #  ifdef gettext_noop
39 #    define N_(String) gettext_noop (String)
40 #  else
41 #    define N_(String) (String)
42 #  endif
43 #else
44 #  define _(String) (String)
45 #  define N_(String) (String)
46 #endif
47 
48 #include "jl2005a.h"
49 #include <gphoto2/gphoto2-port.h>
50 
51 #define GP_MODULE "jl2005a"
52 
53 struct {
54 	char *name;
55 	CameraDriverStatus status;
56 	unsigned short idVendor;
57 	unsigned short idProduct;
58 } models[] = {
59 	{"American Idol Keychain Camera", GP_DRIVER_STATUS_TESTING,
60 							    0x0979, 0x0224},
61 	{"NogaNet TDC-15", GP_DRIVER_STATUS_TESTING, 0x0979, 0x0224},
62 	{"Cobra DC125", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0224},
63 	{NULL,0,0,0}
64 };
65 
66 int
camera_id(CameraText * id)67 camera_id (CameraText *id)
68 {
69 	strcpy (id->text, "JL2005A camera");
70 	return GP_OK;
71 }
72 
73 int
camera_abilities(CameraAbilitiesList * list)74 camera_abilities (CameraAbilitiesList *list)
75 {
76 	int i;
77 	CameraAbilities a;
78 
79 	for (i = 0; models[i].name; i++) {
80 		memset (&a, 0, sizeof(a));
81 		strcpy (a.model, models[i].name);
82 		a.status = models[i].status;
83 		a.port   = GP_PORT_USB;
84 		a.speed[0] = 0;
85 		a.usb_vendor = models[i].idVendor;
86 		a.usb_product= models[i].idProduct;
87 		if (a.status == GP_DRIVER_STATUS_EXPERIMENTAL)
88 			a.operations = GP_OPERATION_NONE;
89 		else
90 			a.operations = GP_OPERATION_CAPTURE_IMAGE;
91 		a.folder_operations = GP_FOLDER_OPERATION_NONE;
92 		a.file_operations   = GP_FILE_OPERATION_PREVIEW
93 					+ GP_FILE_OPERATION_RAW;
94 		gp_abilities_list_append (list, a);
95 	}
96 	return GP_OK;
97 }
98 
99 static int
camera_summary(Camera * camera,CameraText * summary,GPContext * context)100 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
101 {
102 	int num_pics;
103 	num_pics = camera->pl->nb_entries;
104 	GP_DEBUG("camera->pl->nb_entries = %i\n",camera->pl->nb_entries);
105 	sprintf (summary->text,_("This camera contains a Jeilin JL2005A chipset.\n"
106 			"The number of photos in it is %i. \n"), num_pics);
107 	return GP_OK;
108 }
109 
110 
camera_manual(Camera * camera,CameraText * manual,GPContext * context)111 static int camera_manual (Camera *camera, CameraText *manual, GPContext *context)
112 {
113 	strcpy(manual->text,
114 	_(
115         "This driver supports cameras with Jeilin jl2005a chip \n"
116 	"These cameras do not support deletion of photos, nor uploading\n"
117 	"of data. \n"
118 	"Decoding of compressed photos may or may not work well\n"
119 	"and does not work equally well for all supported cameras.\n"
120 	"If present on the camera, video clip frames are downloaded \n"
121 	"as consecutive still photos.\n"
122 	"For further details please consult libgphoto2/camlibs/README.jl2005a\n"
123 	));
124 
125 	return (GP_OK);
126 }
127 
128 
129 static int
camera_about(Camera * camera,CameraText * about,GPContext * context)130 camera_about (Camera *camera, CameraText *about, GPContext *context)
131 {
132 	strcpy (about->text, _("jl2005a camera library\n"
133 			    "Theodore Kilgore <kilgota@auburn.edu>\n"));
134 	return GP_OK;
135 }
136 
137 /*************** File and Downloading Functions *******************/
138 
139 static int
file_list_func(CameraFilesystem * fs,const char * folder,CameraList * list,void * data,GPContext * context)140 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
141                 void *data, GPContext *context)
142 {
143 	Camera *camera = data;
144 	int n;
145 	n = camera->pl->nb_entries;
146 	gp_list_populate (list, "jl_%03i.ppm", n);
147 	return GP_OK;
148 }
149 
150 static int
get_info_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileInfo * info,void * data,GPContext * context)151 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
152 		CameraFileInfo *info, void *data, GPContext *context)
153 {
154 	info->file.fields = GP_FILE_INFO_TYPE;
155 	strcpy (info->file.type, GP_MIME_PPM);
156 
157 	return (GP_OK);
158 }
159 
160 static int
get_file_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileType type,CameraFile * file,void * user_data,GPContext * context)161 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
162 	       CameraFileType type, CameraFile *file, void *user_data,
163 	       GPContext *context)
164 {
165 	Camera *camera = user_data;
166 	int status = GP_OK;
167 	unsigned int w, h = 0;
168 	unsigned int i, j;
169 	int k;
170 	unsigned int b = 0;
171 	int compressed = 0;
172 	unsigned char header[5] = "\xff\xff\xff\xff\x55";
173 	unsigned int size;
174 	unsigned char *data;
175 	unsigned char *image_start;
176 	unsigned char *p_data=NULL;
177 	unsigned char *ppm=NULL, *ptr=NULL;
178 	unsigned char gtable[256];
179 	unsigned char temp;
180 
181 	GP_DEBUG ("Downloading pictures!\n");
182 
183 	/* These are cheap cameras. There ain't no EXIF data. So kill this. */
184 	if (GP_FILE_TYPE_EXIF == type) return GP_ERROR_FILE_EXISTS;
185 	/* Get the number of the photo on the camera */
186 	k = gp_filesystem_number (camera->fs, "/", filename, context);
187 	GP_DEBUG ("Filesystem number is %i\n",k);
188 	b = jl2005a_get_pic_data_size(camera->port, k);
189 	GP_DEBUG("b = %i = 0x%x bytes\n", b,b);
190 	w = jl2005a_get_pic_width(camera->port);
191 	GP_DEBUG ("width is %i\n", w);
192 	h = jl2005a_get_pic_height(camera->port);
193 	GP_DEBUG ("height is %i\n", h);
194 
195 	/* sanity check against bad usb devices */
196 	if ((w ==0) || (w > 1024) || (h == 0) || (h > 1024)) {
197 		GP_DEBUG ("width / height not within sensible range");
198 		return GP_ERROR_CORRUPTED_DATA;
199 	}
200 
201 	if (b < w*h+5) {
202 		GP_DEBUG ("b is %i, while w*h+5 is %i\n", b, w*h+5);
203 		return GP_ERROR_CORRUPTED_DATA;
204 	}
205 
206 	/* Image data to be downloaded contains header and footer bytes */
207 	data = malloc (b+14);
208 	if (!data) return GP_ERROR_NO_MEMORY;
209 
210 	jl2005a_read_picture_data (camera, camera->port, data, b+14);
211 	if (memcmp(header,data,5) != 0)
212 		/* Image data is corrupted! Repeat the operation. */
213 		jl2005a_read_picture_data (camera, camera->port, data, b+14);
214 
215 	if (GP_FILE_TYPE_RAW == type) {
216 		gp_file_set_mime_type(file, GP_MIME_RAW);
217 		gp_file_set_data_and_size(file, (char *)data , b+14 );
218 		return GP_OK;
219 	}
220 
221 	/* Now get ready to put the data into a PPM image file. */
222 	image_start=data+5;
223 	if (w == 176) {
224 		for (i=1; i < h-1; i +=4){
225 			for (j=1; j< w; j ++){
226 				temp=image_start[i*w+j];
227 				image_start[i*w+j] = image_start[(i+1)*w+j];
228 				image_start[(i+1)*w+j] = temp;
229 			}
230 		}
231 		if (h == 72) {
232 			compressed = 1;
233 			h = 144;
234 		}
235 	} else
236 		if (h == 144) {
237 			compressed = 1;
238 			h = 288;
239 		}
240 
241 	/* sanity check the sizes, as the ahd bayer algorithm does not like very small height / width */
242 	if ((h < 72) || (w < 176)) {
243 		status = GP_ERROR_CORRUPTED_DATA;
244 		goto end;
245 	}
246 	p_data = malloc( w*h );
247 	if (!p_data) {
248 		status =  GP_ERROR_NO_MEMORY;
249 		goto end;
250 	}
251 	if (compressed) {
252 		/* compressed seems to mean half the lines */
253 		if (w/2*h > b+14) {
254 			free(p_data);
255 			status = GP_ERROR_CORRUPTED_DATA;
256 			goto end;
257 		}
258 		jl2005a_decompress (image_start, p_data, w, h);
259 	} else {
260 		if (w*h > b+14) {
261 			free(p_data);
262 			status = GP_ERROR_CORRUPTED_DATA;
263 			goto end;
264 		}
265 		memcpy(p_data, image_start, w*h);
266 	}
267 	ppm = malloc (w * h * 3 + 256); /* room for data and header */
268 	if (!ppm) {
269 		free(p_data);
270 		status = GP_ERROR_NO_MEMORY;
271 		goto end;
272 	}
273 	sprintf ((char *)ppm,
274 			"P6\n"
275 			"# CREATOR: gphoto2, JL2005A library\n"
276 			"%d %d\n"
277 			"255\n", w, h);
278 	size = strlen ((char *)ppm);
279 	ptr = ppm + size;
280 	size = size + (w * h * 3);
281 	GP_DEBUG ("size = %i, w = %d, h = %d\n", size, w,h );
282 	gp_ahd_decode (p_data, w , h, ptr, BAYER_TILE_BGGR);
283 
284 	free(p_data);
285 	gp_gamma_fill_table (gtable, .65);
286 	gp_gamma_correct_single (gtable, ptr, w * h);
287 	gp_file_set_mime_type (file, GP_MIME_PPM);
288 	gp_file_set_data_and_size (file, (char *)ppm, size);
289 end:
290 	free(data);
291 	return status;
292 }
293 
294 
295 /*************** Exit and Initialization Functions ******************/
296 
297 static int
camera_exit(Camera * camera,GPContext * context)298 camera_exit (Camera *camera, GPContext *context)
299 {
300 	GP_DEBUG ("jl2005a camera_exit");
301 	jl2005a_reset(camera, camera->port);
302 	gp_port_close(camera->port);
303 	if (camera->pl) {
304 		free (camera->pl);
305 		camera->pl = NULL;
306 	}
307 	return GP_OK;
308 }
309 
310 static CameraFilesystemFuncs fsfuncs = {
311 	.file_list_func = file_list_func,
312 	.get_file_func = get_file_func,
313 	.get_info_func = get_info_func
314 };
315 
316 int
camera_init(Camera * camera,GPContext * context)317 camera_init(Camera *camera, GPContext *context)
318 {
319 	GPPortSettings settings;
320 	int ret = 0;
321 
322 	/* First, set up all the function pointers */
323 	camera->functions->manual	= camera_manual;
324 	camera->functions->summary	= camera_summary;
325 	camera->functions->about	= camera_about;
326 	camera->functions->exit		= camera_exit;
327 
328 	GP_DEBUG ("Initializing the camera\n");
329 	ret = gp_port_get_settings(camera->port,&settings);
330 	if (ret < 0) return ret;
331 
332 	switch (camera->port->type) {
333 		case GP_PORT_SERIAL:
334 			return ( GP_ERROR );
335 		case GP_PORT_USB:
336 			settings.usb.config = 1;
337 			settings.usb.altsetting = 0;
338 			settings.usb.interface = 0;
339 			/* inep 0x84 used for commands, 0x81 for data. */
340 			settings.usb.inep = 0x84;
341 			settings.usb.outep = 0x03;
342 			break;
343 		default:
344 			return ( GP_ERROR );
345 	}
346 
347 	ret = gp_port_set_settings(camera->port,settings);
348 	if (ret < 0) return ret;
349 
350 	GP_DEBUG("interface = %i\n", settings.usb.interface);
351 	GP_DEBUG("inep = %x\n", settings.usb.inep);
352 	GP_DEBUG("outep = %x\n", settings.usb.outep);
353 
354 	/* Tell the CameraFilesystem where to get lists from */
355 	gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
356 
357 	camera->pl = malloc (sizeof (CameraPrivateLibrary));
358 	if (!camera->pl) return GP_ERROR_NO_MEMORY;
359 	memset (camera->pl, 0, sizeof (CameraPrivateLibrary));
360 	/* Connect to the camera */
361 	camera->pl->data_reg_accessed = 0;
362 	camera->pl->data_to_read = 0;
363 	camera->pl->data_used_from_block = 0;
364 	camera->pl->data_cache = NULL;
365 	jl2005a_init (camera,camera->port, camera->pl);
366 
367 	return GP_OK;
368 }
369