aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@s-opensource.com>2017-09-14 11:42:57 -0300
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-09-14 11:53:09 -0300
commit5fec32581f7c471e58119190e98aa3e8c8b79b6d (patch)
tree2a551caa2603ce755a091b97c0865bb3afd648d9 /contrib
parente274ad3b269e1accb1ee37d4f997a63b77a9d483 (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.am4
-rw-r--r--contrib/decode_tm6000/.gitignore1
-rw-r--r--contrib/decode_tm6000/Makefile.am4
-rw-r--r--contrib/decode_tm6000/decode_tm6000.c365
-rw-r--r--contrib/xc3028-firmware/.gitignore1
-rw-r--r--contrib/xc3028-firmware/Makefile.am14
-rw-r--r--contrib/xc3028-firmware/README3
-rw-r--r--contrib/xc3028-firmware/extract_head.h161
-rw-r--r--contrib/xc3028-firmware/firmware-tool.c1106
-rw-r--r--contrib/xc3028-firmware/standards.c151
-rw-r--r--contrib/xc3028-firmware/standards.h25
-rw-r--r--contrib/xc3028-firmware/tuner-xc2028-types.h141
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)

Privacy Policy