Logo Search packages:      
Sourcecode: v4l-utils version File versions  Download package

firmware-tool.c

/*
   Xceive XC2028/3028 tuner module firmware manipulation tool

   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>

   Copyright (C) 2007, 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
      - Improve --list command
      - Add --seek command

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation version 2

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>

#include <asm/byteorder.h>
#include <asm/types.h>

#include "tuner-xc2028-types.h"
#include "linux/videodev2.h"

#include "extract_head.h"
#include "standards.h"

#define LIST_ACTION           (1<<0)
#define ADD_ACTION            (1<<1)
#define DELETE_ACTION         (1<<2)
#define SET_TYPE_ACTION       (1<<3)
#define SET_ID_ACTION         (1<<4)
#define SEEK_FIRM_ACTION      (1<<5)

struct firmware_description {
      __u32 type;
      __u64 id;
      unsigned char *data;
      __u16 int_freq;
      __u32 size;
};

struct firmware {
      char* name;
      struct firmware_description* desc;
      __u16 version;
      __u16 nr_desc;
};

static struct firmware_description* alloc_firmware_description(void) {
      struct firmware_description *d = malloc(sizeof(*d));
      d->type = 0;
      d->id = 0;
      d->data = NULL;
      d->size = 0;
      return d;
}

static void free_firmware_description(struct firmware_description *d) {
      free(d->data);
      free(d);
}

static struct firmware* alloc_firmware(void) {
      struct firmware *f = malloc(sizeof(*f));
      f->name = NULL;
      f->desc = NULL;
      f->nr_desc = 0;
      return f;
}

static void free_firmware(struct firmware *f) {
      free(f->name);
      free(f->desc);
      if(f->desc) {
            unsigned int i = 0;
            for(i = 0; i < f->nr_desc; ++ i) {
                  free(f->desc[i].data);
            }
      }
      free(f);
}

static void add_firmware_description(struct firmware *f,
                        struct firmware_description *d) {
      struct firmware_description* new_desc;

      new_desc = malloc((f->nr_desc + 1) * sizeof(*new_desc));
      memcpy(new_desc, f->desc, f->nr_desc * sizeof(*new_desc));
      memcpy(new_desc + f->nr_desc, d, sizeof(*d));
      free(f->desc);
      f->desc = new_desc;
      ++f->nr_desc;
}

static void delete_firmware_description(struct firmware *f, __u16 i) {
      struct firmware_description* new_desc;

      if(f->nr_desc == 0 || i >= f->nr_desc) {
            return;
      }

      new_desc = malloc((f->nr_desc - 1) * sizeof(*new_desc));
      memcpy(new_desc, f->desc, i * sizeof(*f->desc));
      memcpy(new_desc + i, f->desc + i + 1, (f->nr_desc - i - 1) * sizeof(*f->desc));
      free(f->desc);
      f->desc = new_desc;
      --f->nr_desc;
}

/* name[32] + version[2] + nr_desc[2] */
#define HEADER_LENGTH (32 + 2 + 2)
/* description header: 4 + 8 + 4.*/
#define DESC_HEADER_LENGTH (4 + 8 + 4)

static int read_firmware(unsigned char* data, off_t size, struct firmware** f_res) {
      char *name = malloc(33);
      unsigned char *p = data;
      struct firmware* f = alloc_firmware();
      unsigned int i;

      if(size < HEADER_LENGTH) {
            printf("Invalid firmware header length.\n");
            free_firmware(f);
            return -1;
      }
      name[32] = 0;
      memcpy(name, data, 32);
      f->name = name;
      p += 32;
      f->version = __le16_to_cpu(*(__u16*)p);
      p += sizeof(f->version);
      f->nr_desc = __le16_to_cpu(*(__u16*)p);
      p += sizeof(f->nr_desc);
      f->desc = malloc(f->nr_desc * sizeof(*(f->desc)));

      for(i = 0; i < f->nr_desc; ++i) {
            if(p + DESC_HEADER_LENGTH > data + size) {
                  printf("Invalid description header length.\n");
                  free_firmware(f);
                  return -1;
            }
            f->desc[i].type = __le32_to_cpu(*(__u32*) p);
            p += sizeof(f->desc[i].type);
            f->desc[i].id = __le64_to_cpu(*(__u64*) p);
            p += sizeof(f->desc[i].id);

            if (f->desc[i].type & HAS_IF) {
                  f->desc[i].int_freq = __le16_to_cpu(*(__u16 *) p);
                  p += sizeof(f->desc[i].int_freq);
            }

            f->desc[i].size = __le32_to_cpu(*(__u32*) p);
            p += sizeof(f->desc[i].size);

            if(p + f->desc[i].size > data + size) {
                  printf("Invalid firmware standard length.\n");
                  f->nr_desc = (f->nr_desc == 0) ? 0 : f->nr_desc -1;
                  free_firmware(f);
                  return -1;
            }

            f->desc[i].data = malloc(f->desc[i].size);
            memcpy(f->desc[i].data, p, f->desc[i].size);

            p += f->desc[i].size;
      }

      *f_res = f;
      return 0;
}

