diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-09-05 10:20:56 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-09-05 10:20:56 -0300 |
commit | 5f2d82cefe0ce930f3146875c88b993695c7e03a (patch) | |
tree | 2d33fe6b5949e509d85f48f5c8331adcb22151d3 /v4l2-apps | |
parent | ad789ba2904603896206a72f0445e8ba56da7134 (diff) | |
parent | 2257e70baa578d959dc4bdcce498ef07d2af00da (diff) | |
download | mediapointer-dvb-s2-5f2d82cefe0ce930f3146875c88b993695c7e03a.tar.gz mediapointer-dvb-s2-5f2d82cefe0ce930f3146875c88b993695c7e03a.tar.bz2 |
merge: http://linuxtv.org/hg/~awalls/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@redhat.com>
Fixed a trivial conflict between a janitor's patch from Hans Verkuil and
Andy patch series.
Priority: normal
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'v4l2-apps')
42 files changed, 2186 insertions, 656 deletions
diff --git a/v4l2-apps/Makefile b/v4l2-apps/Makefile index 550473435..2bba8fcd4 100644 --- a/v4l2-apps/Makefile +++ b/v4l2-apps/Makefile @@ -1,11 +1,24 @@ # Makefile for linuxtv.org v4l2-apps -.PHONY: all clean install +.PHONY: all distclean clean install -all clean install: +all:: prepare-includes + +all clean install:: $(MAKE) -C lib $@ $(MAKE) -C util $@ $(MAKE) -C test $@ %: make -C .. $(MAKECMDGOALS) + +clean:: + -$(RM) -rf include + +distclean:: clean + +prepare-includes: + -if [ ! -d include ]; then \ + cp -r ../linux/include include ; \ + ../v4l/scripts/headers_convert.pl `find include -type f` ; \ + fi diff --git a/v4l2-apps/lib/Makefile b/v4l2-apps/lib/Makefile index f123f3380..8443039b4 100644 --- a/v4l2-apps/lib/Makefile +++ b/v4l2-apps/lib/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/lib -CPPFLAGS += -I../../linux/include -I.. +CPPFLAGS += -I../include -I.. includes = v4l2.h diff --git a/v4l2-apps/lib/frequencies.c b/v4l2-apps/lib/frequencies.c index 77a79e61c..64379d2b0 100644 --- a/v4l2-apps/lib/frequencies.c +++ b/v4l2-apps/lib/frequencies.c @@ -21,7 +21,7 @@ #include <stdlib.h> -#include "linux/videodev2.h" +#include <linux/videodev2.h> #include "v4l2.h" /* This source was originally written by Nathan Laredo <laredo@gnu.org>. diff --git a/v4l2-apps/lib/libv4l/ChangeLog b/v4l2-apps/lib/libv4l/ChangeLog index 3d4e40ad2..eefe0028b 100644 --- a/v4l2-apps/lib/libv4l/ChangeLog +++ b/v4l2-apps/lib/libv4l/ChangeLog @@ -1,8 +1,20 @@ +libv4l-0.4.2 +------------ +* The bayer pixel order in gspca's sonixb driver was different from that in + the sn9c102 driver from the mainline kernel, a recent gspca patch fixes + this, adjust libv4l to match (and make it work properly with the sn9c102 + driver). + libv4l-0.4.1 ------------ * When the driver supports read() and we are not converting let the driver handle read() instead of emulating it with mmap mode - +* Fix errors and warnings when compiling with ICC (Gregor Jasny) +* Add support to libv4lconvert for rotating images 90 (for Pixart 7302 cams) + or 180 (Philips SPC200NC / Philips SPC300NC) degrees +* Add support for Pixart custom JPEG format +* Hide non public symbols (Gregor Jasny) +* Fix and enable x86_64 asm jpeg decompress helper functions (Gregor Jasny) libv4l-0.4.0 ------------ diff --git a/v4l2-apps/lib/libv4l/Makefile b/v4l2-apps/lib/libv4l/Makefile index 31254a09e..92928271a 100644 --- a/v4l2-apps/lib/libv4l/Makefile +++ b/v4l2-apps/lib/libv4l/Makefile @@ -1,5 +1,5 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).4.1 +V4L2_LIB_VERSION=$(LIB_RELEASE).4.2 all clean install: $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ diff --git a/v4l2-apps/lib/libv4l/include/libv4l1.h b/v4l2-apps/lib/libv4l/include/libv4l1.h index 9036ae869..c878cc198 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l1.h +++ b/v4l2-apps/lib/libv4l/include/libv4l1.h @@ -26,9 +26,15 @@ extern "C" { #include <stdio.h> #include <unistd.h> +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l1_log_file; +LIBV4L_PUBLIC extern FILE *v4l1_log_file; /* Just like your regular open/close/etc, except that when opening a v4l2 capture only device, full v4l1 emulation is done including emulating the @@ -51,14 +57,14 @@ extern FILE *v4l1_log_file; capture formats, like hw specific bayer compression methods). */ -int v4l1_open (const char *file, int oflag, ...); -int v4l1_close(int fd); -int v4l1_dup(int fd); -int v4l1_ioctl (int fd, unsigned long int request, ...); -ssize_t v4l1_read (int fd, void* buffer, size_t n); -void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC int v4l1_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l1_close(int fd); +LIBV4L_PUBLIC int v4l1_dup(int fd); +LIBV4L_PUBLIC int v4l1_ioctl (int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l1_read (int fd, void* buffer, size_t n); +LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, __off64_t offset); -int v4l1_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4l2.h b/v4l2-apps/lib/libv4l/include/libv4l2.h index 63529cf4b..b05b57cb6 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l2.h +++ b/v4l2-apps/lib/libv4l/include/libv4l2.h @@ -26,9 +26,15 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l2_log_file; +LIBV4L_PUBLIC extern FILE *v4l2_log_file; /* Just like your regular open/close/etc, except that format conversion is done if necessary when capturing. That is if you (try to) set a capture @@ -52,14 +58,14 @@ extern FILE *v4l2_log_file; fail. */ -int v4l2_open (const char *file, int oflag, ...); -int v4l2_close(int fd); -int v4l2_dup(int fd); -int v4l2_ioctl (int fd, unsigned long int request, ...); -ssize_t v4l2_read (int fd, void* buffer, size_t n); -void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC int v4l2_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l2_close(int fd); +LIBV4L_PUBLIC int v4l2_dup(int fd); +LIBV4L_PUBLIC int v4l2_ioctl (int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l2_read (int fd, void* buffer, size_t n); +LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, __off64_t offset); -int v4l2_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); /* Misc utility functions */ @@ -70,12 +76,12 @@ int v4l2_munmap(void *_start, size_t length); Normally returns 0, even if the cid did not exist or was locked, returns non 0 when an other error occured. */ -int v4l2_set_control(int fd, int cid, int value); +LIBV4L_PUBLIC int v4l2_set_control(int fd, int cid, int value); /* This function returns a value of 0 - 65535, scaled to from the actual range of the given v4l control id. when the cid does not exist, could not be accessed for some reason, or some error occured 0 is returned. */ -int v4l2_get_control(int fd, int cid); +LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); /* "low level" access functions, these functions allow somewhat lower level @@ -95,7 +101,7 @@ int v4l2_get_control(int fd, int cid); Returns fd on success, -1 if the fd is not suitable for use through libv4l2 (note the fd is left open in this case). */ -int v4l2_fd_open(int fd, int v4l2_flags); +LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4lconvert.h b/v4l2-apps/lib/libv4l/include/libv4lconvert.h index d8446dac8..87e41ceaa 100644 --- a/v4l2-apps/lib/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/lib/libv4l/include/libv4lconvert.h @@ -31,34 +31,45 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + struct v4lconvert_data; -struct v4lconvert_data *v4lconvert_create(int fd); -void v4lconvert_destroy(struct v4lconvert_data *data); +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); +LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data); /* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if the cam has a format from which v4lconvert can convert to dest_fmt. The real format to which the cam should be set is returned through src_fmt when not NULL. */ -int v4lconvert_try_format(struct v4lconvert_data *data, +LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data, struct v4l2_format *dest_fmt, /* in / out */ struct v4l2_format *src_fmt /* out */ ); /* Just like VIDIOC_ENUM_FMT, except that the emulated formats are added at the end of the list */ -int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); +LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); + +/* Is conversion necessary or can the app use the data directly? */ +LIBV4L_PUBLIC int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt); /* in */ /* return value of -1 on error, otherwise the amount of bytes written to dest */ -int v4lconvert_convert(struct v4lconvert_data *data, +LIBV4L_PUBLIC int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ unsigned char *src, int src_size, unsigned char *dest, int dest_size); /* get a string describing the last error*/ -const char *v4lconvert_get_error_message(struct v4lconvert_data *data); +LIBV4L_PUBLIC const char *v4lconvert_get_error_message(struct v4lconvert_data *data); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/libv4l1/Makefile b/v4l2-apps/lib/libv4l/libv4l1/Makefile index c92731daa..27848477e 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l1/Makefile @@ -1,7 +1,7 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes LIBS = -lpthread @@ -17,7 +17,7 @@ else V4L1_LIB = libv4l1.so V4L1_OBJS += ../libv4l2/libv4l2.so TARGETS += $(V4L1COMPAT) -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif ifeq ($(LIB_RELEASE),) diff --git a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c index 80d2fa09b..39f2d29e0 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c @@ -59,6 +59,7 @@ #include <linux/videodev.h> #include <linux/videodev2.h> #include <libv4l2.h> +#include "libv4l1.h" #include "libv4l1-priv.h" #define V4L1_SUPPORTS_ENUMINPUT 0x01 @@ -582,7 +583,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) input2.index = chan->channel; result = v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2); if (result == 0) { - snprintf(chan->name, sizeof(chan->name), "%s", input2.name); + snprintf(chan->name, sizeof(chan->name), "%s", (char*)input2.name); if (input2.type == V4L2_INPUT_TYPE_TUNER) { chan->tuners = 1; chan->type = VIDEO_TYPE_TV; diff --git a/v4l2-apps/lib/libv4l/libv4l1/log.c b/v4l2-apps/lib/libv4l/libv4l1/log.c index cccc3e6d0..74ce0f20b 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/log.c +++ b/v4l2-apps/lib/libv4l/libv4l1/log.c @@ -25,6 +25,7 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev.h> +#include "libv4l1-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -92,9 +93,9 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) break; case VIDIOCGWIN: case VIDIOCSWIN: - fprintf(v4l1_log_file,"width\t%d\n", + fprintf(v4l1_log_file,"width\t%u\n", ((struct video_window *)arg)->width); - fprintf(v4l1_log_file,"height\t%d\n", + fprintf(v4l1_log_file,"height\t%u\n", ((struct video_window *)arg)->height); break; @@ -115,7 +116,7 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) fprintf(v4l1_log_file,"palette %d\n",( (int)((struct video_picture*)arg)->palette) ); break; - case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/of? %d\n", *((int *)arg) ); + case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/off? %d\n", *((int *)arg) ); break; case VIDIOCSYNC: fprintf(v4l1_log_file,"sync %d\n", *((int *)arg) ); diff --git a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c index f1134fe3b..e4293d2f9 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c @@ -26,12 +26,21 @@ #include <fcntl.h> #include <libv4l1.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + /* Check that open/read/mmap is not a define */ #if defined open || defined read || defined mmap #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; @@ -52,7 +61,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64 (const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -73,16 +82,16 @@ int open64 (const char *file, int oflag, ...) return fd; } -int close(int fd) { +LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l1_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -94,24 +103,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l1_ioctl (fd, request, arg); } -ssize_t read(int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read(int fd, void* buffer, size_t n) { return v4l1_read (fd, buffer, n); } -void *mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } -void *mmap64(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l1_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index d8ac01c34..648d27c0c 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -1,7 +1,7 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes LIBS = -lpthread @@ -17,7 +17,7 @@ else V4L2_LIB = libv4l2.so V4L2_OBJS += ../libv4lconvert/libv4lconvert.so TARGETS += $(V4L2CONVERT) -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif ifeq ($(LIB_RELEASE),) diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index a40ab4ffe..7029f69c3 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -252,23 +252,54 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) return 0; } -static int v4l2_dequeue_read_buffer(int index, int *bytesused) +static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, + unsigned char *dest, int dest_size) { - int result; - struct v4l2_buffer buf; + const int max_tries = 10; + int result, tries = max_tries; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, &buf))) { - int saved_err = errno; - V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); - errno = saved_err; + /* Make sure we have the real v4l2 buffers mapped */ + if ((result = v4l2_map_buffers(index))) return result; + + do { + if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + return result; + } + + devices[index].frame_queued &= ~(1 << buf->index); + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].frame_pointers[buf->index], + buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf + + buf->index * V4L2_FRAME_BUF_SIZE), dest_size); + + if (result < 0) { + int saved_err = errno; + + if(errno == EAGAIN) + V4L2_LOG("warning error while converting frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + + v4l2_queue_read_buffer(index, buf->index); + errno = saved_err; + } + tries--; + } while (result < 0 && errno == EAGAIN && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s\n", + max_tries, v4lconvert_get_error_message(devices[index].convert)); } - devices[index].frame_queued &= ~(1 << buf.index); - *bytesused = buf.bytesused; - return buf.index; + return result; } static int v4l2_queue_read_buffers(int index) @@ -614,7 +645,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) is_capture_request = 1; break; case VIDIOC_ENUM_FMT: - if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: @@ -663,9 +695,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if (stream_needs_locking) pthread_mutex_lock(&devices[index].stream_lock); - converting = devices[index].src_fmt.fmt.pix.pixelformat != - devices[index].dest_fmt.fmt.pix.pixelformat; - + converting = v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); switch (request) { case VIDIOC_QUERYCAP: @@ -815,15 +846,16 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if ((result = v4l2_deactivate_read_stream(index))) break; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); - if (result) { - V4L2_LOG_ERR("dequeing buffer: %s\n", strerror(errno)); + if (!converting) { + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (result) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + } break; } - if (!converting) - break; - /* An application can do a DQBUF before mmap-ing in the buffer, but we need the buffer _now_ to write our converted data to it! */ @@ -844,23 +876,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } } - /* Make sure we have the real v4l2 buffers mapped before trying to - read from them */ - if ((result = v4l2_map_buffers(index))) - break; - - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[buf->index], - buf->bytesused, - devices[index].convert_mmap_buf + - buf->index * V4L2_FRAME_BUF_SIZE, - V4L2_FRAME_BUF_SIZE); - if (result < 0) { - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + result = v4l2_dequeue_and_convert(index, buf, 0, V4L2_FRAME_BUF_SIZE); + if (result < 0) break; - } buf->bytesused = result; buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; @@ -901,22 +919,23 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } -ssize_t v4l2_read (int fd, void* buffer, size_t n) +ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; - int index, bytesused = 0, frame_index; + int index; + struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return syscall(SYS_read, fd, dest, n); pthread_mutex_lock(&devices[index].stream_lock); /* When not converting and the device supports read let the kernel handle it */ if ((devices[index].flags & V4L2_SUPPORTS_READ) && - devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { - result = syscall(SYS_read, fd, buffer, n); + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { + result = syscall(SYS_read, fd, dest, n); goto leave; } @@ -931,26 +950,12 @@ ssize_t v4l2_read (int fd, void* buffer, size_t n) goto leave; } - if ((frame_index = v4l2_dequeue_read_buffer(index, &bytesused)) < 0) { - result = -1; - goto leave; - } - - /* ensure buffers are mapped before using them (they could have been - unmapped by a s_fmt ioctl) */ - if ((result = v4l2_map_buffers(index))) - goto leave; - - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[frame_index], bytesused, - buffer, n); - - v4l2_queue_read_buffer(index, frame_index); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + result = v4l2_dequeue_and_convert(index, &buf, dest, n); - if (result < 0) - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + if (result >= 0) + v4l2_queue_read_buffer(index, buf.index); leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -988,8 +993,8 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, buffer_index = offset & 0xff; if (buffer_index >= devices[index].no_frames || /* Got magic offset and not converting ?? */ - devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { errno = EINVAL; result = MAP_FAILED; goto leave; diff --git a/v4l2-apps/lib/libv4l/libv4l2/log.c b/v4l2-apps/lib/libv4l/libv4l2/log.c index 05f6c46d7..6237d55ec 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/log.c +++ b/v4l2-apps/lib/libv4l/libv4l2/log.c @@ -25,6 +25,8 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev2.h> +#include "libv4l2.h" +#include "libv4l2-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -116,7 +118,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) int pixfmt = fmt->fmt.pix.pixelformat; if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %dx%d\n", + fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %ux%u\n", pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, @@ -136,7 +138,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) struct v4l2_requestbuffers *req = arg; fprintf(v4l2_log_file, " count: %u type: %d memory: %d\n", - req->count, req->type, req->memory); + req->count, (int)req->type, (int)req->memory); } break; } diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index f312828c6..e8756f338 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -27,6 +27,8 @@ #include <syscall.h> #include <fcntl.h> #include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> /* These headers are not needed by us, but by linux/videodev2.h, which is broken on some systems and doesn't include them itself :( */ #include <sys/time.h> @@ -41,7 +43,13 @@ #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; struct v4l2_capability cap; @@ -84,7 +92,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64(const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -107,17 +115,17 @@ int open64(const char *file, int oflag, ...) return fd; } -int close(int fd) +LIBV4L_PUBLIC int close(int fd) { return v4l2_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l2_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -129,24 +137,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l2_ioctl (fd, request, arg); } -ssize_t read (int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read (int fd, void* buffer, size_t n) { return v4l2_read (fd, buffer, n); } -void *mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -void *mmap64(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l2_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile index c2a5be942..641d19d6e 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile @@ -1,16 +1,16 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes ifeq ($(LINKTYPE),static) CONVERT_LIB = libv4lconvert.a else CONVERT_LIB = libv4lconvert.so -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif -CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o \ +CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o flip.o \ jidctflt.o spca561-decompress.o rgbyuv.o spca501.o bayer.o TARGETS = $(CONVERT_LIB) libv4lconvert.pc INCLUDES = ../include/libv4lconvert.h diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/flip.c b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c new file mode 100644 index 000000000..cd3468a89 --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c @@ -0,0 +1,107 @@ +/* + +# RGB / YUV flip/rotate routines + +# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "libv4lconvert-priv.h" + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + src += 3 * width * height - 3; + + for (i = 0; i < width * height; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src -= 3; + } +} + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + /* First flip x and y of the Y plane */ + src += width * height - 1; + for (i = 0; i < width * height; i++) + *dst++ = *src--; + + /* Now flip the U plane */ + src += width * height * 5 / 4; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; + + /* Last flip the V plane */ + src += width * height / 2; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; +} + +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; +#define srcwidth destheight +#define srcheight destwidth + + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = ((srcheight - x - 1) * srcwidth + y) * 3; + *dst++ = src[offset++]; + *dst++ = src[offset++]; + *dst++ = src[offset]; + } +} + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; + + /* Y-plane */ + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* U-plane */ + src += srcwidth * srcheight; + destwidth /= 2; + destheight /= 2; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* V-plane */ + src += srcwidth * srcheight; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } +} diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c index ba70309c9..532abc7ea 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c @@ -80,7 +80,7 @@ #define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) -#if defined(__GNUC__) && (defined(__i686__)) // || defined(__x86_64__)) +#if defined(__GNUC__) && (defined(__i686__) || defined(__x86_64__)) static inline unsigned char descale_and_clamp(int x, int shift) { @@ -92,7 +92,7 @@ static inline unsigned char descale_and_clamp(int x, int shift) "\tcmpl %4,%1\n" "\tcmovg %4,%1\n" : "=r"(x) - : "0"(x), "Ic"((unsigned char)shift), "ir"(1UL<<(shift-1)), "r" (0xff), "r" (0) + : "0"(x), "Ic"((unsigned char)shift), "ir"(1U<<(shift-1)), "r" (0xff), "r" (0) ); return x; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h index bdf847186..915c33283 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -43,6 +43,10 @@ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') #endif +#ifndef V4L2_PIX_FMT_PJPG +#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') +#endif + #ifndef V4L2_PIX_FMT_SGBRG8 #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') #endif @@ -61,15 +65,21 @@ snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE, \ "v4l-convert: error " __VA_ARGS__) +#define V4LCONVERT_UPSIDE_DOWN 0x01 struct v4lconvert_data { int fd; + int flags; /* bitfield */ int supported_src_formats; /* bitfield */ unsigned int no_formats; char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; struct jdec_private *jdec; }; +struct v4lconvert_flags_info { + const char *card; + int flags; +}; void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst, int width, int height); @@ -107,4 +117,16 @@ void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, int width, int height, unsigned int pixfmt); +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height); + #endif diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c index 4c9e67d52..4b48bfac4 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c @@ -48,17 +48,26 @@ static const unsigned int supported_src_pixfmts[] = { V4L2_PIX_FMT_SPCA561, V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_PAC207, + V4L2_PIX_FMT_PJPG, }; static const unsigned int supported_dst_pixfmts[] = { SUPPORTED_DST_PIXFMTS }; +/* List of cams which need special flags */ +static const struct v4lconvert_flags_info v4lconvert_flags[] = { + { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */ + { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */ + { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN }, + { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN }, +}; struct v4lconvert_data *v4lconvert_create(int fd) { int i, j; struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); + struct v4l2_capability cap; if (!data) return NULL; @@ -84,6 +93,15 @@ struct v4lconvert_data *v4lconvert_create(int fd) data->no_formats = i; + /* Check if this cam has any special flags */ + if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) { + for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++) + if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) { + data->flags = v4lconvert_flags[i].flags; + break; + } + } + return data; } @@ -214,17 +232,42 @@ int v4lconvert_try_format(struct v4lconvert_data *data, return 0; } +/* Is conversion necessary ? */ +int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt) /* in */ +{ + int i; + + if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) + return 1; /* Formats differ */ + + if (!(data->flags & V4LCONVERT_UPSIDE_DOWN)) + return 0; /* Formats identical and we don't need flip */ + + /* Formats are identical, but we need flip, do we support the dest_fmt? */ + for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) + if (supported_dst_pixfmts[i] == dest_fmt->fmt.pix.pixelformat) + break; + + if (i == ARRAY_SIZE(supported_dst_pixfmts)) + return 0; /* Needs flip but we cannot do it :( */ + else + return 1; /* Needs flip and thus conversion */ +} + int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ - unsigned char *src, int src_size, unsigned char *dest, int dest_size) + unsigned char *src, int src_size, unsigned char *_dest, int dest_size) { unsigned int header_width, header_height; - int result, needed; + int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE; unsigned char *components[3]; + unsigned char *dest = _dest; /* Special case when no conversion is needed */ - if(!memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) { + if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; @@ -251,7 +294,15 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + if (data->flags & V4LCONVERT_UPSIDE_DOWN) { + rotate = 180; + dest = alloca(needed); + } + switch (src_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_PJPG: + jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG; + /* Fall through */ case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: if (!data->jdec) { @@ -262,9 +313,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } } - tinyjpeg_set_flags(data->jdec, - (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)? - TINYJPEG_FLAGS_MJPEG_TABLE : 0); + tinyjpeg_set_flags(data->jdec, jpeg_flags); if (tinyjpeg_parse_header(data->jdec, src, src_size)) { V4LCONVERT_ERR("parsing JPEG header: %s\n", tinyjpeg_get_errorstring(data->jdec)); @@ -273,13 +322,22 @@ int v4lconvert_convert(struct v4lconvert_data *data, } tinyjpeg_get_size(data->jdec, &header_width, &header_height); - if (header_width != dest_fmt->fmt.pix.width || header_height != dest_fmt->fmt.pix.height) { - V4LCONVERT_ERR("unexpected width / height in JPEG header\n"); - V4LCONVERT_ERR("expected: %dx%d, header: %ux%u\n", - dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, - header_width, header_height); - errno = EIO; - return -1; + if (header_width != dest_fmt->fmt.pix.width || + header_height != dest_fmt->fmt.pix.height) { + /* Check for (pixart) rotated JPEG */ + if (header_width == dest_fmt->fmt.pix.height || + header_height == dest_fmt->fmt.pix.width) { + if (!rotate) + dest = alloca(needed); + rotate += 90; + } else { + V4LCONVERT_ERR("unexpected width / height in JPEG header" + "expected: %ux%u, header: %ux%u\n", + dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, + header_width, header_height); + errno = EIO; + return -1; + } } components[0] = dest; @@ -303,13 +361,24 @@ int v4lconvert_convert(struct v4lconvert_data *data, break; } - /* If the JPEG header checked out ok and we get an error during actual - decompression, log the error, but don't return an errorcode to the - application, so that the user gets what we managed to decompress */ - if (result) - fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", - tinyjpeg_get_errorstring(data->jdec)); - + if (result) { + /* Pixart webcam's seem to regulary generate corrupt frames, which + are best thrown away to avoid flashes in the video stream. Tell + the upper layer this is an intermediate fault and it should try + again with a new buffer by setting errno to EAGAIN */ + if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) { + V4LCONVERT_ERR("Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + errno = EAGAIN; + return -1; + } else { + /* If the JPEG header checked out ok and we get an error during actual + decompression, log the error, but don't return an errorcode to the + application, so that the user gets what we managed to decompress */ + fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + } + } break; case V4L2_PIX_FMT_SBGGR8: @@ -387,7 +456,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C10X: v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SGBRG8; + bayer_fmt = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width, @@ -455,6 +524,45 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + /* Note when rotating dest is our temporary buffer to which our conversion + was done and _dest is the real dest! If the formats are identical no + conversion has been done! */ + if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat) + dest = src; + + switch (rotate) { + case 0: + break; + case 90: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + case 180: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + default: + printf("FIXME add %d degrees rotation\n", rotate); + } + return needed; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c index 4887c25ee..97ef4ebc0 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c @@ -38,7 +38,7 @@ static struct { signed char val; } table[256]; -void init_pixart_decoder(void) +static void init_pixart_decoder(void) { int i; int is_abs, val, len; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c index 742dd06ce..883d62330 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c @@ -20,6 +20,8 @@ */ +#include "libv4lconvert-priv.h" + #define RGB2YUV(r,g,b,y,u,v) \ (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \ (u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \ diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c index 98a513378..4ea526d49 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c @@ -50,7 +50,7 @@ static int sonix_unknown = 0; present at the MSB of byte x. */ -void sonix_decompress_init(void) +static void sonix_decompress_init(void) { int i; int is_abs, val, len, unk; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c index 802345af0..01eed4ec5 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c @@ -28,6 +28,7 @@ * but it might work with other spca561 cameras */ #include <string.h> +#include "libv4lconvert-priv.h" /*fixme: not reentrant */ static unsigned int bit_bucket; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c index 5c3b4e26d..fc9efbeb2 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c @@ -214,6 +214,28 @@ static const unsigned char val_ac_chrominance[] = 0xf9, 0xfa }; +const unsigned char pixart_quantization[][64] = { + { + 0x07, 0x07, 0x08, 0x0a, 0x09, 0x07, 0x0d, 0x0b, + 0x0c, 0x0d, 0x11, 0x10, 0x0f, 0x12, 0x17, 0x27, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + }, + { + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, +}; /* * 4 functions to manage the stream @@ -246,7 +268,7 @@ static const unsigned char val_ac_chrominance[] = unsigned char c; \ if (stream >= priv->stream_end) { \ snprintf(priv->error_string, sizeof(priv->error_string), \ - "fill_nbits error: need %d more bits\n", \ + "fill_nbits error: need %u more bits\n", \ nbits_wanted - nbits_in_reservoir); \ longjmp(priv->jump_state, -EIO); \ } \ @@ -288,6 +310,74 @@ static const unsigned char val_ac_chrominance[] = } while(0); +/* Special Pixart versions of the *_nbits functions, these remove the special + ff ff ff xx sequences pixart cams insert from the bitstream */ +#define pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) \ +do { \ + while (nbits_in_reservoir<nbits_wanted) \ + { \ + unsigned char c; \ + if (stream >= priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "fill_nbits error: need %u more bits\n", \ + nbits_wanted - nbits_in_reservoir); \ + longjmp(priv->jump_state, -EIO); \ + } \ + c = *stream++; \ + reservoir <<= 8; \ + if (c == 0xff) { \ + switch (stream[0]) { \ + case 0x00: \ + stream++; \ + break; \ + case 0xd9: /* EOF marker */ \ + stream++; \ + if (stream != priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: premature EOF\n"); \ + longjmp(priv->jump_state, -EIO); \ + } \ + break; \ + case 0xff: \ + if (stream[1] == 0xff && (stream[2] < 7 || stream[2] == 0xff)) { \ + stream += 3; \ + c = *stream++; \ + break; \ + } \ + /* Error fall through */ \ + default: \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: invalid JPEG marker: 0xff 0x%02x 0x%02x 0x%02x\n", \ + (unsigned int)stream[0], (unsigned int)stream[1], \ + (unsigned int)stream[2]); \ + longjmp(priv->jump_state, -EIO); \ + } \ + } \ + reservoir |= c; \ + nbits_in_reservoir+=8; \ + } \ +} while(0); + +/* Signed version !!!! */ +#define pixart_get_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U<<nbits_in_reservoir)-1); \ + if ((unsigned int)result < (1UL<<((nbits_wanted)-1))) \ + result += (0xFFFFFFFFUL<<(nbits_wanted))+1; \ +} while(0); + +#define pixart_look_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ +} while(0); + +/* Note skip_nbits is identical for both */ + + #define be16_to_cpu(x) (((x)[0]<<8)|(x)[1]) static void resync(struct jdec_private *priv); @@ -334,9 +424,50 @@ static int get_next_huffman_code(struct jdec_private *priv, struct huffman_table slowtable+=2; } } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); return 0; } +/* identical as above but with *_nbits replaced with pixart_*_nbits */ +static int pixart_get_next_huffman_code(struct jdec_private *priv, + struct huffman_table *huffman_table) +{ + int value, hcode; + unsigned int extra_nbits, nbits; + uint16_t *slowtable; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode); + value = huffman_table->lookup[hcode]; + if (value >= 0) + { + unsigned int code_size = huffman_table->code_size[value]; + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size); + return value; + } + + /* Decode more bits each time ... */ + for (extra_nbits=0; extra_nbits<16-HUFFMAN_HASH_NBITS; extra_nbits++) + { + nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode); + slowtable = huffman_table->slowtable[extra_nbits]; + /* Search if the code is in this array */ + while (slowtable[0]) { + if (slowtable[0] == hcode) { + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits); + return slowtable[1]; + } + slowtable+=2; + } + } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); + return 0; +} @@ -388,15 +519,84 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) else { j += count_0; /* skip count_0 zeroes */ - get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); - j++; + if (j < 64) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } } } + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + for (j = 0; j < 64; j++) c->DCT[j] = DCT[zigzag[j]]; } +/* identical as above both with *_nbits replaced with pixart_*_nbits */ +static void pixart_process_Huffman_data_unit(struct jdec_private *priv, int component) +{ + unsigned char j; + unsigned int huff_code; + unsigned char size_val, count_0; + + struct component *c = &priv->component_infos[component]; + short int DCT[64]; + + /* Initialize the DCT coef table */ + memset(DCT, 0, sizeof(DCT)); + + /* DC coefficient decoding */ + huff_code = pixart_get_next_huffman_code(priv, c->DC_table); + if (huff_code) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]); + DCT[0] += c->previous_DC; + c->previous_DC = DCT[0]; + } else { + DCT[0] = c->previous_DC; + } + + + /* AC coefficient decoding */ + j = 1; + while (j<64) + { + huff_code = pixart_get_next_huffman_code(priv, c->AC_table); + + size_val = huff_code & 0xF; + count_0 = huff_code >> 4; + + if (size_val == 0) + { /* RLE */ + if (count_0 == 0) + break; /* EOB found, go out */ + else if (count_0 == 0xF) + j += 16; /* skip 16 zeros */ + } + else + { + j += count_0; /* skip count_0 zeroes */ + if (j < 64 ) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } + } + } + + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + + for (j = 0; j < 64; j++) + c->DCT[j] = DCT[zigzag[j]]; +} + + /* * Takes two array of bits, and build the huffman table for size, and code * @@ -404,12 +604,13 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) * code_size will be used to known how many bits this symbol is encoded. * slowtable will be used when the first lookup didn't give the result. */ -static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) +static int build_huffman_table(struct jdec_private *priv, const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) { unsigned int i, j, code, code_size, val, nbits; unsigned char huffsize[257], *hz; unsigned int huffcode[257], *hc; int next_free_entry; + int slowtable_used[16-HUFFMAN_HASH_NBITS]; /* * Build a temp array @@ -425,7 +626,7 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * memset(table->lookup, 0xff, sizeof(table->lookup)); for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) - table->slowtable[i][0] = 0; + slowtable_used[i] = 0; /* Build a temp array * huffcode[X] => code used to write vals[X] @@ -472,32 +673,43 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * else { /* Perhaps sorting the array will be an optimization */ - uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1]; - while(slowtable[0]) - slowtable+=2; - slowtable[0] = code; - slowtable[1] = val; - slowtable[2] = 0; - /* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */ - } + int slowtable_index = code_size-HUFFMAN_HASH_NBITS-1; + + if (slowtable_used[slowtable_index] == 254) + error("slow Huffman table overflow\n"); + table->slowtable[slowtable_index][slowtable_used[slowtable_index]] + = code; + table->slowtable[slowtable_index][slowtable_used[slowtable_index] + 1] + = val; + slowtable_used[slowtable_index] += 2; + } } + for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) + table->slowtable[i][slowtable_used[i]] = 0; + + return 0; } -static void build_default_huffman_tables(struct jdec_private *priv) +static int build_default_huffman_tables(struct jdec_private *priv) { if ( (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) && priv->default_huffman_table_initialized) - return; + return 0; - build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]); - build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]); + if (build_huffman_table(priv, bits_dc_luminance, val_dc_luminance, &priv->HTDC[0])) + return -1; + if (build_huffman_table(priv, bits_ac_luminance, val_ac_luminance, &priv->HTAC[0])) + return -1; - build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]); - build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]); + if (build_huffman_table(priv, bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1])) + return -1; + if (build_huffman_table(priv, bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1])) + return -1; priv->default_huffman_table_initialized = 1; + return 0; } @@ -1390,6 +1602,44 @@ static void decode_MCU_2x1_3planes(struct jdec_private *priv) IDCT(&priv->component_infos[cCr], priv->Cr, 8); } +static void pixart_decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + unsigned char marker; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, + 8, marker); + /* I think the marker indicates which quantization table to use, iow + a Pixart JPEG may have a different quantization table per MCU, most + MCU's have 0x44 as marker for which our special Pixart quantization + tables are correct. Unfortunately with a 7302 some blocks also have 0x48, + and sometimes even other values. As 0x48 is quite common too, we really + need to find out the correct table for that, as currently the blocks + with a 0x48 marker look wrong. During normal operation the marker stays + within the range below, if it gets out of this range we're most likely + decoding garbage */ + if (marker < 0x20 || marker > 0x7f) { + snprintf(priv->error_string, sizeof(priv->error_string), + "Pixart JPEG error: invalid MCU marker: 0x%02x\n", + (unsigned int)marker); + longjmp(priv->jump_state, -EIO); + } + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8); + + // Y + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + + // Cb + pixart_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + pixart_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + /* * Decode a 2x1 * .-------. @@ -1676,9 +1926,9 @@ static int parse_SOS(struct jdec_private *priv, const unsigned char *stream) error("We do not support more than %d DC Huffman table\n", HUFFMAN_TABLES); if (cid != priv->component_infos[i].cid) - error("SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n", + error("SOS cid order (%u:%u) isn't compatible with the SOF marker (%u:%u)\n", i, cid, i, priv->component_infos[i].cid); - trace("ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); + trace("ComponentId:%u tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); #endif priv->component_infos[i].AC_table = &priv->HTAC[table&0xf]; priv->component_infos[i].DC_table = &priv->HTDC[table>>4]; @@ -1723,10 +1973,13 @@ static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) trace("Length of the table: %d\n", count); #endif - if (index & 0xf0 ) - build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); - else - build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); + if (index & 0xf0 ) { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTAC[index&0xf])) + return -1; + } else { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTDC[index&0xf])) + return -1; + } length -= 1; length -= 16; @@ -1817,6 +2070,8 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) { int chuck_len; int marker; + int sof_marker_found = 0; + int dqt_marker_found = 0; int sos_marker_found = 0; int dht_marker_found = 0; const unsigned char *next_chunck; @@ -1838,10 +2093,12 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) case SOF: if (parse_SOF(priv, stream) < 0) return -1; + sof_marker_found = 1; break; case DQT: if (parse_DQT(priv, stream) < 0) return -1; + dqt_marker_found = 1; break; case SOS: if (parse_SOS(priv, stream) < 0) @@ -1865,9 +2122,24 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) stream = next_chunck; } + if ( !sof_marker_found || + (!dqt_marker_found && !(priv->flags & TINYJPEG_FLAGS_PIXART_JPEG))) + goto bogus_jpeg_format; + + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + if (!priv->default_huffman_table_initialized) { + build_quantization_table(priv->Q_tables[0], pixart_quantization[0]); + build_quantization_table(priv->Q_tables[1], pixart_quantization[1]); + } + + /* Pixart JPEG data starts with one unknown / unused byte */ + priv->stream++; + } + if (!dht_marker_found) { trace("No Huffman table loaded, using the default one\n"); - build_default_huffman_tables(priv); + if (build_default_huffman_tables(priv)) + return -1; } #ifdef SANITY_CHECK @@ -1962,6 +2234,13 @@ static const decode_MCU_fct decode_mcu_3comp_table[4] = { decode_MCU_2x2_3planes, }; +static const decode_MCU_fct pixart_decode_mcu_3comp_table[4] = { + NULL, + NULL, + pixart_decode_MCU_2x1_3planes, + NULL, +}; + static const decode_MCU_fct decode_mcu_1comp_table[4] = { decode_MCU_1x1_1plane, decode_MCU_1x2_1plane, @@ -2021,6 +2300,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) bytes_per_blocklines[2] = 0; decode_mcu_table = decode_mcu_3comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + decode_mcu_table = pixart_decode_mcu_3comp_table; + switch (pixfmt) { case TINYJPEG_FMT_YUV420P: colorspace_array_conv = convert_colorspace_yuv420p; @@ -2056,6 +2338,8 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) case TINYJPEG_FMT_GREY: decode_mcu_table = decode_mcu_1comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + error("Greyscale output not support for PIXART JPEG's\n"); colorspace_array_conv = convert_colorspace_grey; if (priv->components[0] == NULL) priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); @@ -2064,8 +2348,7 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) break; default: - trace("Bad pixel format\n"); - return -1; + error("Bad pixel format\n"); } xstride_by_mcu = ystride_by_mcu = 8; @@ -2091,6 +2374,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) trace("Use decode 2x1 sampling\n"); } + if (decode_MCU == NULL) + error("no decode MCU function for this JPEG format (PIXART?)\n"); + resync(priv); /* Don't forget to that block can be either 8 or 16 lines */ @@ -2130,6 +2416,12 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) } } + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + /* Additional sanity check for funky Pixart format */ + if ((priv->stream_end - priv->stream) > 5) + error("Pixart JPEG error, stream does not end with EOF marker\n"); + } + return 0; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h index 42b60c1ee..b0096f0de 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h @@ -43,6 +43,7 @@ struct jdec_private; /* Flags that can be set by any applications */ #define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1) +#define TINYJPEG_FLAGS_PIXART_JPEG (1<<2) /* Format accepted in outout */ enum tinyjpeg_fmt { diff --git a/v4l2-apps/test/Makefile b/v4l2-apps/test/Makefile index ae8868c21..faafed14d 100644 --- a/v4l2-apps/test/Makefile +++ b/v4l2-apps/test/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/test -CPPFLAGS += -I../../linux/include +CPPFLAGS += -I../include binaries = ioctl-test \ sliced-vbi-test \ diff --git a/v4l2-apps/test/ioctl-test.c b/v4l2-apps/test/ioctl-test.c index f483338fb..99ad717e5 100644 --- a/v4l2-apps/test/ioctl-test.c +++ b/v4l2-apps/test/ioctl-test.c @@ -42,7 +42,7 @@ typedef __u8 u8; typedef __u32 u32; #include <linux/version.h> -#include "../linux/include/media/v4l2-common.h" +#include <media/v4l2-common.h> #include <linux/video_decoder.h> #else typedef u_int32_t u32; diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile index 41d4712e1..dcef94e0f 100644 --- a/v4l2-apps/util/Makefile +++ b/v4l2-apps/util/Makefile @@ -4,10 +4,10 @@ ifeq ($(KERNEL_DIR),) KERNEL_DIR = /usr endif -CPPFLAGS += -I../../linux/include -D_GNU_SOURCE +CPPFLAGS += -I../include -D_GNU_SOURCE LDFLAGS += -lm -binaries = v4l2-ctl v4l2-dbg ivtv-ctl cx18-ctl v4l-board-dbg +binaries = v4l2-ctl v4l2-dbg v4l2-compliance ivtv-ctl cx18-ctl ifeq ($(prefix),) prefix = /usr @@ -26,12 +26,14 @@ clean:: rm -rf keycodes parse.h keytable qv4l2: - if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi - make -C qv4l2 + -if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi + $(MAKE) -C qv4l2 v4l2-dbg: v4l2-dbg.o v4l2-driverids.o v4l2-chipids.o $(CXX) $^ -o $@ +v4l2-dbg.o: v4l2-dbg.h v4l2-dbg-bttv.h v4l2-dbg-em28xx.h v4l2-dbg-saa7134.h + install: mkdir -p $(prefix)/bin cp $(binaries) $(prefix)/bin @@ -57,14 +59,12 @@ keytables: keytable: keytable.c parse.h keytables -v4l-board-dbg: v4l-board-dbg.c bttv-dbg.h saa7134-dbg.h em28xx-dbg.h - -v4l2-driverids.cpp: ../../linux/include/linux/i2c-id.h +v4l2-driverids.cpp: ../include/linux/i2c-id.h @echo "struct driverid { const char *name; unsigned id; } driverids[] = {" >$@ @grep I2C_DRIVERID_ $^ | sed -e 's/.*I2C_DRIVERID_\([0-9A-Z_]*\)[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ -v4l2-chipids.cpp: ../../linux/include/media/v4l2-chip-ident.h +v4l2-chipids.cpp: ../include/media/v4l2-chip-ident.h @echo "struct chipid { const char *name; unsigned id; } chipids[] = {" >$@ @grep V4L2_IDENT_ $^ | sed -e 's/.*V4L2_IDENT_\([0-9A-Z_]*\)[^=]*=[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ diff --git a/v4l2-apps/util/ivtv-ctl.c b/v4l2-apps/util/ivtv-ctl.c index ab836e240..af84164e3 100644 --- a/v4l2-apps/util/ivtv-ctl.c +++ b/v4l2-apps/util/ivtv-ctl.c @@ -351,9 +351,9 @@ int main(int argc, char **argv) unsigned short gpio_dir = 0x0; /* GPIO direction bits */ int gpio_set_dir = 0; int passthrough = 0; - int audio_mute = 0; - int stereo_mode = 0; - int bilingual_mode = 0; + long audio_mute = 0; + long stereo_mode = 0; + long bilingual_mode = 0; int debug_level = 0; __u32 reset = 0; int new_debug_level, gdebug_level; @@ -621,7 +621,7 @@ int main(int argc, char **argv) } if (options[OptPassThrough]) { - int source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; + long source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; doioctl(fd, VIDEO_SELECT_SOURCE, (void *)source, "IVTV_IOC_PASSTHROUGH"); diff --git a/v4l2-apps/util/qv4l2/qv4l2.pro b/v4l2-apps/util/qv4l2/qv4l2.pro index c53a098b5..5e0b7555d 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.pro +++ b/v4l2-apps/util/qv4l2/qv4l2.pro @@ -3,7 +3,7 @@ ###################################################################### TEMPLATE = app -INCLUDEPATH += . ../../../linux/include ../../lib +INCLUDEPATH += . ../../include ../../lib CONFIG += debug # Input diff --git a/v4l2-apps/util/rds/Makefile b/v4l2-apps/util/rds/Makefile index 4f6ebd9f2..fb2213c99 100644 --- a/v4l2-apps/util/rds/Makefile +++ b/v4l2-apps/util/rds/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = rds-saa6588 diff --git a/v4l2-apps/util/v4l-board-dbg.c b/v4l2-apps/util/v4l-board-dbg.c deleted file mode 100644 index e74c7300e..000000000 --- a/v4l2-apps/util/v4l-board-dbg.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - Copyright (C) 2008 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 of the License. - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <linux/types.h> -#include <linux/videodev2.h> - -#include "bttv-dbg.h" -#include "saa7134-dbg.h" -#include "em28xx-dbg.h" - -#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) - -struct board_list { - char *name; - int prefix; /* Register prefix size */ - struct board_regs *regs; - int regs_size; - struct board_regs *alt_regs; - int alt_regs_size; -}; - -struct board_list boards[] = { - [0] = { /* From bttv-dbg.h */ - .name = BTTV_IDENT, - .prefix = sizeof(BTTV_PREFIX) - 1, - .regs = bt8xx_regs, - .regs_size = ARRAY_SIZE(bt8xx_regs), - .alt_regs = bt8xx_regs_other, - .alt_regs_size = ARRAY_SIZE(bt8xx_regs_other), - }, - [1] = { /* From saa7134-dbg.h */ - .name = SAA7134_IDENT, - .prefix = sizeof(SAA7134_PREFIX) - 1, - .regs = saa7134_regs, - .regs_size = ARRAY_SIZE(saa7134_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, - [2] = { /* From em28xx-dbg.h */ - .name = EM28XX_IDENT, - .prefix = sizeof(EM28XX_PREFIX) - 1, - .regs = em28xx_regs, - .regs_size = ARRAY_SIZE(em28xx_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, -}; - -static int is_get=0, is_set=0; - -static int doioctl(int fd, int request, void *parm, const char *name) -{ - int retVal; - - printf("ioctl %s ", name); - retVal = ioctl(fd, request, parm); - if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); - - return retVal; -} - -static void usage(void) -{ - printf("bttv-dbg <args>\n"); -} - -enum Option { - OptGetReg = 'g', - OptSetReg = 's', - OptHelp = 'h', -}; - -static void print_bin (int val, int size) -{ - int i, j, v; - - printf("("); - for (i = size-1; i >= 0; i--) { - v = (val >> (i * 8)) & 0xff; - for (j = 7; j >= 0; j--) { - int bit = (v >> j) & 0x1; - if (bit) - printf("1"); - else - printf("0"); - } - if (i) - printf(" "); - else - printf(")"); - } -} - -int main(int argc, char **argv) -{ - char *device = strdup("/dev/video0"); - char *reg_set = NULL; - int ch; - int i; - int fd = -1; - struct v4l2_register reg; - struct v4l2_capability cap; - struct board_list *curr_bd; - int board = 0; - struct option long_options[] = { - /* Please keep in alphabetical order of the short option. - That makes it easier to see which options are still free. */ - {"get-reg", no_argument, 0, OptGetReg}, - {"set-reg", required_argument, 0, OptSetReg}, - {"help", no_argument, 0, OptHelp}, - {0, 0, 0, 0} - }; - - /* command args */ - if (argc == 1) { - usage(); - return 0; - } - while (1) { - int option_index = 0; - - ch = getopt_long(argc, argv, "gs:", long_options, &option_index); - if (ch == -1) - break; - - switch (ch) { - case OptHelp: - usage(); - return 0; - case OptGetReg: - is_get++; - break; - case OptSetReg: - is_set++; - reg_set = optarg; - - break; - case '?': - fprintf(stderr, "Unknown argument `%s'\n", - argv[optind]); - usage(); - return 1; - } - } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } - - fd = open(device, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", device, - strerror(errno)); - exit(1); - } - free(device); - - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { - printf("Error while reading capabilities\n"); - exit(2); - } - - for (board = ARRAY_SIZE(boards)-1; board >= 0; board--) { - if (!strcasecmp((char *)cap.driver, boards[board].name)) - break; - } - if (board < 0) { - printf("This software doesn't support %s yet\n", cap.driver); - exit(3); - } - - curr_bd = &boards[board]; - - reg.match_type = V4L2_CHIP_MATCH_HOST; - reg.match_chip = 0; - - if (is_get) { - for (i = 0; i < curr_bd->regs_size; i++) { - char name[256]; - reg.reg = curr_bd->regs[i].reg; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading. Maybe you're not root?\n"); - continue; - } - sprintf(name, "%s:", curr_bd->regs[i].name); - - switch (curr_bd->regs[i].size) { - case 1: - printf("%-32s %02llx ", name, reg.val & 0xff); - break; - case 2: - printf("%-32s %04llx ", name, reg.val & 0xffff); - break; - case 4: - printf("%-32s %08llx ", name, reg.val & 0xffffffff); - break; - } - print_bin (reg.val, curr_bd->regs[i].size); - printf("\n"); - } - return 0; - } - - if (is_set) { - char *reg_name; - int val; - int r, size; - unsigned prev; - struct board_regs *bd_reg; - - reg_name = strtok(reg_set, "=:"); - val = strtol(strtok(NULL, "=:"), 0L, 0); - - if (!reg_name) { - printf("set argument is invalid\n"); - return -1; - } - - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name)) { - bd_reg = &curr_bd->alt_regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - printf("Register not found\n"); - return -1; - } - - reg.reg = r; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - prev = reg.val; - - switch (size) { - case 1: - reg.val = (reg.val & (~0xff)) | val; - break; - case 2: - reg.val = (reg.val & (~0xffff)) | val; - break; - case 4: - reg.val = val; - break; - } - - printf("Changing value of register %s(0x%x) from 0x%02x to 0x%02x\n", - bd_reg->name, r, prev, (unsigned int)reg.val); - - prev = reg.val; - - if (ioctl(fd, VIDIOC_DBG_S_REGISTER, ®) < 0) { - printf("Error while writing\n"); - return -1; - } - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - if (reg.val != prev) { - printf("Value of register %s(0x%x) is now 0x%02x\n", - bd_reg->name, r, (unsigned int)reg.val); - } - } - - close(fd); - exit(0); -} diff --git a/v4l2-apps/util/v4l2-compliance.cpp b/v4l2-apps/util/v4l2-compliance.cpp new file mode 100644 index 000000000..2fe5292fe --- /dev/null +++ b/v4l2-apps/util/v4l2-compliance.cpp @@ -0,0 +1,1122 @@ +/* + V4L2 API compliance test tool. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> + +#include <list> +#include <vector> +#include <map> +#include <string> + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptSetDevice = 'd', + OptGetDriverInfo = 'D', + OptHelp = 'h', + OptTest = 't', + OptVerbose = 'v', + OptLast = 256 +}; + +enum Test { + TestCap = 0, + TestChipIdent, + TestRegister, + TestLogStatus, + TestMax +}; + +static int test[TestMax]; + +static char options[OptLast]; + +static int app_result; +static int verbose; + +static unsigned caps; + +typedef std::vector<struct v4l2_ext_control> ctrl_list; +static ctrl_list user_ctrls; +static ctrl_list mpeg_ctrls; + +typedef std::map<std::string, unsigned> ctrl_strmap; +static ctrl_strmap ctrl_str2id; +typedef std::map<unsigned, std::string> ctrl_idmap; +static ctrl_idmap ctrl_id2str; + +typedef std::list<std::string> ctrl_get_list; +static ctrl_get_list get_ctrls; + +typedef std::map<std::string,std::string> ctrl_set_map; +static ctrl_set_map set_ctrls; + +typedef struct { + unsigned flag; + const char *str; +} flag_def; + +static const flag_def service_def[] = { + { V4L2_SLICED_TELETEXT_B, "teletext" }, + { V4L2_SLICED_VPS, "vps" }, + { V4L2_SLICED_CAPTION_525, "cc" }, + { V4L2_SLICED_WSS_625, "wss" }, + { 0, NULL } +}; + +/* fmts specified */ +#define FmtWidth (1L<<0) +#define FmtHeight (1L<<1) +#define FmtChromaKey (1L<<2) +#define FmtGlobalAlpha (1L<<3) + +/* crop specified */ +#define CropWidth (1L<<0) +#define CropHeight (1L<<1) +#define CropLeft (1L<<2) +#define CropTop (1L<<3) + +static struct option long_options[] = { + {"device", required_argument, 0, OptSetDevice}, + {"help", no_argument, 0, OptHelp}, + {"info", no_argument, 0, OptGetDriverInfo}, + {"verbose", no_argument, 0, OptVerbose}, + {"test", required_argument, 0, OptTest}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf("Common options:\n"); + printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); + printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); + printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); + printf(" -h, --help display this help message\n"); + printf(" -t, --test=<num> run specified test.\n"); + printf(" By default all tests are run.\n"); + printf(" 0 = test VIDIOC_QUERYCAP\n"); + printf(" -v, --verbose turn on verbose ioctl error reporting.\n"); + exit(0); +} + +static std::string num2s(unsigned num) +{ + char buf[10]; + + sprintf(buf, "%08x", num); + return buf; +} + +static std::string buftype2s(int type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "Video Capture"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "Video Output"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "Video Overlay"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI Capture"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI Output"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "Sliced VBI Capture"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "Sliced VBI Output"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return "Video Output Overlay"; + case V4L2_BUF_TYPE_PRIVATE: + return "Private"; + default: + return "Unknown (" + num2s(type) + ")"; + } +} + +static std::string fcc2s(unsigned int val) +{ + std::string s; + + s += val & 0xff; + s += (val >> 8) & 0xff; + s += (val >> 16) & 0xff; + s += (val >> 24) & 0xff; + return s; +} + +static std::string field2s(int val) +{ + switch (val) { + case V4L2_FIELD_ANY: + return "Any"; + case V4L2_FIELD_NONE: + return "None"; + case V4L2_FIELD_TOP: + return "Top"; + case V4L2_FIELD_BOTTOM: + return "Bottom"; + case V4L2_FIELD_INTERLACED: + return "Interlaced"; + case V4L2_FIELD_SEQ_TB: + return "Sequential Top-Bottom"; + case V4L2_FIELD_SEQ_BT: + return "Sequential Bottom-Top"; + case V4L2_FIELD_ALTERNATE: + return "Alternating"; + case V4L2_FIELD_INTERLACED_TB: + return "Interlaced Top-Bottom"; + case V4L2_FIELD_INTERLACED_BT: + return "Interlaced Bottom-Top"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string colorspace2s(int val) +{ + switch (val) { + case V4L2_COLORSPACE_SMPTE170M: + return "Broadcast NTSC/PAL (SMPTE170M/ITU601)"; + case V4L2_COLORSPACE_SMPTE240M: + return "1125-Line (US) HDTV (SMPTE240M)"; + case V4L2_COLORSPACE_REC709: + return "HDTV and modern devices (ITU709)"; + case V4L2_COLORSPACE_BT878: + return "Broken Bt878"; + case V4L2_COLORSPACE_470_SYSTEM_M: + return "NTSC/M (ITU470/ITU601)"; + case V4L2_COLORSPACE_470_SYSTEM_BG: + return "PAL/SECAM BG (ITU470/ITU601)"; + case V4L2_COLORSPACE_JPEG: + return "JPEG (JFIF/ITU601)"; + case V4L2_COLORSPACE_SRGB: + return "SRGB"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string flags2s(unsigned val, const flag_def *def) +{ + std::string s; + + while (def->flag) { + if (val & def->flag) { + if (s.length()) s += " "; + s += def->str; + } + def++; + } + return s; +} + +static void print_sliced_vbi_cap(struct v4l2_sliced_vbi_cap &cap) +{ + printf("\tType : %s\n", buftype2s(cap.type).c_str()); + printf("\tService Set : %s\n", + flags2s(cap.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(cap.service_lines[0][i], service_def).c_str(), + flags2s(cap.service_lines[1][i], service_def).c_str()); + } +} + +static std::string name2var(unsigned char *name) +{ + std::string s; + + while (*name) { + if (*name == ' ') s += "_"; + else s += std::string(1, tolower(*name)); + name++; + } + return s; +} + +static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, + struct v4l2_ext_control *ctrl, int show_menus) +{ + struct v4l2_querymenu qmenu = { 0 }; + std::string s = name2var(queryctrl->name); + int i; + + qmenu.id = queryctrl->id; + switch (queryctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + printf("%31s (int) : min=%d max=%d step=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value, + ctrl->value); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printf("%31s (int64): value=%lld", s.c_str(), ctrl->value64); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + printf("%31s (bool) : default=%d value=%d", + s.c_str(), + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_MENU: + printf("%31s (menu) : min=%d max=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_BUTTON: + printf("%31s (button)\n", s.c_str()); + break; + default: break; + } + if (queryctrl->flags) { + const flag_def def[] = { + { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, + { V4L2_CTRL_FLAG_READ_ONLY, "readonly" }, + { V4L2_CTRL_FLAG_UPDATE, "update" }, + { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, + { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { 0, NULL } + }; + printf(" flags=%s", flags2s(queryctrl->flags, def).c_str()); + } + printf("\n"); + if (queryctrl->type == V4L2_CTRL_TYPE_MENU && show_menus) { + for (i = 0; i <= queryctrl->maximum; i++) { + qmenu.index = i; + if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + printf("\t\t\t\t%d: %s\n", i, qmenu.name); + } + } +} + +static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) +{ + struct v4l2_control ctrl = { 0 }; + struct v4l2_ext_control ext_ctrl = { 0 }; + struct v4l2_ext_controls ctrls = { 0 }; + + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { + printf("\n%s\n\n", qctrl.name); + return 1; + } + ext_ctrl.id = qctrl.id; + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id); + ctrls.count = 1; + ctrls.controls = &ext_ctrl; + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER && + qctrl.id < V4L2_CID_PRIVATE_BASE) { + if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { + printf("error %d getting ext_ctrl %s\n", + errno, qctrl.name); + return 0; + } + } + else { + ctrl.id = qctrl.id; + if (ioctl(fd, VIDIOC_G_CTRL, &ctrl)) { + printf("error %d getting ctrl %s\n", + errno, qctrl.name); + return 0; + } + ext_ctrl.value = ctrl.value; + } + print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); + return 1; +} + +static void list_controls(int fd, int show_menus) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + print_control(fd, qctrl, show_menus); + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + print_control(fd, qctrl, show_menus); + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + print_control(fd, qctrl, show_menus); + } +} + +static void find_controls(int fd) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } +} + +static std::string fbufcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_FBUF_CAP_EXTERNOVERLAY) + s += "\t\t\tExtern Overlay\n"; + if (cap & V4L2_FBUF_CAP_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (cap & V4L2_FBUF_CAP_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (cap & V4L2_FBUF_CAP_LIST_CLIPPING) + s += "\t\t\tClipping List\n"; + if (cap & V4L2_FBUF_CAP_BITMAP_CLIPPING) + s += "\t\t\tClipping Bitmap\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static std::string fbufflags2s(unsigned fl) +{ + std::string s; + + if (fl & V4L2_FBUF_FLAG_PRIMARY) + s += "\t\t\tPrimary Graphics Surface\n"; + if (fl & V4L2_FBUF_FLAG_OVERLAY) + s += "\t\t\tOverlay Matches Capture/Output Size\n"; + if (fl & V4L2_FBUF_FLAG_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (fl & V4L2_FBUF_FLAG_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static void printfbuf(const struct v4l2_framebuffer &fb) +{ + int is_ext = fb.capability & V4L2_FBUF_CAP_EXTERNOVERLAY; + + printf("Framebuffer Format:\n"); + printf("\tCapability : %s", fbufcap2s(fb.capability).c_str() + 3); + printf("\tFlags : %s", fbufflags2s(fb.flags).c_str() + 3); + if (fb.base) + printf("\tBase : 0x%p\n", fb.base); + printf("\tWidth : %d\n", fb.fmt.width); + printf("\tHeight : %d\n", fb.fmt.height); + printf("\tPixel Format : %s\n", fcc2s(fb.fmt.pixelformat).c_str()); + if (!is_ext) { + printf("\tBytes per Line: %d\n", fb.fmt.bytesperline); + printf("\tSize image : %d\n", fb.fmt.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(fb.fmt.colorspace).c_str()); + if (fb.fmt.priv) + printf("\tCustom Info : %08x\n", fb.fmt.priv); + } +} + +static void printcrop(const struct v4l2_crop &crop) +{ + printf("Crop: Left %d, Top %d, Width %d, Height %d\n", + crop.c.left, crop.c.top, crop.c.width, crop.c.height); +} + +static void printcropcap(const struct v4l2_cropcap &cropcap) +{ + printf("Crop Capability %s:\n", buftype2s(cropcap.type).c_str()); + printf("\tBounds : Left %d, Top %d, Width %d, Height %d\n", + cropcap.bounds.left, cropcap.bounds.top, cropcap.bounds.width, cropcap.bounds.height); + printf("\tDefault : Left %d, Top %d, Width %d, Height %d\n", + cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height); + printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); +} + +static void printfmt(struct v4l2_format vfmt) +{ + const flag_def vbi_def[] = { + { V4L2_VBI_UNSYNC, "unsynchronized" }, + { V4L2_VBI_INTERLACED, "interlaced" }, + { 0, NULL } + }; + printf("Format %s:\n", buftype2s(vfmt.type).c_str()); + + switch (vfmt.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); + printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); + printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); + printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(vfmt.fmt.pix.colorspace).c_str()); + if (vfmt.fmt.pix.priv) + printf("\tCustom Info : %08x\n", vfmt.fmt.pix.priv); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + printf("\tLeft/Top : %d/%d\n", + vfmt.fmt.win.w.left, vfmt.fmt.win.w.top); + printf("\tWidth/Height: %d/%d\n", + vfmt.fmt.win.w.width, vfmt.fmt.win.w.height); + printf("\tField : %s\n", field2s(vfmt.fmt.win.field).c_str()); + printf("\tChroma Key : 0x%08x\n", vfmt.fmt.win.chromakey); + printf("\tGlobal Alpha: 0x%02x\n", vfmt.fmt.win.global_alpha); + printf("\tClip Count : %u\n", vfmt.fmt.win.clipcount); + printf("\tClip Bitmap : %s\n", vfmt.fmt.win.bitmap ? "Yes" : "No"); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + printf("\tSampling Rate : %u Hz\n", vfmt.fmt.vbi.sampling_rate); + printf("\tOffset : %u samples (%g secs after leading edge)\n", + vfmt.fmt.vbi.offset, + (double)vfmt.fmt.vbi.offset / (double)vfmt.fmt.vbi.sampling_rate); + printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line); + printf("\tSample Format : %s\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str()); + printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]); + printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]); + printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]); + printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]); + if (vfmt.fmt.vbi.flags) + printf("\tFlags : %s\n", flags2s(vfmt.fmt.vbi.flags, vbi_def).c_str()); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + printf("\tService Set : %s\n", + flags2s(vfmt.fmt.sliced.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(vfmt.fmt.sliced.service_lines[0][i], service_def).c_str(), + flags2s(vfmt.fmt.sliced.service_lines[1][i], service_def).c_str()); + } + printf("\tI/O Size : %u\n", vfmt.fmt.sliced.io_size); + break; + case V4L2_BUF_TYPE_PRIVATE: + break; + } +} + +static void print_video_formats(int fd, enum v4l2_buf_type type) +{ + struct v4l2_fmtdesc fmt; + + fmt.index = 0; + fmt.type = type; + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tType : %s\n", buftype2s(type).c_str()); + printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str()); + if (fmt.flags) + printf(" (compressed)"); + printf("\n"); + printf("\tName : %s\n", fmt.description); + printf("\n"); + fmt.index++; + } +} + +static char *pts_to_string(char *str, unsigned long pts) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + float fps; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + fps = 30; + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, + frame); + return p; +} + +static const char *audmode2s(int audmode) +{ + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: return "stereo"; + case V4L2_TUNER_MODE_LANG1: return "lang1"; + case V4L2_TUNER_MODE_LANG2: return "lang2"; + case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual"; + case V4L2_TUNER_MODE_MONO: return "mono"; + default: return "unknown"; + } +} + +static std::string rxsubchans2s(int rxsubchans) +{ + std::string s; + + if (rxsubchans & V4L2_TUNER_SUB_MONO) + s += "mono "; + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo "; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + s += "lang1 "; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + s += "lang2 "; + return s; +} + +static std::string tcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_TUNER_CAP_LOW) + s += "62.5 Hz "; + else + s += "62.5 kHz "; + if (cap & V4L2_TUNER_CAP_NORM) + s += "multi-standard "; + if (cap & V4L2_TUNER_CAP_STEREO) + s += "stereo "; + if (cap & V4L2_TUNER_CAP_LANG1) + s += "lang1 "; + if (cap & V4L2_TUNER_CAP_LANG2) + s += "lang2 "; + return s; +} + +static std::string cap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_CAP_VIDEO_CAPTURE) + s += "\t\tVideo Capture\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT) + s += "\t\tVideo Output\n"; + if (cap & V4L2_CAP_VIDEO_OVERLAY) + s += "\t\tVideo Overlay\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) + s += "\t\tVideo Output Overlay\n"; + if (cap & V4L2_CAP_VBI_CAPTURE) + s += "\t\tVBI Capture\n"; + if (cap & V4L2_CAP_VBI_OUTPUT) + s += "\t\tVBI Output\n"; + if (cap & V4L2_CAP_SLICED_VBI_CAPTURE) + s += "\t\tSliced VBI Capture\n"; + if (cap & V4L2_CAP_SLICED_VBI_OUTPUT) + s += "\t\tSliced VBI Output\n"; + if (cap & V4L2_CAP_RDS_CAPTURE) + s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_TUNER) + s += "\t\tTuner\n"; + if (cap & V4L2_CAP_AUDIO) + s += "\t\tAudio\n"; + if (cap & V4L2_CAP_RADIO) + s += "\t\tRadio\n"; + if (cap & V4L2_CAP_READWRITE) + s += "\t\tRead/Write\n"; + if (cap & V4L2_CAP_ASYNCIO) + s += "\t\tAsync I/O\n"; + if (cap & V4L2_CAP_STREAMING) + s += "\t\tStreaming\n"; + return s; +} + +static v4l2_std_id parse_pal(const char *pal) +{ + if (pal[0] == '-') { + switch (pal[1]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[2] == 'c' || pal[2] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + } + } + fprintf(stderr, "pal specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_secam(const char *secam) +{ + if (secam[0] == '-') { + switch (secam[1]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[2] == 'C' || secam[2] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + } + } + fprintf(stderr, "secam specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_ntsc(const char *ntsc) +{ + if (ntsc[0] == '-') { + switch (ntsc[1]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + } + } + fprintf(stderr, "ntsc specifier not recognised\n"); + return 0; +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + int e; + + errno = 0; + retVal = ioctl(fd, request, parm); + e = errno; + if (verbose) + printf("\t\t%s returned %d (%s)\n", name, retVal, strerror(e)); + if (retVal == 0) return retVal; + if (retVal != -1) { + return -1; + } + retVal = e; + return retVal; +} + +static int parse_subopt(char **subs, char * const *subopts, char **value) +{ + int opt = getsubopt(subs, subopts, value); + + if (opt == -1) { + fprintf(stderr, "Invalid suboptions specified\n"); + usage(); + exit(1); + } + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } + return opt; +} + +static void parse_next_subopt(char **subs, char **value) +{ + static char *const subopts[] = { + NULL + }; + int opt = getsubopt(subs, subopts, value); + + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } +} + +static void print_std(const char *prefix, const char *stds[], unsigned long long std) +{ + int first = 1; + + printf("\t%s-", prefix); + while (*stds) { + if (std & 1) { + if (!first) + printf("/"); + first = 0; + printf("%s", *stds); + } + stds++; + std >>= 1; + } + printf("\n"); +} + +static const char *ok(int res) +{ + if (res) + app_result = res; + return res ? "FAIL" : "OK"; +} + +static int check_string(const char *s, int len, const char *fld) +{ + if (strlen(s) == 0) { + if (verbose) + printf("%s field empty\n", fld); + return -1; + } + if (strlen(s) >= len) { + if (verbose) + printf("%s field not 0-terminated\n", fld); + return -1; + } + return 0; +} + +static int check_ustring(const __u8 *s, int len, const char *fld) +{ + return check_string((const char *)s, len, fld); +} + +static int check_0(void *p, int len) +{ + __u8 *q = (__u8 *)p; + + while (len--) + if (*q++) { + if (verbose) + printf("array not zeroed by driver\n"); + return -1; + } + return 0; +} + +static int testCap(int fd) +{ + struct v4l2_capability vcap; + __u32 caps; + + if (doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP")) + return -1; + if (check_ustring(vcap.driver, sizeof(vcap.driver), "driver")) + return -1; + if (check_ustring(vcap.card, sizeof(vcap.card), "card")) + return -1; + if (check_ustring(vcap.bus_info, sizeof(vcap.bus_info), "bus_info")) + return -1; + if (check_0(vcap.reserved, sizeof(vcap.reserved))) + return -1; + caps = vcap.capabilities; + if (caps == 0) { + if (verbose) printf("no capabilities set\n"); + return -1; + } + return 0; +} + +static int testChipIdent(int fd) +{ + struct v4l2_chip_ident chip; + int ret; + + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + // Must return either 0 (OK) or EINVAL (not supported) + if (ret == 0) { + struct v4l2_chip_ident orig; + + // set invalid match_type + chip.match_type = V4L2_CHIP_MATCH_I2C_ADDR + 1; + chip.match_chip = 0xdeadbeef; + chip.ident = 0xdeadbeef; + chip.revision = 0xdeadbeef; + orig = chip; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + if (ret != EINVAL) { + if (verbose) + printf("Invalid match_type accepted\n"); + return -1; + } + if (memcmp(&orig, &chip, sizeof(chip))) { + if (verbose) + printf("Error, but struct modified\n"); + return -1; + } + return 0; + } + return ret != EINVAL; +} + +static int testRegister(int fd) +{ + struct v4l2_register reg; + struct v4l2_chip_ident chip; + int ret; + int uid = getuid(); + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = 0; + ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, ®, "VIDIOC_DBG_G_REGISTER"); + if (ret == EINVAL) + return 0; + if (uid && ret != EPERM) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n"); + return -1; + } + if (uid == 0 && ret) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n"); + return -1; + } + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT")) { + printf("Must support VIDIOC_G_CHIP_IDENT\n"); + return -1; + } + if (uid) { + // Don't test S_REGISTER as root, don't want to risk + // messing with registers in the compliance test. + reg.reg = reg.val = 0; + ret = doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, "VIDIOC_DBG_S_REGISTER"); + if (ret != EINVAL && ret != EPERM) { + printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n"); + return -1; + } + } + return 0; +} + +static int testLogStatus(int fd) +{ + int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS"); + + return (ret == 0 || ret == EINVAL) ? 0 : -1; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + unsigned t; + int fd = -1; + + /* command args */ + int ch; + const char *device = "/dev/video0"; /* -d device */ + struct v4l2_capability vcap; /* list_cap */ + char short_options[26 * 2 * 2 + 1]; + int idx = 0; + int tests = 0; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptTest: + t = strtoul(optarg, NULL, 0); + + if (t >= TestMax) + usage(); + test[t] = 1; + tests++; + break; + case OptSetDevice: + device = optarg; + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; + char dev = device[0]; + + sprintf(newdev, "/dev/video%c", dev); + device = newdev; + } + break; + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + verbose = options[OptVerbose]; + if (!tests) { + for (t = 0; t < TestMax; t++) + test[t] = 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + + ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + caps = vcap.capabilities; + find_controls(fd); + for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { + if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", (*iter).c_str()); + exit(1); + } + } + for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { + if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", iter->first.c_str()); + exit(1); + } + } + + /* Information Opts */ + + if (options[OptGetDriverInfo]) { + printf("Driver Info:\n"); + printf("\tDriver name : %s\n", vcap.driver); + printf("\tCard type : %s\n", vcap.card); + printf("\tBus info : %s\n", vcap.bus_info); + printf("\tDriver version: %d\n", vcap.version); + printf("\tCapabilities : 0x%08X\n", vcap.capabilities); + printf("%s", cap2s(vcap.capabilities).c_str()); + } + + printf("Compliance test for device %s:\n\n", device); + + printf("Required ioctls:\n"); + if (test[TestCap]) + printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(fd))); + + printf("Debug ioctls:\n"); + if (test[TestChipIdent]) + printf("\ttest VIDIOC_G_CHIP_IDENT: %s\n", ok(testChipIdent(fd))); + if (test[TestRegister]) + printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(fd))); + if (test[TestLogStatus]) + printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(fd))); + + close(fd); + exit(app_result); +} diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index ae9293414..1c765cf96 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -93,6 +93,7 @@ enum Option { OptListFormats, OptLogStatus, OptVerbose, + OptSilent, OptGetVideoOutFormat, OptSetVideoOutFormat, OptGetSlicedVbiCap, @@ -291,15 +292,15 @@ static void usage(void) " set the video capture format [VIDIOC_S_FMT]\n" " pixelformat is either the format index as reported by\n" " --list-formats, or the fourcc value as a string\n" - " --verbose turn on verbose ioctl error reporting.\n" + " --silent only set the result code, do not print any messages\n" + " --verbose turn on verbose ioctl status reporting\n" "\n"); printf("Uncommon options:\n" " --get-fmt-video-out\n" " query the video output format [VIDIOC_G_FMT]\n" " --set-fmt-video-out=width=<w>,height=<h>\n" " set the video output format [VIDIOC_S_FMT]\n" - " --get-fmt-overlay\n" - " query the video overlay format [VIDIOC_G_FMT]\n" + " --get-fmt-overlay query the video overlay format [VIDIOC_G_FMT]\n" " --get-fmt-output-overlay\n" " query the video output overlay format [VIDIOC_G_FMT]\n" " --set-fmt-output-overlay=chromakey=<key>,global_alpha=<alpha>\n" @@ -1017,12 +1018,11 @@ static int doioctl(int fd, int request, void *parm, const char *name) if (retVal < 0) { app_result = -1; } - if (!options[OptVerbose]) return retVal; - printf("%s: ", name); + if (options[OptSilent]) return retVal; if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); + printf("%s: failed: %s\n", name, strerror(errno)); + else if (options[OptVerbose]) + printf("%s: ok\n", name); return retVal; } diff --git a/v4l2-apps/util/bttv-dbg.h b/v4l2-apps/util/v4l2-dbg-bttv.h index 02f829773..cf6274284 100644 --- a/v4l2-apps/util/bttv-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-bttv.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define BTTV_IDENT "bttv" diff --git a/v4l2-apps/util/em28xx-dbg.h b/v4l2-apps/util/v4l2-dbg-em28xx.h index 3d3600c44..c5117c6e7 100644 --- a/v4l2-apps/util/em28xx-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-em28xx.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define EM28XX_IDENT "em28xx" diff --git a/v4l2-apps/util/saa7134-dbg.h b/v4l2-apps/util/v4l2-dbg-saa7134.h index aee29da76..70fd4e068 100644 --- a/v4l2-apps/util/saa7134-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-saa7134.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define SAA7134_IDENT "saa7134" diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index 2561f42b2..e0d6153fe 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -42,6 +42,48 @@ #include <map> #include <string> +#include "v4l2-dbg-bttv.h" +#include "v4l2-dbg-saa7134.h" +#include "v4l2-dbg-em28xx.h" + +#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) + +struct board_list { + const char *name; + int prefix; /* Register prefix size */ + const struct board_regs *regs; + int regs_size; + const struct board_regs *alt_regs; + int alt_regs_size; +}; + +static const struct board_list boards[] = { + { /* From bttv-dbg.h */ + BTTV_IDENT, + sizeof(BTTV_PREFIX) - 1, + bt8xx_regs, + ARRAY_SIZE(bt8xx_regs), + bt8xx_regs_other, + ARRAY_SIZE(bt8xx_regs_other), + }, + { /* From saa7134-dbg.h */ + SAA7134_IDENT, + sizeof(SAA7134_PREFIX) - 1, + saa7134_regs, + ARRAY_SIZE(saa7134_regs), + NULL, + 0, + }, + { /* From em28xx-dbg.h */ + EM28XX_IDENT, + sizeof(EM28XX_PREFIX) - 1, + em28xx_regs, + ARRAY_SIZE(em28xx_regs), + NULL, + 0, + }, +}; + struct driverid { const char *name; unsigned id; @@ -64,19 +106,21 @@ extern struct chipid chipids[]; In general the lower case is used to set something and the upper case is used to retrieve a setting. */ enum Option { - OptListRegisters = 'R', - OptSetRegister = 'r', - OptSetSlicedVbiFormat = 'b', + OptListRegisters = 'l', + OptGetRegister = 'g', + OptSetRegister = 's', OptSetDevice = 'd', OptGetDriverInfo = 'D', - OptScanChipIdents = 'C', - OptGetChipIdent = 'c', + OptChip = 'c', + OptScanChipIdents = 'S', + OptGetChipIdent = 'i', OptSetStride = 'w', OptHelp = 'h', OptLogStatus = 128, OptVerbose, OptListDriverIDs, + OptListSymbols, OptLast = 256 }; @@ -87,56 +131,56 @@ static unsigned capabilities; static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"help", no_argument, 0, OptHelp}, - {"list-registers", required_argument, 0, OptListRegisters}, + {"list-registers", optional_argument, 0, OptListRegisters}, + {"get-register", required_argument, 0, OptGetRegister}, {"set-register", required_argument, 0, OptSetRegister}, + {"chip", required_argument, 0, OptChip}, {"scan-chip-idents", no_argument, 0, OptScanChipIdents}, {"get-chip-ident", required_argument, 0, OptGetChipIdent}, {"info", no_argument, 0, OptGetDriverInfo}, {"verbose", no_argument, 0, OptVerbose}, {"log-status", no_argument, 0, OptLogStatus}, {"list-driverids", no_argument, 0, OptListDriverIDs}, + {"list-symbols", no_argument, 0, OptListSymbols}, {"wide", required_argument, 0, OptSetStride}, {0, 0, 0, 0} }; static void usage(void) { - printf("Usage:\n"); - printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); - printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); - printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); - printf(" -h, --help display this help message\n"); - printf(" --verbose turn on verbose ioctl error reporting.\n"); - printf(" -R, --list-registers=type=<host/i2cdrv/i2caddr>,chip=<chip>[,min=<addr>,max=<addr>] \n"); - printf(" dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n"); - printf(" -r, --set-register=type=<host/i2cdrv/i2caddr>,chip=<chip>,reg=<addr>,val=<val>\n"); - printf(" set the register [VIDIOC_DBG_S_REGISTER]\n"); - printf(" -C, --scan-chip-idents\n"); - printf(" Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -c, --get-chip-ident=type=<host/i2cdrv/i2caddr>,chip=<chip>\n"); - printf(" Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -w, --wide=<reg length>\n"); - printf(" Sets step between two registers\n"); - printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); - printf(" --list-driverids list the known I2C driver IDs for use with the i2cdrv type\n"); - printf("\n"); - printf(" if type == host, then <chip> is the host's chip ID (default 0)\n"); - printf(" if type == i2cdrv (default), then <chip> is the I2C driver name or ID\n"); - printf(" if type == i2caddr, then <chip> is the 7-bit I2C address\n"); + printf("Usage: v4l2-dbg [options] [values]\n" + " -D, --info Show driver info [VIDIOC_QUERYCAP]\n" + " -d, --device=<dev> Use device <dev> instead of /dev/video0\n" + " If <dev> is a single digit, then /dev/video<dev> is used\n" + " -h, --help Display this help message\n" + " --verbose Turn on verbose ioctl error reporting\n" + " -c, --chip=<chip> The chip identifier to use with other commands\n" + " It can be one of:\n" + " I2C driver ID (see --list-driverids)\n" + " I2C 7-bit address\n" + " host<num>: host chip number <num>\n" + " host (default): same as host0\n" + " -l, --list-registers[=min=<addr>[,max=<addr>]]\n" + " Dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n" + " -g, --get-register=<addr>\n" + " Get the specified register [VIDIOC_DBG_G_REGISTER]\n" + " -s, --set-register=<addr>\n" + " Set the register with the commandline arguments\n" + " The register will autoincrement [VIDIOC_DBG_S_REGISTER]\n" + " -S, --scan-chip-idents\n" + " Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n" + " -i, --get-chip-ident\n" + " Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n" + " -w, --wide=<reg length>\n" + " Sets step between two registers\n" + " --list-symbols List the symbolic register names you can use, if any\n" + " --log-status Log the board status in the kernel log [VIDIOC_LOG_STATUS]\n" + " --list-driverids List the known I2C driver IDs for use with the i2cdrv type\n"); exit(0); } -static unsigned parse_type(const std::string &s) -{ - if (s == "host") return V4L2_CHIP_MATCH_HOST; - if (s == "i2caddr") return V4L2_CHIP_MATCH_I2C_ADDR; - return V4L2_CHIP_MATCH_I2C_DRIVER; -} - -static unsigned parse_chip(int type, const std::string &s) +static unsigned parse_chip(const std::string &s) { - if (type == V4L2_CHIP_MATCH_HOST || type == V4L2_CHIP_MATCH_I2C_ADDR || isdigit(s[0])) - return strtoul(s.c_str(), 0, 0); for (int i = 0; driverids[i].name; i++) if (!strcasecmp(s.c_str(), driverids[i].name)) return driverids[i].id; @@ -230,6 +274,56 @@ static void print_chip(struct v4l2_chip_ident *chip) printf("%-10d revision 0x%08x\n", chip->ident, chip->revision); } +static unsigned long long parse_reg(const struct board_list *curr_bd, const std::string ®) +{ + if (curr_bd) { + for (int i = 0; i < curr_bd->regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->regs[i].name + curr_bd->prefix)) { + return curr_bd->regs[i].reg; + } + } + for (int i = 0; i < curr_bd->alt_regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name + curr_bd->prefix)) { + return curr_bd->alt_regs[i].reg; + } + } + } + return strtoull(reg.c_str(), NULL, 0); +} + +static const char *binary(unsigned long long val) +{ + static char bin[80]; + char *p = bin; + int i, j; + int bits = 64; + + if ((val & 0xffffffff00000000LL) == 0) { + if ((val & 0xffff0000) == 0) { + if ((val & 0xff00) == 0) + bits = 8; + else + bits= 16; + } + else + bits = 32; + } + + for (i = bits - 1; i >= 0; i -= 8) { + for (j = i; j >= i - 7; j--) { + if (val & (1LL << j)) + *p++ = '1'; + else + *p++ = '0'; + } + *p++ = ' '; + } + p[-1] = 0; + return bin; +} + static int doioctl(int fd, int request, void *parm, const char *name) { int retVal; @@ -277,9 +371,15 @@ int main(int argc, char **argv) struct v4l2_register set_reg; struct v4l2_register get_reg; struct v4l2_chip_ident chip_id; + const struct board_list *curr_bd = NULL; char short_options[26 * 2 * 2 + 1]; int idx = 0; + std::string reg_min_arg, reg_max_arg; + std::string reg_set_arg; unsigned long long reg_min = 0, reg_max = 0; + std::vector<std::string> get_regs; + int match_type = V4L2_CHIP_MATCH_HOST; + int match_chip = 0; memset(&set_reg, 0, sizeof(set_reg)); memset(&get_reg, 0, sizeof(get_reg)); @@ -310,6 +410,7 @@ int main(int argc, char **argv) case OptHelp: usage(); return 0; + case OptSetDevice: device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { @@ -320,43 +421,44 @@ int main(int argc, char **argv) device = newdev; } break; - case OptSetRegister: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static const char * const subopts[] = { - "type", - "chip", - "reg", - "val", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - set_reg.match_type = parse_type(value); - break; - case 1: - set_reg.match_chip = parse_chip(set_reg.match_type, value); - break; - case 2: - set_reg.reg = strtoull(value, 0L, 0); - break; - case 3: - set_reg.val = strtoull(value, 0L, 0); - break; - } + case OptChip: + if (isdigit(optarg[0])) { + match_type = V4L2_CHIP_MATCH_I2C_ADDR; + match_chip = strtoul(optarg, NULL, 0); + break; + } + if (!memcmp(optarg, "host", 4)) { + match_type = V4L2_CHIP_MATCH_HOST; + match_chip = strtoul(optarg + 4, NULL, 0); + break; } + match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + match_chip = parse_chip(optarg); + if (!match_chip) { + fprintf(stderr, "unknown driver ID %s\n", optarg); + exit(-1); + } + break; + + case OptSetRegister: + reg_set_arg = optarg; + break; + + case OptGetRegister: + get_regs.push_back(optarg); + break; + case OptSetStride: forcedstride = strtoull(optarg, 0L, 0); break; + case OptListRegisters: subs = optarg; - get_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + if (subs == NULL) + break; while (*subs != '\0') { static const char * const subopts[] = { - "type", - "chip", "min", "max", NULL @@ -364,47 +466,27 @@ int main(int argc, char **argv) switch (parse_subopt(&subs, subopts, &value)) { case 0: - get_reg.match_type = parse_type(value); + reg_min_arg = value; + //if (reg_max == 0) + // reg_max = reg_min + 0xff; break; case 1: - get_reg.match_chip = parse_chip(get_reg.match_type, value); - break; - case 2: - reg_min = strtoull(value, 0L, 0); - if (reg_max == 0) - reg_max = reg_min + 0xff; - break; - case 3: - reg_max = strtoull(value, 0L, 0); + reg_max_arg = value; break; } } break; - case OptGetChipIdent: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static const char *const subopts[] = { - "type", - "chip", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - chip_id.match_type = parse_type(value); - break; - case 1: - chip_id.match_chip = parse_chip(chip_id.match_type, value); - break; - } - } + case OptGetChipIdent: + case OptListSymbols: break; + case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); usage(); return 1; + case '?': fprintf(stderr, "Unknown argument `%s'\n", argv[optind]); @@ -412,14 +494,6 @@ int main(int argc, char **argv) return 1; } } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } if ((fd = open(device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", device, @@ -442,15 +516,33 @@ int main(int argc, char **argv) printf("%s", cap2s(vcap.capabilities).c_str()); } + for (int board = ARRAY_SIZE(boards) - 1; board >= 0; board--) { + if (!strcasecmp((char *)vcap.driver, boards[board].name)) { + curr_bd = &boards[board]; + break; + } + } + /* Set options */ if (options[OptSetRegister]) { - if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, - "VIDIOC_DBG_S_REGISTER") == 0) - printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.match_type = match_type; + set_reg.match_chip = match_chip; + if (optind >= argc) + usage(); + set_reg.reg = parse_reg(curr_bd, reg_set_arg); + while (optind < argc) { + set_reg.val = strtoull(argv[optind++], NULL, 0); + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.reg++; + } } if (options[OptGetChipIdent]) { + chip_id.match_type = match_type; + chip_id.match_chip = match_chip; if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0) print_chip(&chip_id); } @@ -462,7 +554,7 @@ int main(int argc, char **argv) chip_id.match_chip = 0; while (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0 && chip_id.ident) { - printf("host 0x%x: ", chip_id.match_chip); + printf("host%d: ", chip_id.match_chip); print_chip(&chip_id); chip_id.match_chip++; } @@ -477,8 +569,30 @@ int main(int argc, char **argv) } } + if (options[OptGetRegister]) { + int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; + printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); + + for (std::vector<std::string>::iterator iter = get_regs.begin(); + iter != get_regs.end(); ++iter) { + get_reg.reg = parse_reg(curr_bd, *iter); + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, &get_reg) < 0) + fprintf(stderr, "ioctl: VIDIOC_DBG_G_REGISTER " + "failed for 0x%llx\n", get_reg.reg); + else + printf("%llx = %llxh = %lldd = %sb\n", get_reg.reg, + get_reg.val, get_reg.val, binary(get_reg.val)); + } + } + if (options[OptListRegisters]) { int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; if (forcedstride) { stride = forcedstride; } else { @@ -487,7 +601,12 @@ int main(int argc, char **argv) } printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); - if (reg_max != 0) { + if (!reg_min_arg.empty()) { + reg_min = parse_reg(curr_bd, reg_min_arg); + if (reg_max_arg.empty()) + reg_max = reg_min + 0xff; + else + reg_max = parse_reg(curr_bd, reg_max_arg); /* Explicit memory range: just do it */ print_regs(fd, &get_reg, reg_min, reg_max, stride); goto list_done; @@ -580,6 +699,19 @@ list_done: printf("%s\n", driverids[i].name); } + if (options[OptListSymbols]) { + if (curr_bd == NULL) { + printf("No symbols found for driver %s\n", vcap.driver); + } + else { + printf("Symbols for driver %s:\n", vcap.driver); + for (int i = 0; i < curr_bd->regs_size; i++) + printf("0x%08x: %s\n", curr_bd->regs[i], curr_bd->regs[i].name); + for (int i = 0; i < curr_bd->alt_regs_size; i++) + printf("0x%08x: %s\n", curr_bd->alt_regs[i], curr_bd->alt_regs[i].name); + } + } + close(fd); exit(0); } diff --git a/v4l2-apps/util/v4l-board-dbg.h b/v4l2-apps/util/v4l2-dbg.h index d7e7a9ef3..06ceb0ea2 100644 --- a/v4l2-apps/util/v4l-board-dbg.h +++ b/v4l2-apps/util/v4l2-dbg.h @@ -14,11 +14,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _V4L_BOARD_DBG -#define _V4L_BOARD_DBG +#ifndef _V4L2_DBG_H_ +#define _V4L2_DBG_H_ + struct board_regs { unsigned int reg; - char *name; + const char *name; int size; }; + #endif diff --git a/v4l2-apps/util/xc3028-firmware/Makefile b/v4l2-apps/util/xc3028-firmware/Makefile index 102f712fd..6894e984e 100644 --- a/v4l2-apps/util/xc3028-firmware/Makefile +++ b/v4l2-apps/util/xc3028-firmware/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = firmware-tool |