| /* |
| 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@kernel.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; |
| }; |
| |
| #if 0 |
| 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); |
| } |
| #endif |
| |
| 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); |
| if(f->desc) { |
| unsigned int i = 0; |
| for(i = 0; i < f->nr_desc; ++ i) { |
| free(f->desc[i].data); |
| } |
| } |
| free(f->desc); |
| 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, 0644); |
| 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; |
| 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; |
| int 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; |
| not_found: |
| #endif |
| 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; |
| unsigned char *lastp; |
| 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, *endp2; |
| /*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; |
| |
| 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; |
| } |