static void write_firmware(struct firmware *f, unsigned char** r_data, off_t *r_size) {
      off_t size;
      unsigned int i = 0;
      unsigned char* data;
      unsigned char* p;

      size = HEADER_LENGTH + f->nr_desc * DESC_HEADER_LENGTH;
      for(i = 0; i < f->nr_desc; ++i) {
            size += f->desc[i].size;
      }

      data = malloc(size);
      p = data;

      memcpy(p, f->name, 32);
      p += 32;

      *(__u16*)p = __cpu_to_le16(f->version);
      p += sizeof(f->version);

      *(__u16*)p = __cpu_to_le16(f->nr_desc);
      p += sizeof(f->nr_desc);

      for(i = 0; i < f->nr_desc; ++i) {
            *(__u32*) p = __cpu_to_le32(f->desc[i].type);
            p += sizeof(f->desc[i].type);

            *(__u64*) p = __cpu_to_le64(f->desc[i].id);
            p += sizeof(f->desc[i].id);

            *(__u32*) p = __cpu_to_le32(f->desc[i].size);
            p += sizeof(f->desc[i].size);

            memcpy(p, f->desc[i].data, f->desc[i].size);
            p += f->desc[i].size;
      }

      *r_data = data;
      *r_size = size;
}

static struct firmware* read_firmware_file(const char* filename) {
      struct stat buf;
      unsigned char *ptr;
      struct firmware *f;
      int fd;

      if(stat(filename, &buf) < 0) {
            perror("Error during stat");
            return NULL;
      }

      fd = open(filename, O_RDONLY);
      if(fd < 0) {
            perror("Error while opening the firmware file");
            return NULL;
      }

      /* allocate firmware buffer*/
      ptr = malloc(buf.st_size);

      if(read(fd, ptr, buf.st_size) < 0) {
            perror("Error while reading the firmware file");
            free(ptr);
            close(fd);
            return NULL;
      }

      if(read_firmware(ptr, buf.st_size, &f) < 0) {
            printf("Invalid firmware file!\n");
            free(ptr);
            close(fd);
            return NULL;
      }

      close(fd);
      free(ptr);
      return f;
}

static void write_firmware_file(const char* filename, struct firmware *f) {
      int fd;
      unsigned char* data;
      off_t size = 0;

      fd = open(filename, O_WRONLY | O_CREAT);
      if(fd < 0) {
            perror("Error while opening the firmware file");
            return;
      }

      if(ftruncate(fd, 0) < 0) {
            perror("Error while deleting the firmware file");
            close(fd);
            return;
      }

      write_firmware(f, &data, &size);

      if(write(fd, data, size) < 0) {
            perror("Error while writing the firmware file");
            close(fd);
            return;
      }

      free(data);
      close(fd);
}

