diff options
Diffstat (limited to 'v4l2-apps/util/v4l2-dbg.cpp')
-rw-r--r-- | v4l2-apps/util/v4l2-dbg.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp new file mode 100644 index 000000000..70d465dea --- /dev/null +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -0,0 +1,516 @@ +/* + Copyright (C) 2007 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 { + OptListRegisters = 'R', + OptSetRegister = 'r', + OptSetSlicedVbiFormat = 'b', + OptSetDevice = 'd', + OptGetDriverInfo = 'D', + OptListChipIdents = 'C', + OptGetChipIdent = 'c', + OptHelp = 'h', + + OptLogStatus = 128, + OptVerbose, + OptLast = 256 +}; + +static char options[OptLast]; + +static unsigned capabilities; + +static struct option long_options[] = { + {"device", required_argument, 0, OptSetDevice}, + {"help", no_argument, 0, OptHelp}, + {"list-registers", required_argument, 0, OptListRegisters}, + {"set-register", required_argument, 0, OptSetRegister}, + {"list-chip-idents", no_argument, 0, OptListChipIdents}, + {"get-chip-ident", required_argument, 0, OptGetChipIdent}, + {"info", no_argument, 0, OptGetDriverInfo}, + {"verbose", no_argument, 0, OptVerbose}, + {"log-status", no_argument, 0, OptLogStatus}, + {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, --list-chip-idents\n"); + printf(" List 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(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\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"); + 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; +} + +/* Copied from <linux/i2c-id.h> */ +#define I2C_DRIVERID_SAA711X 73 /* saa711x video encoders */ +#define I2C_DRIVERID_SAA717X 80 /* saa717x video encoder */ +#define I2C_DRIVERID_SAA7127 72 /* saa7124 video encoder */ +#define I2C_DRIVERID_CX25840 71 /* cx2584x video encoder */ + +static unsigned parse_chip(int type, 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); + if (s == "saa711x") + return I2C_DRIVERID_SAA711X; + if (s == "saa717x") + return I2C_DRIVERID_SAA717X; + if (s == "saa7127") + return I2C_DRIVERID_SAA7127; + if (s == "cx2584x") + return I2C_DRIVERID_CX25840; + return 0; +} + +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_VIDEO_OUTPUT_POS) + s += "\t\tVideo Output Position\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 void print_regs(int fd, struct v4l2_register *reg, unsigned long min, unsigned long max, int stride) +{ + unsigned long mask = stride > 1 ? 0x1f : 0x0f; + unsigned long i; + int line = 0; + + for (i = min & ~mask; i <= max; i += stride) { + if ((i & mask) == 0 && line % 32 == 0) { + if (stride == 4) + printf("\n 00 04 08 0C 10 14 18 1C"); + else + printf("\n 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); + } + + if ((i & mask) == 0) { + printf("\n%08lx: ", i); + line++; + } + if (i < min) { + printf("%*s ", 2 * stride, ""); + continue; + } + reg->reg = i; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, reg) < 0) { + fprintf(stderr, "ioctl: VIDIOC_DBG_G_REGISTER " + "failed for 0x%llx\n", reg->reg); + } else { + printf("%0*llx ", 2 * stride, reg->val); + } + usleep(1); + } + printf("\n"); +} + +static void print_chip(struct v4l2_chip_ident *chip) +{ + printf("Chip %d, revision 0x%08x\n", chip->ident, chip->revision); +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + + if (!options[OptVerbose]) return ioctl(fd, request, parm); + retVal = ioctl(fd, request, parm); + printf("%s: ", name); + if (retVal < 0) + printf("failed: %s\n", strerror(errno)); + else + printf("ok\n"); + + 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; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + + int fd = -1; + + /* command args */ + int ch; + char *device = strdup("/dev/video0"); /* -d device */ + struct v4l2_capability vcap; /* list_cap */ + struct v4l2_register set_reg; + struct v4l2_register get_reg; + struct v4l2_chip_ident chip_id; + char short_options[26 * 2 * 2 + 1]; + int idx = 0; + unsigned long long reg_min = 0, reg_max = 0; + + memset(&set_reg, 0, sizeof(set_reg)); + memset(&get_reg, 0, sizeof(get_reg)); + memset(&chip_id, 0, sizeof(chip_id)); + + if (argc == 1) { + usage(); + return 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 OptSetDevice: + device = strdup(optarg); + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + char dev = device[0]; + + sprintf(device, "/dev/video%c", dev); + } + break; + case OptSetRegister: + subs = optarg; + set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + while (*subs != '\0') { + static 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; + } + } + break; + case OptListRegisters: + subs = optarg; + get_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + while (*subs != '\0') { + static char *const subopts[] = { + "type", + "chip", + "min", + "max", + NULL + }; + + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + get_reg.match_type = parse_type(value); + break; + case 1: + get_reg.match_chip = parse_chip(set_reg.match_type, value); + break; + case 2: + reg_min = strtoull(value, 0L, 0); + break; + case 3: + reg_max = strtoull(value, 0L, 0); + break; + } + } + break; + case OptGetChipIdent: + subs = optarg; + set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + while (*subs != '\0') { + static 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(set_reg.match_type, value); + break; + } + } + 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; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + free(device); + + doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + capabilities = vcap.capabilities; + + /* 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()); + } + + /* 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); + } + + if (options[OptGetChipIdent]) { + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0) + print_chip(&chip_id); + } + + if (options[OptListChipIdents]) { + int i; + + chip_id.match_type = V4L2_CHIP_MATCH_HOST; + chip_id.match_chip = 0; + chip_id.ident = 0; + + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0 && chip_id.ident) + print_chip(&chip_id); + + chip_id.match_type = V4L2_CHIP_MATCH_I2C_ADDR; + for (i = 0; i < 128; i++) { + chip_id.match_chip = i; + chip_id.ident = 0; + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0 && chip_id.ident) { + printf("0x%02x: ", i); + print_chip(&chip_id); + } + } + } + + if (options[OptListRegisters]) { + int stride = 1; + + if (get_reg.match_type == V4L2_CHIP_MATCH_HOST) stride = 4; + printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); + if (reg_max == 0) { + switch (get_reg.match_chip) { + case I2C_DRIVERID_SAA711X: + print_regs(fd, &get_reg, 0, 0xff, stride); + break; + case I2C_DRIVERID_SAA717X: + // FIXME: use correct reg regions + print_regs(fd, &get_reg, 0, 0xff, stride); + break; + case I2C_DRIVERID_SAA7127: + print_regs(fd, &get_reg, 0, 0x7f, stride); + break; + case I2C_DRIVERID_CX25840: + print_regs(fd, &get_reg, 0, 2, stride); + print_regs(fd, &get_reg, 0x100, 0x15f, stride); + print_regs(fd, &get_reg, 0x200, 0x23f, stride); + print_regs(fd, &get_reg, 0x400, 0x4bf, stride); + print_regs(fd, &get_reg, 0x800, 0x9af, stride); + break; + case 0: + print_regs(fd, &get_reg, 0x02000000, 0x020000ff, stride); + break; + } + } + else { + print_regs(fd, &get_reg, reg_min, reg_max, stride); + } + } + + if (options[OptLogStatus]) { + static char buf[40960]; + int len; + + if (doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS") == 0) { + printf("\nStatus Log:\n\n"); + len = klogctl(3, buf, sizeof(buf) - 1); + if (len >= 0) { + char *p = buf; + char *q; + + buf[len] = 0; + while ((q = strstr(p, "START STATUS CARD #"))) { + p = q + 1; + } + if (p) { + while (p > buf && *p != '<') p--; + q = p; + while ((q = strstr(q, "<6>"))) { + memcpy(q, " ", 3); + } + printf("%s", p); + } + } + } + } + + close(fd); + exit(0); +} |