1 /* library.c
2  *
3  * Copyright (C) 2003 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 #define _DEFAULT_SOURCE
22 
23 #include <config.h>
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include <bayer.h>
30 #include <gamma.h>
31 
32 #include <gphoto2/gphoto2.h>
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 "aox.h"
49 #include <gphoto2/gphoto2-port.h>
50 
51 #define GP_MODULE "aox"
52 
53 struct _CameraPrivateLibrary {
54 	Model model;
55 	Info info[2];
56 };
57 
58 static struct {
59    	char *name;
60 	CameraDriverStatus status;
61    	unsigned short idVendor;
62    	unsigned short idProduct;
63 } models[] = {
64         {"Concord EyeQMini_1", GP_DRIVER_STATUS_EXPERIMENTAL, 0x03e8, 0x2182},
65         {"Concord EyeQMini_2", GP_DRIVER_STATUS_EXPERIMENTAL, 0x03e8, 0x2180},
66         {"D-MAX DM3588", GP_DRIVER_STATUS_EXPERIMENTAL, 0x03e8, 0x2130},
67 	{NULL,0,0,0}
68 };
69 
70 int
camera_id(CameraText * id)71 camera_id (CameraText *id)
72 {
73     	strcpy (id->text, "Aox chipset camera");
74     	return GP_OK;
75 }
76 
77 int
camera_abilities(CameraAbilitiesList * list)78 camera_abilities (CameraAbilitiesList *list)
79 {
80     	int i;
81     	CameraAbilities a;
82 
83     	for (i = 0; models[i].name; i++) {
84         	memset (&a, 0, sizeof(a));
85        		strcpy (a.model, models[i].name);
86        		a.status = models[i].status;
87        		a.port   = GP_PORT_USB;
88        		a.speed[0] = 0;
89        		a.usb_vendor = models[i].idVendor;
90        		a.usb_product= models[i].idProduct;
91        		if (a.status == GP_DRIVER_STATUS_EXPERIMENTAL)
92 			a.operations = GP_OPERATION_NONE;
93 		else
94 			a.operations = GP_OPERATION_CAPTURE_IMAGE;
95        		a.folder_operations = GP_FOLDER_OPERATION_NONE;
96        		a.file_operations   = GP_FILE_OPERATION_PREVIEW;
97        		gp_abilities_list_append (list, a);
98     	}
99     	return GP_OK;
100 }
101 
102 static int
camera_summary(Camera * camera,CameraText * summary,GPContext * context)103 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
104 {
105 
106 	int num_lo_pics =aox_get_num_lo_pics(camera->pl->info);
107 	int num_hi_pics =aox_get_num_hi_pics(camera->pl->info);
108 
109     	sprintf (summary->text,_("Your USB camera has an Aox chipset.\n"
110 			"Number of lo-res PICs = %i\n"
111 			"Number of hi-res PICs = %i\n"
112 			"Number of PICs = %i\n"
113        			), num_lo_pics, num_hi_pics, num_lo_pics+num_hi_pics);
114 
115     	return GP_OK;
116 }
117 
118 
119 static int
camera_about(Camera * camera,CameraText * about,GPContext * context)120 camera_about (Camera *camera, CameraText *about, GPContext *context)
121 {
122     	strcpy (about->text, _("Aox generic driver\n"
123 			    "Theodore Kilgore <kilgota@auburn.edu>\n"));
124     	return GP_OK;
125 }
126 
127 /*************** File and Downloading Functions *******************/
128 
129 static int
file_list_func(CameraFilesystem * fs,const char * folder,CameraList * list,void * data,GPContext * context)130 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
131                 void *data, GPContext *context)
132 {
133         Camera *camera = data;
134 	int num_lo_pics = aox_get_num_lo_pics (camera->pl->info);
135 	int num_hi_pics = aox_get_num_hi_pics (camera->pl->info);
136 	int n = num_hi_pics + num_lo_pics;
137 	char name[30];
138 	int i;
139 	/* Low-resolution pictures are always downloaded first. We do not know
140 	 * yet how to process them, so they will remain in RAW format. */
141 
142 	for (i=0; i< num_lo_pics; i++){
143 		snprintf( name, sizeof(name), "aox_pic%03i.raw", i+1 );
144 		gp_list_append(list, name, NULL);
145 	}
146 
147 	for (i = num_lo_pics; i < n; i++){
148 		snprintf( name, sizeof(name), "aox_pic%03i.ppm", i+1 );
149 		gp_list_append(list, name, NULL);
150 	}
151     	return GP_OK;
152 }
153 
154 static int
get_file_func(CameraFilesystem * fs,const char * folder,const char * filename,CameraFileType type,CameraFile * file,void * user_data,GPContext * context)155 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
156 	       CameraFileType type, CameraFile *file, void *user_data,
157 	       GPContext *context)
158 {
159     	Camera *camera = user_data;
160 
161 /* The camera will always download the low-resolution pictures first, if any.
162  * As those are compressed, they are not of fixed size. Unfortunately, the
163  * compression method is not known.
164  * For a high-resolution picture, the size is always the same.
165  * Every picture file has a header, which is of length 0x98. The high-
166  * resolution pictures are just Bayer data; their headers will be discarded.
167  */
168 
169         int i, j, k, n, num_lo_pics, num_hi_pics, w = 0, h = 0;
170 	unsigned char temp;
171 	unsigned char *data;
172 	unsigned char *p_data = NULL;
173 	unsigned char *output = NULL;
174 	int len;
175 	int header_len;
176 	char header[128];
177 	unsigned char gtable[256];
178 
179 	k = gp_filesystem_number(camera->fs, "/", filename, context);
180 
181 
182 	num_lo_pics = aox_get_num_lo_pics(camera->pl->info);
183 	num_hi_pics = aox_get_num_hi_pics(camera->pl->info);
184 
185 
186 	GP_DEBUG("There are %i compressed photos\n", num_lo_pics);
187 	GP_DEBUG("There are %i hi-res photos\n", num_hi_pics);
188 
189 	if ( (k < num_lo_pics) ) {
190 		n = k;
191 		w = 320;
192 		h = 240;
193 	} else {
194 		n = k - num_lo_pics;
195 		w = 640;
196 		h = 480;
197 	}
198 
199 	len = aox_get_picture_size (camera->port, num_lo_pics,
200 						num_hi_pics, n, k);
201 	if (len < GP_OK) return len;
202 
203 	GP_DEBUG("len = %i\n", len);
204 	data = malloc(len);
205 	if (!data) {
206 		printf("Malloc failed\n"); return 0;}
207     	aox_read_picture_data (camera->port, (char *)data, len, n);
208 
209 	switch (type) {
210 	case GP_FILE_TYPE_EXIF:
211 		free (data);
212 		return (GP_ERROR_FILE_EXISTS);
213 
214 	case GP_FILE_TYPE_PREVIEW:
215 	case GP_FILE_TYPE_NORMAL:
216 		if (w == 320) {
217 			gp_file_detect_mime_type (file); /* Detected as "raw"*/
218 			gp_file_set_data_and_size (file, (char *)data, len);
219 			gp_file_adjust_name_for_mime_type (file);
220 			break;
221 		}
222 		if (w == 640) {
223 
224 			if (len < 0x98 + w*h) {
225 				GP_DEBUG("len %d is less than expected %d\n", len, 0x98+w*h);
226 				return GP_ERROR;
227 			}
228 
229 			/* Stripping useless header */
230 			p_data = data + 0x98;
231 			/* Picture is mirror-imaged.*/
232     			for (i = 0; i < h; ++i) {
233 				for (j = 0 ; j < w/2; j++) {
234         				temp = p_data[w*i +j];
235         				p_data[w*i +j] = p_data[w*i+ w -1 -j];
236         				p_data[w*i + w  - 1 - j] = temp;
237 				}
238     			}
239 			/* Not only this, but some columns are
240 			 * interchanged, too. */
241 			for (i = 0; i < w*h/4; i++) {
242 				temp = p_data[4*i +1];
243 				p_data[4*i + 1] = p_data[4*i+2];
244 				p_data[4*i+2] = temp;
245 			}
246 			/* And now create a ppm file, with our own header */
247 			header_len = snprintf(header, 127,
248 				"P6\n"
249 				"# CREATOR: gphoto2, aox library\n"
250 				"%d %d\n"
251 				"255\n", w, h);
252 
253 			output = malloc(3*w*h);
254 			if(!output) {
255 				free(output);
256 				return GP_ERROR_NO_MEMORY;
257 			}
258 			if (camera->pl->model == AOX_MODEL_DMAX)
259 			    gp_bayer_decode (p_data, w, h,
260 					output, BAYER_TILE_RGGB);
261 			else
262 			    gp_bayer_decode (p_data, w, h,
263 					output, BAYER_TILE_GRBG);
264 			/* gamma correction of .70 may not be optimal. */
265 			gp_gamma_fill_table (gtable, .65);
266 			gp_gamma_correct_single (gtable, output, w * h);
267     			gp_file_set_mime_type (file, GP_MIME_PPM);
268     			gp_file_append (file, header, header_len);
269 			gp_file_append (file, (char *)output, 3*w*h);
270 		}
271 		free (data);
272 		free (output);
273 		return GP_OK;
274 	case GP_FILE_TYPE_RAW:
275 		gp_file_set_data_and_size (file, (char *)data, len);
276 		gp_file_set_mime_type (file, GP_MIME_RAW);
277 		gp_file_adjust_name_for_mime_type(file);
278 		break;
279 	default:
280 		free (data);
281 		return (GP_ERROR_NOT_SUPPORTED);
282 	}
283 	return GP_OK;
284 }
285 
286 /*************** Exit and Initialization Functions ******************/
287 
288 static int
camera_exit(Camera * camera,GPContext * context)289 camera_exit (Camera *camera, GPContext *context)
290 {
291 	GP_DEBUG ("Aox camera_exit");
292 
293 	if (camera->pl) {
294 		free (camera->pl);
295 		camera->pl = NULL;
296 	}
297 
298 	return GP_OK;
299 }
300 
301 static CameraFilesystemFuncs fsfuncs = {
302 	.file_list_func = file_list_func,
303 	.get_file_func = get_file_func
304 };
305 
306 int
camera_init(Camera * camera,GPContext * context)307 camera_init(Camera *camera, GPContext *context)
308 {
309 	GPPortSettings settings;
310 	CameraAbilities abilities;
311 	int ret = 0;
312 
313 	/* First, set up all the function pointers */
314 	camera->functions->summary      = camera_summary;
315 	camera->functions->about        = camera_about;
316 	camera->functions->exit	    = camera_exit;
317 
318 	GP_DEBUG ("Initializing the camera\n");
319 	ret = gp_port_get_settings(camera->port,&settings);
320 	if (ret < 0) return ret;
321 
322         ret = gp_camera_get_abilities(camera,&abilities);
323         if (ret < 0) return ret;
324         GP_DEBUG("product number is 0x%x\n", abilities.usb_product);
325 
326 	switch (camera->port->type) {
327 		case GP_PORT_SERIAL:
328 			return ( GP_ERROR );
329 		case GP_PORT_USB:
330 			settings.usb.config = 1;
331 			settings.usb.altsetting = 0;
332 			settings.usb.interface = 1;
333 			settings.usb.inep = 0x84;
334 			settings.usb.outep =0x05;
335 			break;
336 		default:
337 			return ( GP_ERROR );
338 	}
339 
340 	ret = gp_port_set_settings(camera->port,settings);
341 	if (ret < 0) return ret;
342 
343 	GP_DEBUG("interface = %i\n", settings.usb.interface);
344 	GP_DEBUG("inep = %x\n", settings.usb.inep);
345 	GP_DEBUG("outep = %x\n", settings.usb.outep);
346 
347         /* Tell the CameraFilesystem where to get lists from */
348 	gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
349 
350 	camera->pl = malloc (sizeof (CameraPrivateLibrary));
351 	if (!camera->pl) return GP_ERROR_NO_MEMORY;
352 	memset (camera->pl, 0, sizeof (CameraPrivateLibrary));
353 
354 	switch(abilities.usb_product) {
355 	case 0x2130:
356 		camera->pl->model = AOX_MODEL_DMAX;
357 		break;
358 	default:
359 		camera->pl->model = AOX_MODEL_MINI;
360 	}
361 	/* Connect to the camera */
362 	aox_init (camera->port, &camera->pl->model, camera->pl->info);
363 
364 	return GP_OK;
365 }
366