static void dump_firm_type(FILE *fp, unsigned int type)
{
      if (type & SCODE)
            fprintf(fp, "SCODE FW  ");
      else if (type & BASE)
            fprintf(fp, "BASE FW   ");
      else
            fprintf(fp, "STD FW    ");

      if (type & F8MHZ)
            fprintf(fp, "F8MHZ ");
      if (type & MTS)
            fprintf(fp, "MTS ");
      if (type & D2620)
            fprintf(fp, "D2620 ");
      if (type & D2633)
            fprintf(fp, "D2633 ");
      if (type & DTV6)
            fprintf(fp, "DTV6 ");
      if (type & QAM)
            fprintf(fp, "QAM ");
      if (type & DTV7)
            fprintf(fp, "DTV7 ");
      if (type & DTV78)
            fprintf(fp, "DTV78 ");
      if (type & DTV8)
            fprintf(fp, "DTV8 ");
      if (type & FM)
            fprintf(fp, "FM ");
      if (type & INPUT1)
            fprintf(fp, "INPUT1 ");
      if (type & LCD)
            fprintf(fp, "LCD ");
      if (type & NOGD)
            fprintf(fp, "NOGD ");
      if (type & MONO)
            fprintf(fp, "MONO ");
      if (type & ATSC)
            fprintf(fp, "ATSC ");
      if (type & IF)
            fprintf(fp, "IF ");
      if (type & LG60)
            fprintf(fp, "LG60 ");
      if (type & ATI638)
            fprintf(fp, "ATI638 ");
      if (type & OREN538)
            fprintf(fp, "OREN538 ");
      if (type & OREN36)
            fprintf(fp, "OREN36 ");
      if (type & TOYOTA388)
            fprintf(fp, "TOYOTA388 ");
      if (type & TOYOTA794)
            fprintf(fp, "TOYOTA794 ");
      if (type & DIBCOM52)
            fprintf(fp, "DIBCOM52 ");
      if (type & ZARLINK456)
            fprintf(fp, "ZARLINK456 ");
      if (type & CHINA)
            fprintf(fp, "CHINA ");
      if (type & F6MHZ)
            fprintf(fp, "F6MHZ ");
      if (type & INPUT2)
            fprintf(fp, "INPUT2 ");
      if (type & HAS_IF)
            fprintf(fp, "HAS IF ");
}

