diff options
author | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-09-14 11:42:57 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-09-14 11:53:09 -0300 |
commit | 5fec32581f7c471e58119190e98aa3e8c8b79b6d (patch) | |
tree | 2a551caa2603ce755a091b97c0865bb3afd648d9 /contrib | |
parent | e274ad3b269e1accb1ee37d4f997a63b77a9d483 (diff) |
Move decode_tm6000 and xc3028-firmware to contrib
Those are actually ancillary development tools, and aren't
used by end-users. So, move them to contrib/ dir.
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/Makefile.am | 4 | ||||
-rw-r--r-- | contrib/decode_tm6000/.gitignore | 1 | ||||
-rw-r--r-- | contrib/decode_tm6000/Makefile.am | 4 | ||||
-rw-r--r-- | contrib/decode_tm6000/decode_tm6000.c | 365 | ||||
-rw-r--r-- | contrib/xc3028-firmware/.gitignore | 1 | ||||
-rw-r--r-- | contrib/xc3028-firmware/Makefile.am | 14 | ||||
-rw-r--r-- | contrib/xc3028-firmware/README | 3 | ||||
-rw-r--r-- | contrib/xc3028-firmware/extract_head.h | 161 | ||||
-rw-r--r-- | contrib/xc3028-firmware/firmware-tool.c | 1106 | ||||
-rw-r--r-- | contrib/xc3028-firmware/standards.c | 151 | ||||
-rw-r--r-- | contrib/xc3028-firmware/standards.h | 25 | ||||
-rw-r--r-- | contrib/xc3028-firmware/tuner-xc2028-types.h | 141 |
12 files changed, 1975 insertions, 1 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am index d8f15a68..46be290a 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,5 +1,5 @@ if LINUX_OS -SUBDIRS = test +SUBDIRS = test xc3028-firmware else SUBDIRS = freebsd endif @@ -12,6 +12,8 @@ if WITH_GCONV SUBDIRS += gconv endif +SUBDIRS += decode_tm6000 + EXTRA_DIST = \ parse-sniffusb2.pl \ parse_tcpdump_log.pl \ diff --git a/contrib/decode_tm6000/.gitignore b/contrib/decode_tm6000/.gitignore new file mode 100644 index 00000000..7c881113 --- /dev/null +++ b/contrib/decode_tm6000/.gitignore @@ -0,0 +1 @@ +decode_tm6000 diff --git a/contrib/decode_tm6000/Makefile.am b/contrib/decode_tm6000/Makefile.am new file mode 100644 index 00000000..ac4e85e5 --- /dev/null +++ b/contrib/decode_tm6000/Makefile.am @@ -0,0 +1,4 @@ +bin_PROGRAMS = decode_tm6000 +decode_tm6000_SOURCES = decode_tm6000.c +decode_tm6000_LDADD = ../libv4l2util/libv4l2util.la +decode_tm6000_LDFLAGS = $(ARGP_LIBS) diff --git a/contrib/decode_tm6000/decode_tm6000.c b/contrib/decode_tm6000/decode_tm6000.c new file mode 100644 index 00000000..4bffbdd6 --- /dev/null +++ b/contrib/decode_tm6000/decode_tm6000.c @@ -0,0 +1,365 @@ +/* + decode_tm6000.c - decode multiplexed format from TM5600/TM6000 USB + + Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA. + */ +#include "../libv4l2util/v4l2_driver.h" +#include <stdio.h> +#include <string.h> +#include <argp.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> + +const char *argp_program_version="decode_tm6000 version 0.0.1"; +const char *argp_program_bug_address="Mauro Carvalho Chehab <mchehab@infradead.org>"; +const char doc[]="Decodes tm6000 proprietary format streams"; +const struct argp_option options[] = { + {"verbose", 'v', 0, 0, "enables debug messages", 0}, + {"device", 'd', "DEV", 0, "uses device for reading", 0}, + {"output", 'o', "FILE", 0, "outputs raw stream to a file", 0}, + {"input", 'i', "FILE", 0, "parses a file, instead of a device", 0}, + {"freq", 'f', "Freq", 0, "station frequency, in MHz (default is 193.25)", 0}, + {"nbufs", 'n', "quant",0, "number of video buffers", 0}, + {"audio", 'a', 0, 0, "outputs audio on stdout", 0}, + {"read", 'r', 0, 0, "use read() instead of mmap method", 0}, + { 0, 0, 0, 0, 0, 0 } +}; + +static char outbuf[692224]; +static int debug=0, audio=0, use_mmap=1, nbufs=4; +static float freq_mhz=193.25; +static char *devicename="/dev/video0"; +static char *filename=NULL; +static enum { + NORMAL, + INPUT, + OUTPUT +} mode = NORMAL; + +static FILE *fout; + +//const char args_doc[]="ARG1 ARG2"; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'a': + audio++; + break; + case 'r': + use_mmap=0; + break; + case 'v': + debug++; + break; + case 'd': + devicename=arg; + break; + case 'i': + case 'o': + if (mode!=NORMAL) { + argp_error(state,"You can't use input/output options simultaneously.\n"); + break; + } + if (key=='i') + mode=INPUT; + else + mode=OUTPUT; + + filename=arg; + break; + case 'f': + freq_mhz=atof(arg); + break; + case 'n': + nbufs=atoi(arg); + if (nbufs<2) + nbufs=2; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + .options=options, + .parser=parse_opt, + .args_doc=NULL, + .doc=doc, +}; + +#define TM6000_URB_MSG_LEN 180 +enum { + TM6000_URB_MSG_VIDEO=1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +const char *tm6000_msg_type[]= { + "unknown(0)", //0 + "video", //1 + "audio", //2 + "vbi", //3 + "pts", //4 + "err", //5 + "unknown(6)", //6 + "unknown(7)", //7 +}; + +#define dprintf(fmt,arg...) \ + if (debug) fprintf(stderr, fmt, ##arg) + +static int recebe_buffer (struct v4l2_buffer *v4l2_buf, struct v4l2_t_buf *buf) +{ + dprintf("Received %zd bytes\n", buf->length); +fflush(stdout); + memcpy (outbuf,buf->start,buf->length); + return buf->length; +} + + +static int prepare_read (struct v4l2_driver *drv) +{ + struct v4l2_format fmt; + double freq; + int rc; + + memset (drv,0,sizeof(*drv)); + + if (v4l2_open (devicename, 1,drv)<0) { + perror ("Error opening dev"); + return -1; + } + + memset (&fmt,0,sizeof(fmt)); + + uint32_t pixelformat=V4L2_PIX_FMT_TM6000; + + if (v4l2_gettryset_fmt_cap (drv,V4L2_SET,&fmt, 720, 480, + pixelformat,V4L2_FIELD_ANY)) { + perror("set_input to tm6000 raw format"); + return -1; + } + + if (freq_mhz) { + freq=freq_mhz * 1000 * 1000; + rc=v4l2_getset_freq (drv,V4L2_SET, &freq); + if (rc<0) + printf ("Cannot set freq to %.3f MHz\n",freq_mhz); + } + + if (use_mmap) { + printf("Preparing for receiving frames on %d buffers...\n",nbufs); + fflush (stdout); + + rc=v4l2_mmap_bufs(drv, nbufs); + if (rc<0) { + printf ("Cannot mmap %d buffers\n",nbufs); + return -1; + } + +// v4l2_stop_streaming(&drv); + rc=v4l2_start_streaming(drv); + if (rc<0) { + printf ("Cannot start streaming\n"); + return -1; + } + } + printf("Waiting for frames...\n"); + + return 0; +} + +static int read_stream (struct v4l2_driver *drv, int fd) +{ + if (use_mmap) { + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO (&fds); + FD_SET (fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select (fd + 1, &fds, NULL, NULL, &tv); + if (-1 == r) { + if (EINTR == errno) { + perror ("select"); + return -errno; + } + } + + if (0 == r) { + fprintf (stderr, "select timeout\n"); + return -errno; + } + + return v4l2_rcvbuf(drv, recebe_buffer); + } else { + int size=read(fd, outbuf, sizeof(outbuf)); + return size; + } + + return 0; +} + +static int read_char (struct v4l2_driver *drv, int fd) +{ + static int sizebuf=0; + static unsigned char *p=NULL; + unsigned char c; + + if (sizebuf<=0) { + sizebuf=read_stream(drv,fd); + if (sizebuf<=0) + return -1; + p=(unsigned char *)outbuf; + } + c=*p; + p++; + sizebuf--; + + return c; +} + + +int main (int argc, char*argv[]) +{ + int fd; + unsigned int i; + unsigned char buf[TM6000_URB_MSG_LEN], img[720*2*480]; + unsigned int cmd, size, field, block, line, pos=0; + unsigned long header=0; + int linesize=720*2,skip=0; + struct v4l2_driver drv; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (mode!=INPUT) { + if (prepare_read (&drv)<0) + return -1; + fd=drv.fd; + } else { + /*mode == INPUT */ + + fd=open(filename,O_RDONLY); + if (fd<0) { + perror ("error opening a file for parsing"); + return -1; + } + dprintf("file %s opened for parsing\n",filename); + use_mmap=0; + } + + if (mode==OUTPUT) { + fout=fopen(filename,"w"); + if (!fout) { + perror ("error opening a file to write"); + return -1; + } + dprintf("file %s opened for output\n",filename); + + do { + size=read_stream (&drv,fd); + + if (size<=0) { + close (fd); + return -1; + } + dprintf("writing %d bytes\n",size); + fwrite(outbuf,1, size,fout); +// fflush(fout); + } while (1); + } + + + while (1) { + skip=0; + header=0; + do { + int c; + c=read_char (&drv,fd); + if (c<0) { + perror("read"); + return -1; + } + + header=(header>>8)&0xffffff; + header=header|(c<<24); + skip++; + } while ( (((header>>24)&0xff) != 0x47) ); + + /* split the header fields */ + size = (((header & 0x7e)<<1) -1) * 4; + block = (header>>7) & 0xf; + field = (header>>11) & 0x1; + line = (header>>12) & 0x1ff; + cmd = (header>>21) & 0x7; + + /* Read the remaining buffer */ + for (i=0;i<sizeof(buf);i++) { + int c; + c=read_char (&drv,fd); + if (c<0) { + perror("read"); + return -1; + } + buf[i]=c; + } + + /* FIXME: Mounts the image as field0+field1 + * It should, instead, check if the user selected + * entrelaced or non-entrelaced mode + */ + pos=((line<<1)+field)*linesize+ + block*TM6000_URB_MSG_LEN; + + /* Prints debug info */ + dprintf("0x%08x (skip %d), %s size=%d, line=%d, field=%d, block=%d\n", + (unsigned int)header, skip, + tm6000_msg_type[cmd], + size, line, field, block); + + /* Don't allow to write out of the buffer */ + if (pos+sizeof(buf) > sizeof(img)) + cmd = TM6000_URB_MSG_ERR; + + /* handles each different URB message */ + switch(cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + memcpy(buf,&img[pos],sizeof(buf)); + case TM6000_URB_MSG_AUDIO: + if (audio) + fwrite(buf,sizeof(buf),1,stdout); +// case TM6000_URB_MSG_VBI: +// case TM6000_URB_MSG_PTS: + break; + } + } + close(fd); + return 0; +} diff --git a/contrib/xc3028-firmware/.gitignore b/contrib/xc3028-firmware/.gitignore new file mode 100644 index 00000000..e9c07c4f --- /dev/null +++ b/contrib/xc3028-firmware/.gitignore @@ -0,0 +1 @@ +firmware-tool diff --git a/contrib/xc3028-firmware/Makefile.am b/contrib/xc3028-firmware/Makefile.am new file mode 100644 index 00000000..43ff8b12 --- /dev/null +++ b/contrib/xc3028-firmware/Makefile.am @@ -0,0 +1,14 @@ +noinst_PROGRAMS = firmware-tool + +firmware_tool_SOURCES = firmware-tool.c standards.c extract_head.h standards.h tuner-xc2028-types.h + +EXTRA_DIST = README + +# custom targets + +sync-with-kernel: + @if [ ! -f $(KERNEL_DIR)/drivers/media/tuners/tuner-xc2028-types.h ]; then \ + echo "Error you must set KERNEL_DIR to point to an extracted kernel source dir"; \ + exit 1; \ + fi + cp -a $(KERNEL_DIR)/drivers/media/tuners/tuner-xc2028-types.h $(srcdir)/ diff --git a/contrib/xc3028-firmware/README b/contrib/xc3028-firmware/README new file mode 100644 index 00000000..85df42a7 --- /dev/null +++ b/contrib/xc3028-firmware/README @@ -0,0 +1,3 @@ +Firmware dumps must be in ASCII format and each line corresponds to an I2C instruction sent to the chip. Each line +must either be a special instruction like RESET_CLK, RESET_TUNER (see standards.c) or be a sequence of bytes +represented in hexadecimal format xx separated by a space character ' '. diff --git a/contrib/xc3028-firmware/extract_head.h b/contrib/xc3028-firmware/extract_head.h new file mode 100644 index 00000000..373ac78b --- /dev/null +++ b/contrib/xc3028-firmware/extract_head.h @@ -0,0 +1,161 @@ +char *extract_header = "#!/usr/bin/perl\n" + "#use strict;\n" + "use IO::Handle;\n" + "\n" + "my $debug=0;\n" + "\n" + "sub verify ($$)\n" + "{\n" + " my ($filename, $hash) = @_;\n" + " my ($testhash);\n" + "\n" + " if (system(\"which md5sum > /dev/null 2>&1\")) {\n" + " die \"This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\\n\";\n" + " }\n" + "\n" + " open(CMD, \"md5sum \".$filename.\"|\");\n" + " $testhash = <CMD>;\n" + " $testhash =~ /([a-zA-Z0-9]*)/;\n" + " $testhash = $1;\n" + " close CMD;\n" + " die \"Hash of extracted file does not match (found $testhash, expected $hash!\\n\" if ($testhash ne $hash);\n" + "}\n" + "\n" + "sub get_hunk ($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my ($chunklength, $buf, $rcount, $out);\n" + "\n" + " sysseek(INFILE, $offset, SEEK_SET);\n" + " while ($length > 0) {\n" + " # Calc chunk size\n" + " $chunklength = 2048;\n" + " $chunklength = $length if ($chunklength > $length);\n" + "\n" + " $rcount = sysread(INFILE, $buf, $chunklength);\n" + " die \"Ran out of data\\n\" if ($rcount != $chunklength);\n" + " $out .= $buf;\n" + " $length -= $rcount;\n" + " }\n" + " return $out;\n" + "}\n" + "\n" + "sub write_le16($)\n" + "{\n" + " my $val = shift;\n" + " my $msb = ($val >> 8) &0xff;\n" + " my $lsb = $val & 0xff;\n" + "\n" + " syswrite(OUTFILE, chr($lsb).chr($msb));\n" + "}\n" + "\n" + "sub write_le32($)\n" + "{\n" + " my $val = shift;\n" + " my $l3 = ($val >> 24) & 0xff;\n" + " my $l2 = ($val >> 16) & 0xff;\n" + " my $l1 = ($val >> 8) & 0xff;\n" + " my $l0 = $val & 0xff;\n" + "\n" + " syswrite(OUTFILE, chr($l0).chr($l1).chr($l2).chr($l3));\n" + "}\n" + "\n" + "sub write_le64($$)\n" + "{\n" + " my $msb_val = shift;\n" + " my $lsb_val = shift;\n" + " my $l7 = ($msb_val >> 24) & 0xff;\n" + " my $l6 = ($msb_val >> 16) & 0xff;\n" + " my $l5 = ($msb_val >> 8) & 0xff;\n" + " my $l4 = $msb_val & 0xff;\n\n" + " my $l3 = ($lsb_val >> 24) & 0xff;\n" + " my $l2 = ($lsb_val >> 16) & 0xff;\n" + " my $l1 = ($lsb_val >> 8) & 0xff;\n" + " my $l0 = $lsb_val & 0xff;\n" + "\n" + " syswrite(OUTFILE,\n" + " chr($l0).chr($l1).chr($l2).chr($l3).\n" + " chr($l4).chr($l5).chr($l6).chr($l7));\n" + "}\n" + "\n" + "sub write_hunk($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my $out = get_hunk($offset, $length);\n" + "\n" + " printf \"(len %d) \",$length if ($debug);\n" + "\n" + " for (my $i=0;$i<$length;$i++) {\n" + " printf \"%02x \",ord(substr($out,$i,1)) if ($debug);\n" + " }\n" + " printf \"\\n\" if ($debug);\n" + "\n" + " syswrite(OUTFILE, $out);\n" + "}\n" + "\n" + "sub write_hunk_fix_endian($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my $out = get_hunk($offset, $length);\n" + "\n" + " printf \"(len_fix %d) \",$length if ($debug);\n" + "\n" + " for (my $i=0;$i<$length;$i++) {\n" + " printf \"%02x \",ord(substr($out,$i,1)) if ($debug);\n" + " }\n" + " printf \"\\n\" if ($debug);\n" + "\n" + " $i=0;\n" + " while ($i<$length) {\n" + " my $size = ord(substr($out,$i,1))*256+ord(substr($out,$i+1,1));\n" + " syswrite(OUTFILE, substr($out,$i+1,1));\n" + " syswrite(OUTFILE, substr($out,$i,1));\n" + " $i+=2;\n" + " if ($size>0 && $size <0x8000) {\n" + " for (my $j=0;$j<$size;$j++) {\n" + " syswrite(OUTFILE, substr($out,$j+$i,1));\n" + " }\n" + " $i+=$size;\n" + " }\n" + " }\n" + "}\n" + "\n" + "sub main_firmware($$$$)\n" + "{\n" + " my $out;\n" + " my $j=0;\n" + " my $outfile = shift;\n" + " my $name = shift;\n" + " my $version = shift;\n" + " my $nr_desc = shift;\n" + "\n" + " for ($j = length($name); $j <32; $j++) {\n" + " $name = $name.chr(0);\n" + "}\n\n" + " open OUTFILE, \">$outfile\";\n" + " syswrite(OUTFILE, $name);\n" + " write_le16($version);\n" + " write_le16($nr_desc);\n"; + +char *write_hunk = "\twrite_hunk(%d, %d);\n"; +char *write_hunk_fix_endian = "\twrite_hunk_fix_endian(%d, %d);\n"; + +// Parameters: file windows filename, hash, outfile, version, name +char *end_extract = "}\n\nsub extract_firmware {\n" + " my $sourcefile = \"%s\";\n" + " my $hash = \"%s\";\n" + " my $outfile = \"%s\";\n" + " my $name = \"%s\";\n" + " my $version = %d;\n" + " my $nr_desc = %d;\n" + " my $out;\n" + "\n" + " verify($sourcefile, $hash);\n" + "\n" + " open INFILE, \"<$sourcefile\";\n" + " main_firmware($outfile, $name, $version, $nr_desc);\n" + " close INFILE;\n" + "}\n" + "\n" + "extract_firmware;\n" + "printf \"Firmwares generated.\\n\";\n"; diff --git a/contrib/xc3028-firmware/firmware-tool.c b/contrib/xc3028-firmware/firmware-tool.c new file mode 100644 index 00000000..969d17a9 --- /dev/null +++ b/contrib/xc3028-firmware/firmware-tool.c @@ -0,0 +1,1106 @@ +/* + 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; +}; + +#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; +} diff --git a/contrib/xc3028-firmware/standards.c b/contrib/xc3028-firmware/standards.c new file mode 100644 index 00000000..f719a881 --- /dev/null +++ b/contrib/xc3028-firmware/standards.c @@ -0,0 +1,151 @@ +/* + Xceive XC2028/3028 tuner module firmware manipulation tool + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + 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 "standards.h" + +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <asm/byteorder.h> +#include <asm/types.h> + +#define MAX(a,b) ((a) >= (b) ? (a) : (b)) + +struct vector { + unsigned char* data; + unsigned int size; +}; + +static struct vector* alloc_vector(unsigned int size) { + struct vector *v = malloc(sizeof(*v)); + v->data = malloc(size); + v->size = size; + return v; +} + +static void free_vector(struct vector* v) { + free(v->data); + free(v); +} + +static void enlarge_vector(struct vector* v, unsigned int new_size) { + unsigned char *n_data; + unsigned int old_size = v->size; + + v->size = MAX(v->size, new_size); + n_data = malloc(v->size); + memcpy(n_data, v->data, old_size); + free(v->data); + v->data = n_data; +} + +static void copy_vector(struct vector *v, unsigned int i, + unsigned char* ptr, unsigned int len) { + if(i + len > v->size) { + enlarge_vector(v, MAX(2 * v->size, i + len)); + } + memcpy(v->data + i, ptr, len); +} + +static void write_vector8(struct vector *v, unsigned int i, __u8 value) { + __u8 buf[1]; + + buf[0] = value; + copy_vector(v, i, buf, 1); +} + +static void write_vector16(struct vector *v, unsigned int i, __u16 value) { + __u8 buf[2]; + + buf[0] = value & 0xff; + buf[1] = value >> 8; + copy_vector(v, i, buf, 2); +} + +static const char reset_tuner_str[] = "RESET_TUNER"; +static const char reset_clk_str[] = "RESET_CLK"; + +void create_standard_data(char* filename, unsigned char** data, unsigned int *r_len) { + FILE *file; + char* line = NULL; + ssize_t r = 0; + size_t len = 0; + struct vector *v = alloc_vector(1); + unsigned int v_i = 0; + + if (!(file = fopen(filename, "r"))) { + perror("Cannot open the firmware standard file.\n"); + *data = NULL; + } + + while ((r = getline(&line, &len, file)) != -1) { + unsigned int i = 0; + unsigned int val, count = 0; + unsigned int values[len]; + + printf("read line \"%s\"\n", line); + + if(len >= 9 && memcmp(reset_clk_str, line, strlen(reset_clk_str) - 1) == 0) { + printf("adding RESET_CLK\n"); + write_vector16(v, v_i, (__u16) 0xff00); + v_i += 2; + continue; + } + else if(len >= 11 && memcmp(reset_tuner_str, line, strlen(reset_tuner_str) - 1) == 0) { + printf("adding RESET_TUNER\n"); + write_vector16(v, v_i, (__u16) 0x0000); + v_i += 2; + continue; + } + + while(i < len && sscanf(line + i*sizeof(char), "%2x", &val) == 1) { + printf("%2x ", val); + values[count] = val; + ++count; + i += 2; + while(line[i] == ' ') { + ++i; + } + } + + write_vector16(v, v_i, __cpu_to_le16((__u16) count)); + v_i += 2; + + + for(i = 0; i < count; ++i) { + write_vector8(v, v_i, (__u8) values[i]); + ++v_i; + } + + printf("\n"); + } + write_vector16(v, v_i, 0xffff); + v_i += 2; + + free(line); + fclose(file); + *data = malloc(v_i); + memcpy(*data, v->data, v_i); + free_vector(v); + *r_len = v_i; +} + diff --git a/contrib/xc3028-firmware/standards.h b/contrib/xc3028-firmware/standards.h new file mode 100644 index 00000000..c9da02b5 --- /dev/null +++ b/contrib/xc3028-firmware/standards.h @@ -0,0 +1,25 @@ +/* + Xceive XC2028/3028 tuner module firmware manipulation tool + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + 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. + */ + +#ifndef STANDARDS_H +#define STANDARDS_H + +void create_standard_data(char* filename, unsigned char** data, unsigned int *len); + +#endif diff --git a/contrib/xc3028-firmware/tuner-xc2028-types.h b/contrib/xc3028-firmware/tuner-xc2028-types.h new file mode 100644 index 00000000..7e479878 --- /dev/null +++ b/contrib/xc3028-firmware/tuner-xc2028-types.h @@ -0,0 +1,141 @@ +/* tuner-xc2028_types + * + * This file includes internal tipes to be used inside tuner-xc2028. + * Shouldn't be included outside tuner-xc2028 + * + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License v2 + */ + +/* xc3028 firmware types */ + +/* BASE firmware should be loaded before any other firmware */ +#define BASE (1<<0) +#define BASE_TYPES (BASE|F8MHZ|MTS|FM|INPUT1|INPUT2|INIT1) + +/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */ +#define F8MHZ (1<<1) + +/* Multichannel Television Sound (MTS) + Those firmwares are capable of using xc2038 DSP to decode audio and + produce a baseband audio output on some pins of the chip. + There are MTS firmwares for the most used video standards. It should be + required to use MTS firmwares, depending on the way audio is routed into + the bridge chip + */ +#define MTS (1<<2) + +/* FIXME: I have no idea what's the difference between + D2620 and D2633 firmwares + */ +#define D2620 (1<<3) +#define D2633 (1<<4) + +/* DTV firmwares for 6, 7 and 8 MHz + DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS + DTV8 - 8MHz - DVB-C/DVB-T + */ +#define DTV6 (1 << 5) +#define QAM (1 << 6) +#define DTV7 (1<<7) +#define DTV78 (1<<8) +#define DTV8 (1<<9) + +#define DTV_TYPES (D2620|D2633|DTV6|QAM|DTV7|DTV78|DTV8|ATSC) + +/* There's a FM | BASE firmware + FM specific firmware (std=0) */ +#define FM (1<<10) + +#define STD_SPECIFIC_TYPES (MTS|FM|LCD|NOGD) + +/* Applies only for FM firmware + Makes it use RF input 1 (pin #2) instead of input 2 (pin #4) + */ +#define INPUT1 (1<<11) + + +/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M) + and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) + There are variants both with and without NOGD + Those firmwares produce better result with LCD displays + */ +#define LCD (1<<12) + +/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M) + and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) + The NOGD firmwares don't have group delay compensation filter + */ +#define NOGD (1<<13) + +/* Old firmwares were broken into init0 and init1 */ +#define INIT1 (1<<14) + +/* SCODE firmware selects particular behaviours */ +#define MONO (1 << 15) +#define ATSC (1 << 16) +#define IF (1 << 17) +#define LG60 (1 << 18) +#define ATI638 (1 << 19) +#define OREN538 (1 << 20) +#define OREN36 (1 << 21) +#define TOYOTA388 (1 << 22) +#define TOYOTA794 (1 << 23) +#define DIBCOM52 (1 << 24) +#define ZARLINK456 (1 << 25) +#define CHINA (1 << 26) +#define F6MHZ (1 << 27) +#define INPUT2 (1 << 28) +#define SCODE (1 << 29) + +/* This flag identifies that the scode table has a new format */ +#define HAS_IF (1 << 30) + +/* There are different scode tables for MTS and non-MTS. + The MTS firmwares support mono only + */ +#define SCODE_TYPES (SCODE | MTS) + + +/* Newer types not defined on videodev2.h. + The original idea were to move all those types to videodev2.h, but + it seemed overkill, since, with the exception of SECAM/K3, the other + types seem to be autodetected. + It is not clear where secam/k3 is used, nor we have a feedback of this + working or being autodetected by the standard secam firmware. + */ + +#define V4L2_STD_SECAM_K3 (0x04000000) + +/* Audio types */ + +#define V4L2_STD_A2_A (1LL<<32) +#define V4L2_STD_A2_B (1LL<<33) +#define V4L2_STD_NICAM_A (1LL<<34) +#define V4L2_STD_NICAM_B (1LL<<35) +#define V4L2_STD_AM (1LL<<36) +#define V4L2_STD_BTSC (1LL<<37) +#define V4L2_STD_EIAJ (1LL<<38) + +#define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B) +#define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B) + +/* To preserve backward compatibility, + (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported + */ + +#define V4L2_STD_AUDIO (V4L2_STD_A2 | \ + V4L2_STD_NICAM | \ + V4L2_STD_AM | \ + V4L2_STD_BTSC | \ + V4L2_STD_EIAJ) + +/* Used standards with audio restrictions */ + +#define V4L2_STD_PAL_BG_A2_A (V4L2_STD_PAL_BG | V4L2_STD_A2_A) +#define V4L2_STD_PAL_BG_A2_B (V4L2_STD_PAL_BG | V4L2_STD_A2_B) +#define V4L2_STD_PAL_BG_NICAM_A (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A) +#define V4L2_STD_PAL_BG_NICAM_B (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B) +#define V4L2_STD_PAL_DK_A2 (V4L2_STD_PAL_DK | V4L2_STD_A2) +#define V4L2_STD_PAL_DK_NICAM (V4L2_STD_PAL_DK | V4L2_STD_NICAM) +#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM) +#define V4L2_STD_SECAM_L_AM (V4L2_STD_SECAM_L | V4L2_STD_AM) |