"Fossies" - the Fresh Open Source Software Archive

Member "libgphoto2-2.5.27/camlibs/st2205/st2205.c" (31 Jan 2021, 30282 Bytes) of package /linux/privat/libgphoto2-2.5.27.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "st2205.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.5.26_vs_2.5.27.

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