static void dump_firm_std(FILE *fp, v4l2_std_id id)
{
      v4l2_std_id old=-1, curr_id;

      /* Dumps video standards */
      while (old!=id) {
            old=id;
            if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) {
                  fprintf (fp, "PAL ");
                  curr_id = V4L2_STD_PAL;
            } else if ( (id & V4L2_STD_MN) == V4L2_STD_MN) {
                  fprintf (fp, "NTSC PAL/M PAL/N ");
                  curr_id = V4L2_STD_PAL;
            } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) {
                  fprintf (fp, "PAL/BG ");
                  curr_id = V4L2_STD_PAL_BG;
            } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) {
                  fprintf (fp, "PAL/DK ");
                  curr_id = V4L2_STD_PAL_DK;
            } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) {
                  fprintf (fp, "PAL/B ");
                  curr_id = V4L2_STD_PAL_B;
            } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) {
                  fprintf (fp, "PAL/B1 ");
                  curr_id = V4L2_STD_PAL_B1;
            } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) {
                  fprintf (fp, "PAL/G ");
                  curr_id = V4L2_STD_PAL_G;
            } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) {
                  fprintf (fp, "PAL/H ");
                  curr_id = V4L2_STD_PAL_H;
            } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) {
                  fprintf (fp, "PAL/I ");
                  curr_id = V4L2_STD_PAL_I;
            } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) {
                  fprintf (fp, "PAL/D ");
                  curr_id = V4L2_STD_PAL_D;
            } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) {
                  fprintf (fp, "PAL/D1 ");
                  curr_id = V4L2_STD_PAL_D1;
            } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) {
                  fprintf (fp, "PAL/K ");
                  curr_id = V4L2_STD_PAL_K;
            } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) {
                  fprintf (fp, "PAL/M ");
                  curr_id = V4L2_STD_PAL_M;
            } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) {
                  fprintf (fp, "PAL/N ");
                  curr_id = V4L2_STD_PAL_N;
            } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) {
                  fprintf (fp, "PAL/Nc ");
                  curr_id = V4L2_STD_PAL_Nc;
            } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) {
                  fprintf (fp, "PAL/60 ");
                  curr_id = V4L2_STD_PAL_60;
            } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
                  fprintf (fp, "NTSC ");
                  curr_id = V4L2_STD_NTSC;
            } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) {
                  fprintf (fp, "NTSC/M ");
                  curr_id = V4L2_STD_NTSC_M;
            } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) {
                  fprintf (fp, "NTSC/M Jp ");
                  curr_id = V4L2_STD_NTSC_M_JP;
            } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) {
                  fprintf (fp, "NTSC 443 ");
                  curr_id = V4L2_STD_NTSC_443;
            } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) {
                  fprintf (fp, "NTSC/M Kr ");
                  curr_id = V4L2_STD_NTSC_M_KR;
            } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
                  fprintf (fp, "SECAM ");
                  curr_id = V4L2_STD_SECAM;
            } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) {
                  fprintf (fp, "SECAM/DK ");
                  curr_id = V4L2_STD_SECAM_DK;
            } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) {
                  fprintf (fp, "SECAM/B ");
                  curr_id = V4L2_STD_SECAM_B;
            } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) {
                  fprintf (fp, "SECAM/D ");
                  curr_id = V4L2_STD_SECAM_D;
            } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) {
                  fprintf (fp, "SECAM/G ");
                  curr_id = V4L2_STD_SECAM_G;
            } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) {
                  fprintf (fp, "SECAM/H ");
                  curr_id = V4L2_STD_SECAM_H;
            } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) {
                  fprintf (fp, "SECAM/K ");
                  curr_id = V4L2_STD_SECAM_K;
            } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) {
                  fprintf (fp, "SECAM/K1 ");
                  curr_id = V4L2_STD_SECAM_K1;
            } else if ( (id & V4L2_STD_SECAM_K3) == V4L2_STD_SECAM_K3) {
                  fprintf (fp, "SECAM/K3 ");
                  curr_id = V4L2_STD_SECAM_K3;
            } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) {
                  fprintf (fp, "SECAM/L ");
                  curr_id = V4L2_STD_SECAM_L;
            } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) {
                  fprintf (fp, "SECAM/Lc ");
                  curr_id = V4L2_STD_SECAM_LC;
            } else if ( (id & V4L2_STD_A2) == V4L2_STD_A2) {
                  fprintf (fp, "A2 ");
                  curr_id = V4L2_STD_A2;
            } else if ( (id & V4L2_STD_A2_A) == V4L2_STD_A2_A) {
                  fprintf (fp, "A2/A ");
                  curr_id = V4L2_STD_A2_A;
            } else if ( (id & V4L2_STD_A2_B) == V4L2_STD_A2_B) {
                  fprintf (fp, "A2/B ");
                  curr_id = V4L2_STD_A2_B;
            } else if ( (id & V4L2_STD_NICAM) == V4L2_STD_NICAM) {
                  fprintf (fp, "NICAM ");
                  curr_id = V4L2_STD_NICAM;
            } else if ( (id & V4L2_STD_NICAM_A) == V4L2_STD_NICAM_A) {
                  fprintf (fp, "NICAM/A ");
                  curr_id = V4L2_STD_NICAM_A;
            } else if ( (id & V4L2_STD_NICAM_B) == V4L2_STD_NICAM_B) {
                  fprintf (fp, "NICAM/B ");
                  curr_id = V4L2_STD_NICAM_B;
            } else if ( (id & V4L2_STD_AM) == V4L2_STD_AM) {
                  fprintf (fp, "AM ");
                  curr_id = V4L2_STD_AM;
            } else if ( (id & V4L2_STD_BTSC) == V4L2_STD_BTSC) {
                  fprintf (fp, "BTSC ");
                  curr_id = V4L2_STD_BTSC;
            } else if ( (id & V4L2_STD_EIAJ) == V4L2_STD_EIAJ) {
                  fprintf (fp, "EIAJ ");
                  curr_id = V4L2_STD_EIAJ;
            } else {
                  curr_id = 0;
                  break;
            }
            id &= ~curr_id;
      }
}

static void list_firmware_desc(FILE *fp, struct firmware_description *desc)
{
      fprintf(fp, "type: ");
      dump_firm_type(fp, desc->type);
      fprintf(fp, "(0x%08x), ", desc->type);
      if (desc->type & HAS_IF)
            fprintf(fp, "IF = %.2f MHz ", desc->int_freq/1000.0);
      fprintf(fp, "id: ");
      dump_firm_std(fp, desc->id);
      fprintf(fp, "(%016llx), ", desc->id);
      fprintf(fp, "size: %u\n", desc->size);
}

