From a352e034d40183ad3248233b75352a71f698771c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 25 Feb 2007 11:20:58 +0100 Subject: Add v4l2-dbg debugging utility. From: Hans Verkuil With v4l2-dbg it is possible to make register dumps of host or i2c chips. It is also possible to query what chips are available. This is just the first version, it will be improved substantially. Signed-off-by: Hans Verkuil --- v4l2-apps/util/v4l2-dbg.cpp | 516 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 v4l2-apps/util/v4l2-dbg.cpp (limited to 'v4l2-apps/util/v4l2-dbg.cpp') 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 + + 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 +#include /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* 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= use device instead of /dev/video0\n"); + printf(" if is a single digit, then /dev/video 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=,chip=[,min=,max=] \n"); + printf(" dump registers from to [VIDIOC_DBG_G_REGISTER]\n"); + printf(" -r, --set-register=type=,chip=,reg=,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=,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 is the host's chip ID (default 0)\n"); + printf(" if type == i2cdrv (default), then is the I2C driver name or ID\n"); + printf(" if type == i2caddr, then 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 */ +#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); +} -- cgit v1.2.3