Remove generated files
[framework/connectivity/libgphoto2.git] / camlibs / st2205 / st2205.c
1 /* Sitronix st2205 picframe access library
2  *
3  *   Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <_stdint.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #include <gphoto2/gphoto2-result.h>
33 #include "st2205.h"
34
35 struct image_table_entry {
36         uint8_t present;  /* 1 when this image is present, 0 when deleted */
37         uint32_t address; /* memory address where this image is stored */
38         char name[ST2205_FILENAME_LENGTH + 1]; /* Image name (11 bytes) */
39 } __attribute__((packed));
40
41 /* The st2205 port driver's write and read functions require page aligned
42    buffers, as they use O_DIRECT. */
43 static char *st2205_malloc_page_aligned(int size)
44 {
45         int fd;
46         char *aligned;
47         
48         fd = open ("/dev/zero", O_RDWR);
49         aligned = mmap (0, size, PROT_READ|PROT_WRITE,MAP_PRIVATE, fd, 0);
50         if (aligned == MAP_FAILED)
51                 return NULL;
52
53         return aligned;
54 }
55
56 static void st2205_free_page_aligned(char *aligned, int size)
57 {
58         if (aligned != NULL)
59                 munmap(aligned, size);
60 }
61
62 static int
63 st2205_send_command(Camera *camera, int cmd, int arg1, int arg2)
64 {
65         char *buf = camera->pl->buf;
66
67         if (gp_port_seek (camera->port, ST2205_CMD_OFFSET, SEEK_SET) !=
68             ST2205_CMD_OFFSET)
69                 return GP_ERROR_IO;
70
71         memset(buf, 0, 512);
72         buf[0] = cmd;
73         buf[1] = (arg1 >> 24) & 0xff;
74         buf[2] = (arg1 >> 16) & 0xff;
75         buf[3] = (arg1 >>  8) & 0xff;
76         buf[4] = (arg1      ) & 0xff;
77         buf[5] = (arg2 >> 24) & 0xff;
78         buf[6] = (arg2 >> 16) & 0xff;
79         buf[7] = (arg2 >>  8) & 0xff;
80         buf[8] = (arg2      ) & 0xff;
81
82         if (gp_port_write (camera->port, buf, 512) != 512)
83                 return GP_ERROR_IO_WRITE;
84
85         return GP_OK;
86 }
87
88 static int
89 st2205_read_block(Camera *camera, int block, char *buf)
90 {
91         int ret;
92         if (camera->pl->mem_dump) {
93                 ret = fseek(camera->pl->mem_dump, block * ST2205_BLOCK_SIZE,
94                             SEEK_SET);
95                 if (ret) {
96                         gp_log (GP_LOG_ERROR, "st2205",
97                                 "seeking in memdump: %s", strerror(errno));
98                         return GP_ERROR_IO_READ;
99                 }
100                 ret = fread(buf, 1, ST2205_BLOCK_SIZE, camera->pl->mem_dump);
101                 if (ret != ST2205_BLOCK_SIZE) {
102                         if (ret < 0)
103                                 gp_log (GP_LOG_ERROR, "st2205",
104                                         "reading memdump: %s",
105                                         strerror(errno));
106                         else
107                                 gp_log (GP_LOG_ERROR, "st2205",
108                                         "short read reading from memdump");
109                         return GP_ERROR_IO_READ;
110                 }
111         } else {
112                 CHECK (st2205_send_command (camera, 4, block,
113                                             ST2205_BLOCK_SIZE))
114                 if (gp_port_seek (camera->port, ST2205_READ_OFFSET, SEEK_SET)
115                                 != ST2205_READ_OFFSET)
116                         return GP_ERROR_IO;
117
118                 if (gp_port_read (camera->port, buf, ST2205_BLOCK_SIZE)
119                                 != ST2205_BLOCK_SIZE)
120                         return GP_ERROR_IO_READ;
121         }
122         return GP_OK;
123 }
124
125 static int
126 st2205_write_block(Camera *camera, int block, char *buf)
127 {
128         int ret;
129         if (camera->pl->mem_dump) {
130                 ret = fseek(camera->pl->mem_dump, block * ST2205_BLOCK_SIZE,
131                             SEEK_SET);
132                 if (ret) {
133                         gp_log (GP_LOG_ERROR, "st2205",
134                                 "seeking in memdump: %s", strerror(errno));
135                         return GP_ERROR_IO_WRITE;
136                 }
137                 ret = fwrite(buf, 1, ST2205_BLOCK_SIZE, camera->pl->mem_dump);
138                 if (ret != ST2205_BLOCK_SIZE) {
139                         gp_log (GP_LOG_ERROR, "st2205",
140                                 "writing memdump: %s", strerror(errno));
141                         return GP_ERROR_IO_WRITE;
142                 }
143         } else {
144                 /* Prepare for write */
145                 CHECK (st2205_send_command (camera, 3, block,
146                                             ST2205_BLOCK_SIZE))
147                 /* Write */
148                 if (gp_port_seek (camera->port, ST2205_WRITE_OFFSET, SEEK_SET)
149                                 != ST2205_WRITE_OFFSET)
150                         return GP_ERROR_IO;
151
152                 if (gp_port_write (camera->port, buf, ST2205_BLOCK_SIZE)
153                                 != ST2205_BLOCK_SIZE)
154                         return GP_ERROR_IO_WRITE;
155                 /* Commit */
156                 CHECK (st2205_send_command (camera, 2, block,
157                                             ST2205_BLOCK_SIZE))
158                 /* Read commit response (ignored) */
159                 if (gp_port_seek (camera->port, ST2205_READ_OFFSET, SEEK_SET)
160                                 != ST2205_READ_OFFSET)
161                         return GP_ERROR_IO;
162
163                 if (gp_port_read (camera->port, camera->pl->buf, 512)
164                                 != 512)
165                         return GP_ERROR_IO_READ;
166         }
167         return GP_OK;
168 }
169
170 static int
171 st2205_check_block_present(Camera *camera, int block)
172 {
173         int ret;
174
175         if ((block + 1) * ST2205_BLOCK_SIZE > camera->pl->mem_size) {
176                 gp_log (GP_LOG_ERROR, "st2205", "read beyond end of memory");
177                 return GP_ERROR_CORRUPTED_DATA;
178         }
179
180         if (camera->pl->block_is_present[block])
181                 return GP_OK;
182
183         ret = st2205_read_block(camera, block, camera->pl->mem +
184                                 block * ST2205_BLOCK_SIZE);
185         if (ret == 0)
186                 camera->pl->block_is_present[block] = 1;
187
188         return ret;
189 }
190
191 static int
192 st2205_detect_mem_size(Camera *camera)
193 {
194         char *buf0, *buf1;
195         int i, ret;
196
197         buf0 = st2205_malloc_page_aligned(ST2205_BLOCK_SIZE);
198         buf1 = st2205_malloc_page_aligned(ST2205_BLOCK_SIZE);
199         if (!buf0 || !buf1) {
200                 st2205_free_page_aligned(buf0, ST2205_BLOCK_SIZE);
201                 st2205_free_page_aligned(buf1, ST2205_BLOCK_SIZE);
202                 return GP_ERROR_NO_MEMORY;
203         }
204
205         ret = st2205_read_block(camera, 0, buf0);
206         if (ret)
207                 return ret;
208
209         for (i = 0; i < 3; i++) {
210                 ret = st2205_read_block(camera,
211                                         (524288 / ST2205_BLOCK_SIZE) << i,
212                                         buf1);
213                 if (ret)
214                         return ret;
215                 if (memcmp(buf0, buf1, ST2205_BLOCK_SIZE) == 0)
216                         break;
217         }
218
219         camera->pl->mem_size = 524288 << i;     
220
221         st2205_free_page_aligned(buf0, ST2205_BLOCK_SIZE);
222         st2205_free_page_aligned(buf1, ST2205_BLOCK_SIZE);
223
224         return GP_OK;   
225 }
226
227 static int
228 st2205_read_mem(Camera *camera, int offset,
229         void *buf, int len)
230 {
231         int to_copy, block = offset / ST2205_BLOCK_SIZE;
232
233         while (len) {
234                 CHECK (st2205_check_block_present (camera, block))
235
236                 to_copy = ST2205_BLOCK_SIZE - (offset % ST2205_BLOCK_SIZE);
237                 if (to_copy > len)
238                         to_copy = len;
239
240                 memcpy(buf, camera->pl->mem + offset, to_copy);
241                 buf += to_copy;
242                 len -= to_copy;
243                 offset += to_copy;
244                 block++;
245         }
246         return GP_OK;
247 }
248
249 static int
250 st2205_write_mem(Camera *camera, int offset,
251         void *buf, int len)
252 {
253         int to_copy, block = offset / ST2205_BLOCK_SIZE;
254
255         /* Don't allow writing to the firmware space */
256         if ((offset + len) >
257             (camera->pl->mem_size - camera->pl->firmware_size)) {
258                 gp_log (GP_LOG_ERROR, "st2205", "write beyond end of memory");
259                 return GP_ERROR_CORRUPTED_DATA;
260         }
261
262         while (len) {
263                 CHECK (st2205_check_block_present (camera, block))
264
265                 to_copy = ST2205_BLOCK_SIZE - (offset % ST2205_BLOCK_SIZE);
266                 if (to_copy > len)
267                         to_copy = len;
268
269                 memcpy(camera->pl->mem + offset, buf, to_copy);
270                 camera->pl->block_dirty[block] = 1;
271
272                 buf += to_copy;
273                 len -= to_copy;
274                 offset += to_copy;
275                 block++;
276         }
277         return GP_OK;
278 }
279
280 static int
281 st2205_read_file_count(Camera *camera)
282 {
283         uint8_t count;
284
285         CHECK (st2205_read_mem (camera, ST2205_COUNT_OFFSET, &count, 1))
286
287         return count;
288 }
289
290 static int
291 st2205_write_file_count(Camera *camera, int count)
292 {
293         uint8_t c = count;
294
295         CHECK (st2205_write_mem (camera, ST2205_COUNT_OFFSET, &c, 1))
296
297         return GP_OK;
298 }
299
300 static int
301 st2205_calc_fat_checksum(Camera *camera)
302 {
303         int i, checksum = 0;
304
305         CHECK (st2205_check_block_present(camera, 0))
306
307         /* Calculate the "FAT" checksum, note that the present bits are skipped
308            (as is the checksum location itself). The picframe itself does not
309            care about this, but the windows software does! */
310         for (i = 2; i < ST2205_FAT_SIZE; i++)
311                 if (i % 16)
312                         checksum += (uint8_t)camera->pl->mem[i];
313
314         return checksum & 0xffff;
315 }
316
317 static int
318 st2205_check_fat_checksum(Camera *camera)
319 {
320         int checksum, expected_checksum;
321
322         CHECK (st2205_check_block_present (camera, 0))
323         checksum = le16atoh ((uint8_t *)camera->pl->mem);
324
325         expected_checksum = st2205_calc_fat_checksum (camera);
326         if (expected_checksum < 0) return expected_checksum;
327
328         if (checksum != expected_checksum) {
329                 gp_log (GP_LOG_ERROR, "st2205",
330                         "image table checksum mismatch");
331                 return GP_ERROR_CORRUPTED_DATA;
332         }
333
334         return GP_OK;
335 }
336
337 static int
338 st2205_update_fat_checksum(Camera *camera)
339 {
340         int checksum;
341         uint8_t buf[2];
342
343         checksum = st2205_calc_fat_checksum(camera);
344         if (checksum < 0) return checksum;
345
346         htole16a(buf, checksum);
347         return st2205_write_mem (camera, 0, buf, 2);
348 }
349
350 static int st2205_copy_fat(Camera *camera)
351 {
352         int i;
353
354         /* The "FAT" repeats itself on some frames, copy it over */
355         CHECK (st2205_check_block_present (camera, 0))
356         for (i = 1; i < camera->pl->no_fats; i++)
357                 CHECK (st2205_write_mem (camera, i * ST2205_FAT_SIZE,
358                                          camera->pl->mem, ST2205_FAT_SIZE))
359
360         return GP_OK;
361 }
362
363 static int
364 st2205_add_picture(Camera *camera, int idx, const char *filename,
365         int start, int shuffle, unsigned char *buf, int size)
366 {
367         int count;
368         struct image_table_entry entry;
369
370         count = st2205_read_file_count(camera);
371         if (count < 0) return count;
372
373         if (idx > count) {
374                 gp_log (GP_LOG_ERROR, "st2205",
375                         "adding picture beyond end of FAT");
376                 return GP_ERROR_BAD_PARAMETERS;
377         }
378
379         memset(&entry, 0, sizeof(entry));
380         entry.present = 1;
381         entry.address = htole32(start);
382         snprintf(entry.name, sizeof(entry.name), "%s", filename);
383         CHECK (st2205_write_mem (camera, ST2205_FILE_OFFSET (idx),
384                                  &entry, sizeof(entry)))
385
386         if (idx == count) {
387                 /* update picture count */
388                 count++;
389                 CHECK (st2205_write_file_count (camera, count))
390
391                 /* Add a fake last entry, with no name pointing to beginning
392                    of freespace, neither the windows software nor the picframe
393                    seem to care about this, but the windows software does this,
394                    so lets do it too. */
395                 memset (&entry, 0, sizeof(entry));
396                 entry.address = htole32 (start + size);
397                 CHECK (st2205_write_mem (camera, ST2205_FILE_OFFSET (count),
398                                          &entry, sizeof(entry)))
399         }
400
401         CHECK (st2205_update_fat_checksum (camera))
402         CHECK (st2205_copy_fat (camera))
403         CHECK (st2205_write_mem (camera, start, buf, size))
404
405         /* Let the caller know at which index we stored the table entry */
406         return idx;
407 }
408
409 static int st2205_file_present(Camera *camera, int idx)
410 {
411         struct image_table_entry entry;
412
413         CHECK (st2205_read_mem (camera, ST2205_FILE_OFFSET (idx),
414                                 &entry, sizeof(entry)))
415
416         return entry.present;
417 }
418
419 /***************** Begin "public" functions *****************/
420
421 /* Note this function assumes the names array is filled with zeros
422    before it gets called */
423 int
424 st2205_get_filenames(Camera *camera, st2205_filename *names)
425 {
426         int i, count;
427         struct image_table_entry entry;
428
429         count = st2205_read_file_count(camera);
430         if (count < 0) return count;
431
432         if (count > ST2205_MAX_NO_FILES) {
433                 gp_log (GP_LOG_ERROR, "st2205", "file table count overflow");
434                 return GP_ERROR_CORRUPTED_DATA;
435         }
436
437         for (i = 0; i < count; i++) {
438                 CHECK (st2205_read_mem (camera, ST2205_FILE_OFFSET (i),
439                                         &entry, sizeof(entry)))
440
441                 if (!entry.present)
442                         continue;
443
444                 /* Note we use memcpy as we don't want to depend on the names
445                    on the picframe being 0-terminated. We always write 0
446                    terminated names, as does windows, but better safe then
447                    sorry. */
448                 memcpy(names[i], entry.name, ST2205_FILENAME_LENGTH);
449                 /* Make sure a file with no name gets a "name" */
450                 if (!names[i][0])
451                         names[i][0] = '?';
452         }
453
454         return GP_OK;
455 }
456
457 int
458 st2205_read_raw_file(Camera *camera, int idx, unsigned char **raw)
459 {
460         struct image_table_entry entry;
461         struct st2205_image_header header;
462         int ret, count, size;
463
464         *raw = NULL;
465
466         count = st2205_read_file_count(camera);
467         if (count < 0) return count;
468
469         if (idx >= count) {
470                 gp_log (GP_LOG_ERROR, "st2205",
471                         "read file beyond end of FAT");
472                 return GP_ERROR_BAD_PARAMETERS;
473         }
474
475         CHECK (st2205_read_mem (camera, ST2205_FILE_OFFSET (idx),
476                                 &entry, sizeof(entry)))
477
478         /* This should never happen */
479         if (!entry.present) {
480                 gp_log (GP_LOG_ERROR, "st2205",
481                         "trying to read a deleted file");
482                 return GP_ERROR_BAD_PARAMETERS;
483         }
484         LE32TOH(entry.address);
485
486         GP_DEBUG ("file: %d start at: %08x\n", idx, entry.address);
487
488         if (camera->pl->compressed) {
489                 CHECK (st2205_read_mem (camera, entry.address,
490                                         &header, sizeof(header)))
491
492                 if (header.marker != ST2205_HEADER_MARKER) {
493                         gp_log (GP_LOG_ERROR, "st2205", "invalid header magic");
494                         return GP_ERROR_CORRUPTED_DATA;
495                 }
496
497                 BE16TOH(header.width);
498                 BE16TOH(header.height);
499                 BE16TOH(header.length);
500                 BE16TOH(header.blocks);
501
502                 if ((header.width != camera->pl->width) ||
503                     (header.height != camera->pl->height)) {
504                         gp_log (GP_LOG_ERROR, "st2205",
505                                 "picture size does not match frame size.");
506                         return GP_ERROR_CORRUPTED_DATA;
507                 }
508
509                 if (((header.width / 8) * (header.height / 8)) != header.blocks) {
510                         gp_log (GP_LOG_ERROR, "st2205", "invalid block count");
511                         return GP_ERROR_CORRUPTED_DATA;
512                 }
513
514                 GP_DEBUG ("file: %d header read, size: %dx%d, length: %d bytes\n",
515                           idx, header.width, header.height, header.length);
516
517                 size = header.length + sizeof (header);
518         } else
519                 size = camera->pl->width * camera->pl->height * 2;
520
521         *raw = malloc (size);
522         if (!*raw) {
523                 gp_log (GP_LOG_ERROR, "st2205", "allocating memory");
524                 return GP_ERROR_NO_MEMORY;
525         }
526
527         ret = st2205_read_mem (camera, entry.address, *raw, size);
528         if (ret < 0) {
529                 free (*raw);
530                 *raw = NULL;
531                 return ret;
532         }
533
534         return size;
535 }
536
537 int
538 st2205_read_file(Camera *camera, int idx, int **rgb24)
539 {
540         int ret;
541         unsigned char *src;
542
543         CHECK (st2205_read_raw_file (camera, idx, &src))
544
545         if (camera->pl->compressed)
546                 ret = st2205_decode_image (camera->pl, src, rgb24);
547         else
548                 ret = st2205_rgb565_to_rgb24 (camera->pl, src, rgb24);
549
550         free(src);
551
552         return ret;
553 }
554
555 static int
556 st2205_real_write_file(Camera *camera,
557         const char *filename, int **rgb24, unsigned char *buf,
558         int shuffle, int allow_uv_corr)
559 {
560         int size, count;
561         struct image_table_entry entry;
562         struct st2205_image_header header;
563         int i, start, end, hole_start = 0, hole_idx = 0;
564
565         if (camera->pl->compressed)
566                 size = st2205_code_image (camera->pl, rgb24, buf, shuffle,
567                                           allow_uv_corr);
568         else
569                 size = st2205_rgb24_to_rgb565 (camera->pl, rgb24, buf);
570
571         count = st2205_read_file_count (camera);
572         if (count < 0) return count;
573
574         /* Try to find a large enough "hole" in the memory */
575         end = camera->pl->picture_start;
576         for (i = 0; i <= count; i++) {
577                 /* Fake a present entry at the end of picture mem */
578                 if (i == count) {
579                         entry.present = 1;
580                         start = camera->pl->mem_size -
581                                 camera->pl->firmware_size;
582                         /* If the last entry in the "FAT" was present, we need
583                            to set hole_start to the end of the last picture */
584                         if (!hole_start) {
585                                 hole_start = end;
586                                 hole_idx = i;
587                         }
588                 } else {
589                         CHECK (st2205_read_mem (camera, ST2205_FILE_OFFSET (i),
590                                                 (unsigned char *)&entry,
591                                                 sizeof(entry)))
592
593                         start = entry.address;
594                         if (entry.present) {
595                                 if (camera->pl->compressed) {
596                                         CHECK (st2205_read_mem (camera, start,
597                                                               &header,
598                                                               sizeof(header)))
599
600                                         BE16TOH(header.length);
601                                         end = start + sizeof(header) + 
602                                               header.length;
603                                 } else
604                                         end = start + size;
605                         }
606                 }
607
608                 /* If we have a hole start address look for present entries (so a hole end
609                          address), otherwise look for non present entries */
610                 if (hole_start) {
611                         if (entry.present) {
612                                 int hole_size = start - hole_start;
613                                 GP_DEBUG ("found a hole at: %08x, of %d bytes "
614                                           "(need %d)\n", hole_start, hole_size,
615                                           size);
616                                 if (hole_size < size) {
617                                         /* Too small, start searching
618                                            for the next hole */
619                                         hole_start = 0;
620                                         continue;
621                                 }
622
623                                 /* bingo we have a large enough hole */
624                                 return st2205_add_picture(camera, hole_idx,
625                                                 filename, hole_start, shuffle,
626                                                 buf, size);
627                         }
628                 } else {
629                         if (!entry.present) {
630                                 /* We have a hole starting at the end of the
631                                    *previous* picture, note that end at this
632                                    moment points to the end of the last present
633                                    picture, as we don't set end for non present
634                                    pictures, which is exactly what we want. */
635                                 hole_start = end;
636                                 hole_idx = i;
637                         }
638                 }
639         }
640         
641         /* No freespace found, try again with uv correction tables disabled */
642         if (camera->pl->compressed && allow_uv_corr)
643                 return st2205_real_write_file (camera, filename, rgb24, buf,
644                                                shuffle, 0);
645
646         gp_log (GP_LOG_ERROR, "st2205", "not enough freespace to add file %s",
647                 filename);
648         return GP_ERROR_NO_SPACE;
649 }
650
651 int
652 st2205_write_file(Camera *camera,
653         const char *filename, int **rgb24)
654 {
655         /* The buffer must be large enough for the worst case scenario */
656         unsigned char buf[camera->pl->width * camera->pl->height * 2];
657         int shuffle;
658
659         shuffle = (long long)rand_r(&camera->pl->rand_seed) *
660                    camera->pl->no_shuffles / (RAND_MAX + 1ll);
661
662         return st2205_real_write_file (camera, filename, rgb24, buf,
663                                        shuffle, 1);
664 }
665
666 int
667 st2205_delete_file(Camera *camera, int idx)
668 {
669         uint8_t c = 0;
670         int i, present, count, new_count = 0;
671
672         count = st2205_read_file_count(camera);
673         if (count < 0) return count;
674
675         if (idx >= count) {
676                 gp_log (GP_LOG_ERROR, "st2205",
677                         "delete file beyond end of FAT");
678                 return GP_ERROR_BAD_PARAMETERS;
679         }
680
681         /* Calculate new file count after the delete operation */
682         for (i = 0; i < count; i++) {
683                 if (i == idx)
684                         continue;
685
686                 present = st2205_file_present (camera, i);
687                 if (present < 0) return present;
688                 if (present)
689                         new_count = i + 1;
690         }
691
692         CHECK (st2205_write_mem (camera, ST2205_FILE_OFFSET (idx), &c, 1))
693         CHECK (st2205_write_file_count (camera, new_count))
694         CHECK (st2205_update_fat_checksum (camera))
695         CHECK (st2205_copy_fat (camera))
696
697         return GP_OK;
698 }
699
700 int
701 st2205_delete_all(Camera *camera)
702 {
703         CHECK (st2205_check_block_present(camera, 0))
704         memset (camera->pl->mem + ST2205_FILE_OFFSET (0), 0,
705                 ST2205_FAT_SIZE - ST2205_FILE_OFFSET (0));
706         /* Mark the memory block we've directly manipulated dirty. */
707         camera->pl->block_dirty[0] = 1;
708
709         CHECK (st2205_write_file_count (camera, 0))
710         CHECK (st2205_update_fat_checksum (camera))
711         CHECK (st2205_copy_fat (camera))
712
713         return GP_OK;
714 }
715
716 int
717 st2205_set_time_and_date(Camera *camera, struct tm *t)
718 {
719         uint8_t *buf = (uint8_t *)camera->pl->buf;
720
721         /* We cannot do this when operating on a dump */
722         if (camera->pl->mem_dump)
723                 return GP_OK;
724
725         memset(buf, 0, 512);
726         buf[0] = 6; /* cmd 6 set time */
727         htobe16a (buf + 1, t->tm_year + 1900);
728         buf[3] = t->tm_mon + 1;
729         buf[4] = t->tm_mday;
730         buf[5] = t->tm_hour;
731         buf[6] = t->tm_min;
732         /* The st2205 does not allow one to set seconds, instead the
733            seconds end up being whatever they were when the set time
734            command is send :( */
735
736         if (gp_port_seek (camera->port, ST2205_CMD_OFFSET, SEEK_SET)
737                         != ST2205_CMD_OFFSET)
738                 return GP_ERROR_IO;
739
740         if (gp_port_write (camera->port, camera->pl->buf, 512) != 512)
741                 return GP_ERROR_IO_WRITE;
742
743         /* HACK, the st2205 does not like it if this is the last command
744            send to it, so force re-reading of block 0 */
745         camera->pl->block_is_present[0] = 0;
746         CHECK (st2205_check_block_present(camera, 0))
747
748         return GP_OK;
749 }
750
751 int
752 st2205_commit(Camera *camera)
753 {
754         int i, j;
755         int mem_block_size = (camera->pl->mem_size - camera->pl->firmware_size)
756                                 / ST2205_BLOCK_SIZE;
757         int erase_block_size = ST2205_ERASE_BLOCK_SIZE / ST2205_BLOCK_SIZE;
758
759         for (i = 0; i < mem_block_size; i += erase_block_size) {
760                 for (j = 0; j < erase_block_size; j++)
761                         if (camera->pl->block_dirty[i + j])
762                                 break;
763
764                 /* If we have no dirty blocks in this erase block continue */
765                 if (j == erase_block_size)
766                         continue;
767
768                 /* Make sure all data blocks in this erase block have been
769                    read before erasing the block! */
770                 for (j = 0; j < erase_block_size; j++)
771                         CHECK (st2205_check_block_present (camera, i + j))
772
773                 /* Re-write all the data blocks in this erase block! */
774                 for (j = 0; j < erase_block_size; j++) {
775                         CHECK (st2205_write_block (camera, i + j,
776                                                    camera->pl->mem +
777                                                    (i + j) *
778                                                    ST2205_BLOCK_SIZE))
779                         camera->pl->block_dirty[i + j] = 0;
780                 }
781         }
782         return GP_OK;
783 }
784
785 static int
786 st2205_init(Camera *camera)
787 {
788         uint16_t *lookup_src, *dest;
789         uint8_t *shuffle_src;
790         int x, y, i, j, shuffle_size, checksum, expected_checksum;
791         int is240x320 = 0;
792         const struct {
793                 int width, height, no_tables, usable_tables;
794                 unsigned char unknown3[8];
795                 int checksum;
796         } shuffle_info[] = {
797                 { 128, 160, 8, 7, /* Last shuffle table does not work ?? */
798                   { 0xff, 0xff, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 },
799                   0x00035200 },
800                 { 128, 128, 7, 7,
801                   { 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01 },
802                   0x00025800 },
803                 { 120, 160, 7, 7,
804                   { 0xff, 0xff, 0x04, 0x04, 0x04, 0x04, 0x04 },
805                   0x00030570 },
806                 { 96, 64, 7, 7,
807                   { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 },
808                   0x00008700 },
809                 { 0, 0, 0 }
810         };
811         const struct {
812                 int lookup_offset;
813                 int firmware_size;
814                 int picture_start;
815                 int no_fats;
816         } version_info[] = {
817                 { ST2205_V1_LOOKUP_OFFSET, ST2205_V1_FIRMWARE_SIZE,
818                   ST2205_V1_PICTURE_START, 4 },
819                 { ST2205_V2_LOOKUP_OFFSET, ST2205_V2_FIRMWARE_SIZE,
820                   ST2205_V2_PICTURE_START, 1 },
821                 { }
822         };
823         const int uncompressed_firmware_checksums[] = {
824                 0x00ab02fc, /* Frame 96x64 from blokker (Netherlands) */
825                 0x00aa8060, /* Blokker frame with picframe hacked firmware */
826                 0 };
827
828         GP_DEBUG ("st2205_init called");
829
830         CHECK (st2205_detect_mem_size(camera))
831
832         if ((camera->pl->width % 8) || (camera->pl->height % 8)) {
833                 gp_log (GP_LOG_ERROR, "st2205",
834                         "lcd width and height must be a multiple of 8");
835                 return GP_ERROR_IO;
836         }
837
838         if (camera->pl->width == 240 && camera->pl->height == 320)
839                 is240x320 = 1;
840
841         shuffle_size = (camera->pl->width / 8) * (camera->pl->height / 8);
842         if (shuffle_size > ST2205_SHUFFLE_SIZE) {
843                 gp_log (GP_LOG_ERROR, "st2205",
844                         "shuffle table size too small!");
845                 return GP_ERROR_FIXED_LIMIT_EXCEEDED;
846         }
847
848         camera->pl->mem = st2205_malloc_page_aligned(camera->pl->mem_size);
849         if (!camera->pl->mem)
850                 return GP_ERROR_NO_MEMORY;
851
852         /* Get the lookup tables from the device */
853         for (i = 0; version_info[i].lookup_offset; i++) {
854                 int lookup_offset = version_info[i].lookup_offset;
855                 checksum = 0;
856
857                 if ((lookup_offset + ST2205_LOOKUP_SIZE) >
858                     camera->pl->mem_size)
859                         continue;
860
861                 CHECK (st2205_check_block_present(camera,
862                                         lookup_offset / ST2205_BLOCK_SIZE))
863
864                 lookup_src = (uint16_t *)(camera->pl->mem + lookup_offset);
865                 dest = (uint16_t *)(&camera->pl->lookup[0][0][0]);
866                 for (j = 0; j < 3 * 256 * 8; j++) {
867                         *dest++ = le16toh(*lookup_src++);
868                         checksum += (uint8_t)
869                                 (camera->pl->mem + lookup_offset)[j * 2];
870                         checksum += (uint8_t)
871                                 (camera->pl->mem + lookup_offset)[j * 2 + 1];
872                 }
873
874                 if (checksum == ST2205_LOOKUP_CHECKSUM)
875                         break;
876         }
877
878         if (!version_info[i].lookup_offset) {
879                 gp_log (GP_LOG_ERROR, "st2205", "lookup tables not found");
880                 return GP_ERROR_MODEL_NOT_FOUND;
881         }
882
883         camera->pl->firmware_size = version_info[i].firmware_size;
884         camera->pl->picture_start = version_info[i].picture_start;
885         camera->pl->no_fats       = version_info[i].no_fats;
886
887         /* Generate shuffle tables 0 and 1 */
888         for (y = 0, i = 0; y < camera->pl->height; y += 8)
889                 for (x = 0; x < camera->pl->width; x += 8, i++) {
890                         camera->pl->shuffle[0][i].x = x;
891                         camera->pl->shuffle[0][i].y = y;
892                 }
893
894         for (x = 0, i = 0; x < camera->pl->width; x += 8)
895                 for (y = 0; y < camera->pl->height; y += 8, i++) {
896                         camera->pl->shuffle[1][i].x = x;
897                         camera->pl->shuffle[1][i].y = y;
898                 }
899
900         /* Get the other shuffle tables from the device (they start directly
901            after the chroma lookup table) */
902         shuffle_src = (uint8_t *)lookup_src;
903
904         /* Skip to the tables for the right resolution */
905         for (i = 0; shuffle_info[i].no_tables; i++) {
906                 if (camera->pl->width   == shuffle_info[i].width &&
907                                 camera->pl->height == shuffle_info[i].height)
908                         break;
909                 if (is240x320 && shuffle_info[i].width == 120 &&
910                                 shuffle_info[i].height == 160)
911                         break;
912                 shuffle_src += (shuffle_info[i].width *
913                                 shuffle_info[i].height * 2 / 64) *
914                                (shuffle_info[i].no_tables - 2);
915         }
916         if (!shuffle_info[i].no_tables) {
917                 gp_log (GP_LOG_ERROR, "st2205",
918                         "unknown display resolution: %dx%d",
919                         camera->pl->width, camera->pl->height);
920                 return GP_ERROR_MODEL_NOT_FOUND;
921         }
922
923         memcpy (camera->pl->unknown3, shuffle_info[i].unknown3,
924                 sizeof(camera->pl->unknown3));
925         camera->pl->no_shuffles = shuffle_info[i].usable_tables;
926         expected_checksum = shuffle_info[i].checksum;
927         checksum = 0;
928         for (j = 2; j < camera->pl->no_shuffles; j++)
929                 for (i = 0; i < shuffle_size; i++) {
930                         camera->pl->shuffle[j][i].x = *shuffle_src++;
931                         camera->pl->shuffle[j][i].y = *shuffle_src++;
932                         if (camera->pl->shuffle[j][i].x >= camera->pl->width ||
933                             camera->pl->shuffle[j][i].y >= camera->pl->height){
934                                 gp_log (GP_LOG_ERROR, "st2205",
935                                       "shuffle table coordinate out of range");
936                                 return GP_ERROR_CORRUPTED_DATA;
937                         }
938                         checksum += camera->pl->shuffle[j][i].x;
939                         checksum += camera->pl->shuffle[j][i].y;
940                         if (is240x320) {
941                                 camera->pl->shuffle[j][i].x *= 2;
942                                 camera->pl->shuffle[j][i].y *= 2;
943                                 camera->pl->shuffle[j][i + 1].x =
944                                         camera->pl->shuffle[j][i].x + 8;
945                                 camera->pl->shuffle[j][i + 1].y =
946                                         camera->pl->shuffle[j][i].y;
947                                 camera->pl->shuffle[j][i + 2].x =
948                                         camera->pl->shuffle[j][i].x;
949                                 camera->pl->shuffle[j][i + 2].y =
950                                         camera->pl->shuffle[j][i].y + 8;
951                                 camera->pl->shuffle[j][i + 3].x =
952                                         camera->pl->shuffle[j][i].x + 8;
953                                 camera->pl->shuffle[j][i + 3].y =
954                                         camera->pl->shuffle[j][i].y + 8;
955                                 i += 3;
956                         }
957                 }
958
959         if (checksum != expected_checksum) {
960                 gp_log (GP_LOG_ERROR, "st2205",
961                         "shuffle table checksum mismatch");
962                 return GP_ERROR_MODEL_NOT_FOUND;
963         }
964
965         CHECK (st2205_check_fat_checksum (camera))
966
967         camera->pl->rand_seed = time(NULL);
968
969         /* Some 96x64 models don't use compression, unfortunately I've found
970            no way to detect if this is the case, so we keep a list of firmware
971            checksums to identify these. */
972         for (i = camera->pl->mem_size - camera->pl->firmware_size;
973              i < camera->pl->mem_size; i += ST2205_BLOCK_SIZE)
974                 CHECK (st2205_check_block_present (camera,
975                                                    i / ST2205_BLOCK_SIZE))
976         checksum = 0;
977         for (i = camera->pl->mem_size - camera->pl->firmware_size;
978              i < camera->pl->mem_size; i++)
979                 checksum += (uint8_t)camera->pl->mem[i];
980
981         GP_DEBUG ("firmware checksum: 0x%08x", checksum);
982
983         for (i = 0; uncompressed_firmware_checksums[i]; i++)
984                 if (uncompressed_firmware_checksums[i] == checksum)
985                         break;
986
987         if (!uncompressed_firmware_checksums[i])
988                 camera->pl->compressed = 1;
989         else
990                 camera->pl->compressed = 0;
991
992         return GP_OK;
993 }
994
995 static void
996 st2205_exit(Camera *camera)
997 {
998         st2205_free_page_aligned(camera->pl->mem, camera->pl->mem_size);
999         camera->pl->mem = NULL;
1000 }
1001
1002 int
1003 st2205_open_device(Camera *camera)
1004 {
1005         camera->pl->buf = st2205_malloc_page_aligned(512);
1006         if (!camera->pl->buf)
1007                 return GP_ERROR_NO_MEMORY;
1008
1009         /* Check this is a Sitronix frame */
1010         CHECK (gp_port_seek (camera->port, 0, SEEK_SET))
1011         if (gp_port_read (camera->port, camera->pl->buf, 512) != 512)
1012                 return GP_ERROR_IO_READ;
1013         if (strcmp (camera->pl->buf, "SITRONIX CORP."))
1014                 return GP_ERROR_MODEL_NOT_FOUND;
1015
1016         /* Read LCD size from the device */
1017         CHECK (st2205_send_command (camera, 5, 0 ,0))
1018
1019         if (gp_port_seek (camera->port, ST2205_READ_OFFSET, SEEK_SET) !=
1020             ST2205_READ_OFFSET)
1021                 return GP_ERROR_IO;
1022
1023         if (gp_port_read (camera->port, camera->pl->buf, 512) != 512)
1024                 return GP_ERROR_IO_READ;
1025
1026         camera->pl->width  = be16atoh ((uint8_t *)camera->pl->buf);
1027         camera->pl->height = be16atoh ((uint8_t *)camera->pl->buf + 2);
1028
1029         GP_DEBUG ("Sitronix picframe of %dx%d detected.",
1030                   camera->pl->width, camera->pl->height);
1031
1032         return st2205_init (camera);
1033 }
1034
1035 int
1036 st2205_open_dump(Camera *camera, const char *dump,
1037                  int width, int height)
1038 {
1039         camera->pl->mem_dump = fopen(dump, "r+");
1040         if (!camera->pl->mem_dump) {
1041                 gp_log (GP_LOG_ERROR, "st2205", "opening memdump file: %s: %s",
1042                         dump, strerror(errno));
1043                 return GP_ERROR_IO_INIT;
1044         }
1045
1046         camera->pl->width  = width;
1047         camera->pl->height = height;
1048
1049         return st2205_init (camera);
1050 }
1051
1052 void st2205_close(Camera *camera)
1053 {
1054         st2205_exit (camera);
1055         if (camera->pl->mem_dump) {
1056                 fclose (camera->pl->mem_dump);
1057                 camera->pl->mem_dump = NULL;
1058         }
1059         st2205_free_page_aligned(camera->pl->buf, 512);
1060         camera->pl->buf = NULL;
1061 }
1062
1063 int
1064 st2205_get_mem_size(Camera *camera)
1065 {
1066         return camera->pl->mem_size;
1067 }
1068
1069 int
1070 st2205_get_free_mem_size(Camera *camera)
1071 {
1072         struct image_table_entry entry;
1073         struct st2205_image_header header;
1074         int i, count, start, end, hole_start = 0, free = 0;
1075
1076         count = st2205_read_file_count (camera);
1077         if (count < 0) return count;
1078
1079         /* Find all holes in the memory and add their sizes together */
1080         end = camera->pl->picture_start;
1081         for (i = 0; i <= count; i++) {
1082                 /* Fake a present entry at the end of picture mem */
1083                 if (i == count) {
1084                         entry.present = 1;
1085                         start = camera->pl->mem_size -
1086                                 camera->pl->firmware_size;
1087                         /* If the last entry in the "FAT" was present, we need
1088                            to set hole_start to the end of the last picture */
1089                         if (!hole_start)
1090                                 hole_start = end;
1091                 } else {
1092                         CHECK (st2205_read_mem (camera, ST2205_FILE_OFFSET (i),
1093                                                 &entry, sizeof(entry)))
1094
1095                         start = entry.address;
1096                         if (entry.present) {
1097                                 if (camera->pl->compressed) {
1098                                         CHECK (st2205_read_mem (camera, start,
1099                                                               &header,
1100                                                               sizeof(header)))
1101
1102                                         BE16TOH(header.length);
1103                                         end = start + sizeof(header) +
1104                                               header.length;
1105                                 } else {
1106                                         end = start +
1107                                               camera->pl->width *
1108                                               camera->pl->height * 2;
1109                                 }
1110                         }
1111                 }
1112
1113                 /* If we have a hole start address look for present entries (so
1114                    a hole end address), else look for non present entries */
1115                 if (hole_start) {
1116                         if (entry.present) {
1117                                 free += start - hole_start;
1118                                 hole_start = 0;
1119                         }
1120                 } else {
1121                         if (!entry.present)
1122                                 hole_start = end;
1123                 }
1124         }
1125
1126         return free;
1127 }