static void list_firmware(struct firmware *f, unsigned int dump, char *binfile)
{
      unsigned int i = 0;

      printf("firmware name:\t%s\n", f->name);
      printf("version:\t%d.%d (%u)\n", f->version >> 8, f->version & 0xff,
                                f->version);
      printf("standards:\t%u\n", f->nr_desc);
      for(i = 0; i < f->nr_desc; ++i) {
            printf("Firmware %2u, ", i);
            list_firmware_desc(stdout, &f->desc[i]);
            if (dump) {
                  printf("\t");
                  unsigned j, k = 0;
                  for (j = 0; j < f->desc[i].size; j++) {
                        printf("%02x", f->desc[i].data[j]);

                        k++;
                        if (k >= 32) {
                              printf("\n\t");
                              k = 0;
                        } else if (!(k % 2))
                              printf(" ");
                  }
                  printf("\n");
            }
            if (binfile) {
                  char name[strlen(binfile)+4], *p;
                  p = strrchr(binfile,'.');
                  if (p) {
                        int n = p - binfile;
                        strncpy(name, binfile, n);
                        sprintf(name + n, "%03i", i);
                        strcat(name, p);
                  } else {
                        strcpy(name, binfile);
                        sprintf(name + strlen(name), "%03i", i);
                  }
                  FILE *fp;

                  fp = fopen(name,"w");
                  if (!fp) {
                        perror("Opening file to write");
                        return;
                  }
                  fwrite(f->desc[i].data, f->desc[i].size, 1, fp);
                  fclose(fp);
            }
      }
}

static void add_standard(struct firmware* f, char* firmware_file, char* standard_file) {
      unsigned char* standard_data;
      unsigned int len, i;
      struct firmware_description desc;

      create_standard_data(standard_file, &standard_data, &len);
      if(!standard_data) {
            fprintf(stderr, "Couldn't create the firmware standard data.\n");
            return;
      }
      desc.id = 0;
      desc.type = 0;
      desc.size = len;
      desc.data = standard_data;
      add_firmware_description(f, &desc);
      write_firmware_file(firmware_file, f);
}

static void delete_standard(struct firmware* f, char* firmware_file, __u16 i) {
      delete_firmware_description(f, i);
      write_firmware_file(firmware_file, f);
}

static void set_standard_type(struct firmware* f, char* firmware_file, __u16 i, __u32 type) {
      if(i > f->nr_desc) {
            return;
      }
      f->desc[i].type = type;
      write_firmware_file(firmware_file, f);
}

static void set_standard_id(struct firmware* f, char* firmware_file, __u16 i, __u32 id) {
      if(i > f->nr_desc) {
            return;
      }
      f->desc[i].id = id;
      write_firmware_file(firmware_file, f);
}

struct chunk_hunk;

struct chunk_hunk {
      unsigned char *data;
      long pos;
      int size;
      int need_fix_endian;
      int hint_method;
      struct chunk_hunk *next;
};

static int seek_chunks(struct chunk_hunk *fhunk,
            unsigned char *seek, unsigned char *endp, /* File to seek */
            unsigned char *fdata, unsigned char *endf)      /* Firmware */
{
      unsigned char *fpos, *p, *p2, *lastp;
      int rc, fsize;
      unsigned char *temp_data;
      struct chunk_hunk *hunk = fhunk;
      /* Method 3 vars */
      static unsigned char *base_start = 0;
      int ini_sig = 8, sig_len = 14, end_sig = 8;

      /* Method 1a: Seek for a complete firmware */
      for (p = seek; p < endp; p++) {
            fpos = p;
            for (p2 = fdata; p2 < endf; p2++, fpos++) {
                  if (*fpos != *p2)
                        break;
            }
            if (p2 == endf) {
                  hunk->data = NULL;
                  hunk->pos = p - seek;
                  hunk->size = endf - fdata;
                  hunk->next = NULL;
                  hunk->need_fix_endian = 0;
                  hunk->hint_method = 0;
                  return 1;
            }
      }

      fsize = endf - fdata;
      temp_data = malloc(fsize);
      memcpy(temp_data, fdata, fsize);

      /* Try again, changing endian */
      for (p2 = temp_data; p2 < temp_data + fsize;) {
            unsigned char c;
            int size = *p2 + (*(p2 + 1) << 8);
            c = *p2;
            *p2 = *(p2 + 1);
            *(p2 + 1) = c;
            p2+=2;
            if ((size > 0) && (size < 0x8000))
                  p2 += size;
      }

      /* Method 1b: Seek for a complete firmware with changed endians */
      for (p = seek; p < endp; p++) {
            fpos = p;
            for (p2 = temp_data; p2 < temp_data + fsize; p2++, fpos++) {
                  if (*fpos != *p2)
                        break;
            }
            if (p2 == temp_data + fsize) {
                  hunk->data = NULL;
                  hunk->pos = p - seek;
                  hunk->size = endf - fdata;
                  hunk->next = NULL;
                  hunk->need_fix_endian = 1;
                  hunk->hint_method = 0;
                  return 1;
            }
      }

      free(temp_data);

      /* Method 2: seek for base firmware */
      if (!base_start)
            base_start = seek;

      /* Skip if firmware is not a base firmware */
      if (endf - fdata < 1000)
            goto method3;

      for (p = base_start; p < endp; p++) {
            fpos = p;
            for (p2 = fdata + ini_sig;
                 p2 < fdata + ini_sig + sig_len; p2++,
                 fpos++) {
                  if (*fpos != *p2)
                        break;
            }
            if (p2 == fdata + ini_sig + sig_len) {
                  base_start = p - ini_sig;

                  p = memmem (base_start, endp-base_start,
                        temp_data + fsize - end_sig, end_sig);

                  if (p)
                        p = memmem (p + end_sig, endp-base_start,
                              temp_data + fsize - end_sig, end_sig);

                  if (!p) {
                        printf("Found something that looks like a firmware start at %lx\n",
                              (long)(base_start - seek));

                        base_start += ini_sig + sig_len;
                        goto method3;
                  }

                  p += end_sig;

                  printf("Found firmware at %lx, size = %ld\n",
                        (long)(base_start - seek),
                        (long)(p - base_start));

                  hunk->data = NULL;
                  hunk->pos = base_start - seek;
                  hunk->size = p - base_start;
                  hunk->next = NULL;
                  hunk->need_fix_endian = 1;
                  hunk->hint_method = 3;

                  base_start = p;

                  return 2;
            }
      }

method3:
#if 0
      /* Method 3: Seek for each firmware chunk */
      p = seek;
      for (p2 = fdata; p2 < endf;) {
            int size = *p2 + (*(p2 + 1) << 8);

            /* Encode size/reset/sleep directly */
            hunk->size = 2;
            hunk->data = malloc(hunk->size);
            memcpy(hunk->data, p2, hunk->size);
            hunk->pos = -1;
            hunk->next = calloc(1, sizeof(hunk));
            hunk->need_fix_endian = 0;
            hunk->hint_method = 0;

            hunk = hunk->next;
            p2 += 2;

            if ((size > 0) && (size < 0x8000)) {
                  unsigned char *ep;
                  int   found = 0;
                  ep = p2 + size;
                  ///////////////////
                  for (; p < endp; p++) {
                        unsigned char *p3;
                        fpos = p;
                        for (p3 = p2; p3 < ep; p3++, fpos++)
                              if (*fpos != *p3)
                                    break;
                        if (p3 == ep) {
                              found = 1;
                              hunk->pos = p - seek;
                              hunk->size = size;
                              hunk->next = calloc(1, sizeof(hunk));
                              hunk->need_fix_endian = 0;
                              hunk->hint_method = 0;

                              hunk = hunk->next;
                              break;
                        }
                  }
                  if (!found) {
                        goto not_found;
                  }
                  p2 += size;
            }
      }
      return 3;
#endif
not_found:
      memset(fhunk, 0, sizeof(struct chunk_hunk));
      printf("Couldn't find firmware\n");
      return 0;

      /* Method 4: Seek for first firmware chunks */
#if 0
seek_next:
      for (p = seek; p < endp; p++) {
            fpos = p;
            for (p2 = fdata; p2 < endf; p2++, fpos++) {
                  if (*fpos != *p2)
                        break;
            }
            if (p2 > fdata + 3) {
                  int i = 0;
                  printf("Found %ld equal bytes at %06x:\n",
                        p2 - fdata, p - seek);
                  fpos = p;
                  lastp = fpos;
                  for (p2 = fdata; p2 < endf; p2++, fpos++) {
                        if (*fpos != *p2)
                              break;
                        printf("%02x ",*p2);
                  }
                  for (i=0; p2 < endf && i <5 ; p2++, fpos++, i++) {
                        printf("%02x(%02x) ",*p2 , *fpos);
                  }
                  printf("\n");
                  /* Seek for the next chunk */
                  fdata = p2;

                  if (fdata == endf) {
                        printf ("Found all chunks.\n");
                        return 4;
                  }
            }
      }

      printf ("NOT FOUND: %02x\n", *fdata);
      fdata++;
      goto seek_next;
#endif
}

static void seek_firmware(struct firmware *f, char *seek_file, char *write_file) {
      unsigned int i = 0, j, nfound = 0;
      long size, rd = 0;
      unsigned char *seek, *p, *endp, *p2, *endp2, *fpos;
      /*FIXME: Calculate it, instead of using a hardcode value */
      char *md5 = "0e44dbf63bb0169d57446aec21881ff2";
      FILE *fp;

      struct chunk_hunk hunks[f->nr_desc];
      memset (hunks, 0, sizeof(struct chunk_hunk) * f->nr_desc);

      fp=fopen(seek_file, "r");
      if (!fp) {
            perror("Opening seek file");
            exit(-1);
      }
      fseek(fp, 0L, SEEK_END);
      size = ftell(fp);
      rewind(fp);
      seek = malloc(size);
      p = seek;

      do {
            i = fread(p, 1, 16768, fp);
            if (i > 0) {
                  rd += i;
                  p += i;
            }
      } while (i > 0);

      fclose(fp);

      if (rd != size) {
            fprintf(stderr, "Error while reading the seek file: "
                        "should read %ld, instead of %ld ", size, rd);
            exit (-1);
      }
      endp = p;

      printf("firmware name:\t%s\n", f->name);
      printf("version:\t%d.%d (%u)\n", f->version >> 8, f->version & 0xff,
                                f->version);
      printf("number of standards:\t%u\n", f->nr_desc);
      for(i = 0; i < f->nr_desc; ++i) {
            int found;

            endp2 = f->desc[i].data + f->desc[i].size;

            found = seek_chunks (&hunks[i],
                       seek, endp, f->desc[i].data, endp2);

            if (!found) {
                  printf("NOT FOUND: Firmware %d ", i);
                  list_firmware_desc(stdout, &f->desc[i]);
            } else {
                  nfound++;
                  printf("Found with method %d: Firmware %d ", found, i);
                  if (found == 2)
                        f->desc[i].size = hunks[i].size;
                  list_firmware_desc(stdout, &f->desc[i]);
            }
      }
      printf ("Found %d complete firmwares\n", nfound);

      if (!write_file)
            return;

      fp = fopen(write_file, "w");
      if (!fp) {
            perror("Writing firmware file");
            exit(-1);
      }

      fprintf(fp, "%s", extract_header);
      for (i = 0, j = -1; i < f->nr_desc; i++) {
            struct chunk_hunk *hunk = &hunks[i];

            if (!hunk->size)
                  continue;
            j++;

            if (hunk->hint_method)
                  fprintf(fp, "\n\t#\n\t# Guessed format ");

            fprintf(fp, "\n\t#\n\t# Firmware %d, ", j);
            list_firmware_desc(fp, &f->desc[i]);
            fprintf(fp, "\t#\n\n");

            fprintf(fp, "\twrite_le32(0x%08x);\t\t\t# Type\n",
                  f->desc[i].type);
            fprintf(fp, "\twrite_le64(0x%08Lx, 0x%08Lx);\t# ID\n",
                  f->desc[i].id>>32, f->desc[i].id & 0xffffffff);
            if (f->desc[i].type & HAS_IF)
                  fprintf(fp, "\twrite_le16(%d);\t\t\t# IF\n",
                        f->desc[i].int_freq);
            fprintf(fp, "\twrite_le32(%d);\t\t\t# Size\n",
                  f->desc[i].size);
            while (hunk) {
                  if (hunk->data) {
                        int k;
                        fprintf(fp, "\tsyswrite(OUTFILE, ");
                        for (k = 0; k < hunk->size; k++) {
                              fprintf(fp, "chr(%d)", hunk->data[k]);
                              if (k < hunk->size-1)
                                    fprintf(fp,".");
                        }
                        fprintf(fp,");\n");
                  } else {
                        if (!hunk->size)
                              break;

                        if (hunk->need_fix_endian)
                              fprintf(fp, write_hunk_fix_endian,
                                    hunk->pos, hunk->size);
                        else
                              fprintf(fp, write_hunk,
                                    hunk->pos, hunk->size);
                  }
                  hunk = hunk->next;
            }
      }

      fprintf(fp, end_extract, seek_file, md5, "xc3028-v27.fw",
            f->name, f->version, nfound);
}

static void print_usage(void)
{
      printf("firmware-tool usage:\n");
      printf("\t firmware-tool --list [--dump] [--write <bin-file>] <firmware-file>\n");
      printf("\t firmware-tool --add <firmware-dump> <firmware-file>\n");
      printf("\t firmware-tool --delete <index> <firmware-file>\n");
      printf("\t firmware-tool --type <type> --index <i> <firmware-file>\n");
      printf("\t firmware-tool --id <type> --index <i> <firmware-file>\n");
      printf("\t firmware-tool --seek <seek-file> [--write <write-file>] <firmware-file>\n");
}

int main(int argc, char* argv[])
{
      int c;
      int nr_args;
      unsigned int action = 0, dump = 0;
      char* firmware_file, *file = NULL, *nr_str = NULL, *index_str = NULL;
      char *seek_file = NULL, *write_file = NULL;
      struct firmware *f;
      __u64 nr;

      while(1) {
            static struct option long_options[] = {
                  {"list",     no_argument,      0, 'l'},
                  {"add",     required_argument, 0, 'a'},
                  {"delete",  required_argument, 0, 'd'},
                  {"type",  required_argument, 0, 't'},
                  {"id",  required_argument, 0, 's'},
                  {"index",  required_argument, 0, 'i'},
                  {"seek", required_argument, 0, 'k'},
                  {"write", required_argument , 0, 'w'},
                  {"dump", no_argument, 0, 'm'},
                  {0, 0, 0, 0}
            };
            int option_index = 0;

            c = getopt_long(argc, argv, "", long_options, &option_index);

            if (c == -1) {
                  break;
            }

            switch(c) {
                  case 'l':
                        puts("list action\n");
                        if(action != 0) {
                              printf("Please specify only one action.\n");
                        }
                        action |= LIST_ACTION;
                        break;
                  case 'm':
                        dump = 1;
                        break;
                  case 'a':
                        puts("add action\n");
                        if(action != 0) {
                              printf("Please specify only one action.\n");
                        }
                        action |= ADD_ACTION;
                        file = optarg;
                        break;
                  case 'd':
                        puts("delete action\n");
                        if(action != 0) {
                              printf("Please specify only one action.\n");
                        }
                        action |= DELETE_ACTION;
                        nr_str = optarg;
                        break;
                  case 't':
                        puts("set-type action\n");
                        if(action != 0) {
                              printf("Please specify only one action.\n");
                        }
                        action |= SET_TYPE_ACTION;
                        nr_str = optarg;
                        break;
                  case 's':
                        puts("set-id action\n");
                        if(action != 0) {
                              printf("Please specify only one action.\n");
                        }
                        action |= SET_ID_ACTION;
                        nr_str = optarg;
                        break;
                  case 'i':
                        index_str = optarg;
                        break;
                  case 'k':
                        puts("seek firmwares\n");
                        action = SEEK_FIRM_ACTION;
                        seek_file = optarg;
                        break;
                  case 'w':
                        write_file = optarg;
                        break;
                  default:
                        print_usage();
                        return 0;
            }
      }

      nr_args = (action == LIST_ACTION) ? 1 : 1;
      if(!(optind + nr_args == argc)) {
            printf("Wrong number of arguments!\n\n");
            print_usage();
            return -1;
      }

      if(!action) {
            printf("Please specify an action!\n\n");
            print_usage();
            return -1;
      }

      firmware_file = argv[optind];

      printf("firmware file name: %s\n", firmware_file);

      f = read_firmware_file(firmware_file);
      if(!f) {
            printf("Couldn't read the firmware file!\n");
            return -1;
      }

      switch(action) {
            case LIST_ACTION:
                  list_firmware(f, dump, write_file);
            break;

            case ADD_ACTION:
                  add_standard(f, firmware_file, file);
            break;

            case DELETE_ACTION:
                  delete_standard(f, firmware_file, strtoul(nr_str, NULL, 10));
            break;

            case SET_TYPE_ACTION:
                  set_standard_type(f, firmware_file, strtoul(index_str, NULL, 10), strtoul(nr_str, NULL, 10));
            break;

            case SET_ID_ACTION:
                  set_standard_id(f, firmware_file, strtoul(index_str, NULL, 10), strtoul(nr_str, NULL, 10));

            case SEEK_FIRM_ACTION:
                  seek_firmware(f, seek_file, write_file);
            break;
      }
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index