int main(int argc, char **argv) { char *value, *subs; int i, forcedstride = 0; int fd = -1; /* command args */ int ch; const char *device = "/dev/video0"; /* -d device */ struct v4l2_capability vcap; /* list_cap */ struct v4l2_dbg_register set_reg; struct v4l2_dbg_register get_reg; struct v4l2_dbg_chip_info chip_info; const struct board_list *curr_bd = NULL; char short_options[26 * 2 * 3 + 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; struct v4l2_dbg_match match; char *p; match.type = V4L2_CHIP_MATCH_BRIDGE; match.addr = 0; memset(&set_reg, 0, sizeof(set_reg)); memset(&get_reg, 0, sizeof(get_reg)); 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++] = ':'; } else if (long_options[i].has_arg == optional_argument) { short_options[idx++] = ':'; 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 = optarg; if (device[0] >= '0' && device[0] <= '9' && strlen(device) <= 3) { static char newdev[20]; sprintf(newdev, "/dev/video%s", device); device = newdev; } break; case OptChip: if (!memcmp(optarg, "subdev", 6) && isdigit(optarg[6])) { match.type = V4L2_CHIP_MATCH_SUBDEV; match.addr = strtoul(optarg + 6, NULL, 0); break; } if (!memcmp(optarg, "bridge", 6)) { match.type = V4L2_CHIP_MATCH_BRIDGE; match.addr = strtoul(optarg + 6, NULL, 0); break; } match.type = V4L2_CHIP_MATCH_BRIDGE; match.addr = 0; 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; if (subs == NULL) break; while (*subs != '\0') { static const char * const subopts[] = { "min", "max", NULL }; switch (parse_subopt(&subs, subopts, &value)) { case 0: reg_min_arg = value; //if (reg_max == 0) // reg_max = reg_min + 0xff; break; case 1: reg_max_arg = value; break; } } break; 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]); usage(); return 1; } } if ((fd = open(device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", device, strerror(errno)); exit(1); } 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.%d.%d\n", vcap.version >> 16, (vcap.version >> 8) & 0xff, vcap.version & 0xff); printf("\tCapabilities : 0x%08X\n", vcap.capabilities); printf("%s", cap2s(vcap.capabilities).c_str()); } chip_info.name[0] = '\0'; if (options[OptChip]) { /* try to figure out which chip it is */ chip_info.match = match; if (doioctl(fd, VIDIOC_DBG_G_CHIP_INFO, &chip_info, "VIDIOC_DBG_G_CHIP_INFO") != 0) chip_info.name[0] = '\0'; if (!strncasecmp(match.name, "ac97", 4)) { curr_bd = &boards[AC97_BOARD]; } else { for (int board = ARRAY_SIZE(boards) - 1; board >= 0; board--) { if (!strcasecmp(chip_info.name, boards[board].name)) { curr_bd = &boards[board]; break; } } } } /* Set options */ if (options[OptSetRegister]) { set_reg.match = match; if (optind >= argc) usage(); set_reg.reg = parse_reg(curr_bd, reg_set_arg); while (optind < argc) { unsigned size = 0; if (doioctl(fd, VIDIOC_DBG_G_REGISTER, &set_reg, "VIDIOC_DBG_G_REGISTER") >= 0) size = set_reg.size; set_reg.val = strtoull(argv[optind++], NULL, 0); if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, "VIDIOC_DBG_S_REGISTER") >= 0) { const char *name = reg_name(curr_bd, set_reg.reg); printf("Register "); if (name) printf("%s (0x%08llx)", name, set_reg.reg); else printf("0x%08llx", set_reg.reg); printf(" set to 0x%llx\n", set_reg.val); } else { printf("Failed to set register 0x%08llx value 0x%llx: %s\n", set_reg.reg, set_reg.val, strerror(errno)); } set_reg.reg += size ? : (forcedstride ? : 1); } } if (options[OptScanChips]) { chip_info.match.type = V4L2_CHIP_MATCH_BRIDGE; chip_info.match.addr = 0; while (doioctl(fd, VIDIOC_DBG_G_CHIP_INFO, &chip_info, "VIDIOC_DBG_G_CHIP_INFO") == 0 && chip_info.name[0]) { printf("bridge%d: ", chip_info.match.addr); print_name(&chip_info); chip_info.match.addr++; } chip_info.match.type = V4L2_CHIP_MATCH_SUBDEV; chip_info.match.addr = 0; while (doioctl(fd, VIDIOC_DBG_G_CHIP_INFO, &chip_info, "VIDIOC_DBG_G_CHIP_INFO") == 0 && chip_info.name[0]) { printf("subdev%d: ", chip_info.match.addr++); print_name(&chip_info); } } if (options[OptGetRegister]) { get_reg.match = match; 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 { const char *name = reg_name(curr_bd, get_reg.reg); printf("Register "); if (name) printf("%s (0x%08llx)", name, get_reg.reg); else printf("0x%08llx", get_reg.reg); printf(" = %llxh (%lldd %sb)\n", get_reg.val, get_reg.val, binary(get_reg.val)); } } } if (options[OptListRegisters]) { std::string name; int stride = 1; get_reg.match = match; if (forcedstride) { stride = forcedstride; } else if (get_reg.match.type == V4L2_CHIP_MATCH_BRIDGE) { stride = 4; } printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); if (curr_bd) { if (reg_min_arg.empty()) reg_min = 0; else reg_min = parse_reg(curr_bd, reg_min_arg); if (reg_max_arg.empty()) reg_max = (1ll << 32) - 1; else reg_max = parse_reg(curr_bd, reg_max_arg); for (int i = 0; i < curr_bd->regs_size; i++) { if (reg_min_arg.empty() || ((curr_bd->regs[i].reg >= reg_min) && curr_bd->regs[i].reg <= reg_max)) { get_reg.reg = curr_bd->regs[i].reg; 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 { const char *name = reg_name(curr_bd, get_reg.reg); printf("Register "); if (name) printf("%s (0x%08llx)", name, get_reg.reg); else printf("0x%08llx", get_reg.reg); printf(" = %llxh (%lldd %sb)\n", get_reg.val, get_reg.val, binary(get_reg.val)); } } } goto list_done; } 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; } p = strchr(chip_info.name, ' '); if (p) *p = '\0'; name = chip_info.name; if (name == "saa7115") { print_regs(fd, &get_reg, 0, 0xff, stride); } else if (name == "saa717x") { // FIXME: use correct reg regions print_regs(fd, &get_reg, 0, 0xff, stride); } else if (name == "saa7127") { print_regs(fd, &get_reg, 0, 0x7f, stride); } else if (name == "ov7670") { print_regs(fd, &get_reg, 0, 0x89, stride); } else if (name == "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); } else if (name == "cs5345") { print_regs(fd, &get_reg, 1, 0x10, stride); } else if (name == "cx23416") { print_regs(fd, &get_reg, 0x02000000, 0x020000ff, stride); } else if (name == "cx23418") { print_regs(fd, &get_reg, 0x02c40000, 0x02c409c7, stride); } else if (name == "cafe") { print_regs(fd, &get_reg, 0, 0x43, stride); print_regs(fd, &get_reg, 0x88, 0x8f, stride); print_regs(fd, &get_reg, 0xb4, 0xbb, stride); print_regs(fd, &get_reg, 0x3000, 0x300c, stride); } else { /* unknown chip, dump 0-0xff by default */ print_regs(fd, &get_reg, 0, 0xff, stride); } } list_done: if (options[OptLogStatus]) { static char buf[40960]; int len = -1; if (doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS") == 0) { printf("\nStatus Log:\n\n"); #ifdef HAVE_KLOGCTL len = klogctl(3, buf, sizeof(buf) - 1); #endif if (len >= 0) { bool found_status = false; char *p = buf; char *q; buf[len] = 0; while ((q = strstr(p, "START STATUS"))) { found_status = true; p = q + 1; } if (found_status) { while (p > buf && *p != '<') p--; q = p; while ((q = strstr(q, "<6>"))) { memcpy(q, " ", 3); } printf("%s", p); } } } } if (options[OptListSymbols]) { if (curr_bd == NULL) { printf("No symbols found for driver %s\n", vcap.driver); } else { printf("Symbols for driver %s:\n", curr_bd->name); for (int i = 0; i < curr_bd->regs_size; i++) printf("0x%08x: %s\n", curr_bd->regs[i].reg, 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].reg, curr_bd->alt_regs[i].name); } } close(fd); exit(0); }
int main(int argc, char **argv) { int i; struct node node = { -1 }; struct node video_node = { -1 }; struct node video_node2 = { -1 }; struct node radio_node = { -1, true }; struct node radio_node2 = { -1, true }; struct node vbi_node = { -1 }; struct node vbi_node2 = { -1 }; /* command args */ int ch; const char *device = NULL; const char *video_device = NULL; /* -d device */ const char *radio_device = NULL; /* -r device */ const char *vbi_device = NULL; /* -V device */ struct v4l2_capability vcap; /* list_cap */ char short_options[26 * 2 * 2 + 1]; int idx = 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: video_device = optarg; if (video_device[0] >= '0' && video_device[0] <= '9' && strlen(video_device) <= 3) { static char newdev[20]; sprintf(newdev, "/dev/video%s", video_device); video_device = newdev; } break; case OptSetRadioDevice: radio_device = optarg; if (radio_device[0] >= '0' && radio_device[0] <= '9' && strlen(radio_device) <= 3) { static char newdev[20]; sprintf(newdev, "/dev/radio%s", radio_device); radio_device = newdev; } break; case OptSetVbiDevice: vbi_device = optarg; if (vbi_device[0] >= '0' && vbi_device[0] <= '9' && strlen(vbi_device) <= 3) { static char newdev[20]; sprintf(newdev, "/dev/vbi%s", vbi_device); vbi_device = newdev; } break; case OptNoWarnings: show_warnings = false; break; case OptVerbose: show_info = true; 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; } wrapper = options[OptUseWrapper]; struct utsname uts; int v1, v2, v3; uname(&uts); sscanf(uts.release, "%d.%d.%d", &v1, &v2, &v3); if (v1 == 2 && v2 == 6) kernel_version = v3; if (!video_device && !radio_device && !vbi_device) video_device = "/dev/video0"; if (video_device && (video_node.fd = test_open(video_device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", video_device, strerror(errno)); exit(1); } if (radio_device && (radio_node.fd = test_open(radio_device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", radio_device, strerror(errno)); exit(1); } if (vbi_device && (vbi_node.fd = test_open(vbi_device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", vbi_device, strerror(errno)); exit(1); } if (video_node.fd >= 0) { node.fd = video_node.fd; device = video_device; node.is_video = true; } else if (radio_node.fd >= 0) { node.fd = radio_node.fd; device = radio_device; node.is_radio = true; printf("is radio\n"); } else if (vbi_node.fd >= 0) { node.fd = vbi_node.fd; device = vbi_device; node.is_vbi = true; } node.device = device; doioctl(&node, VIDIOC_QUERYCAP, &vcap); if (vcap.capabilities & V4L2_CAP_DEVICE_CAPS) node.caps = vcap.device_caps; else node.caps = vcap.capabilities; if (node.caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_RDS_CAPTURE | V4L2_CAP_RADIO | V4L2_CAP_TUNER)) node.has_inputs = true; if (node.caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR)) node.has_outputs = true; if (node.caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_RDS_CAPTURE)) node.can_capture = true; if (node.caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_RDS_OUTPUT)) node.can_output = true; /* Information Opts */ if (kernel_version) printf("Running on 2.6.%d\n\n", kernel_version); 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.%d.%d\n", vcap.version >> 16, (vcap.version >> 8) & 0xff, vcap.version & 0xff); printf("\tCapabilities : 0x%08X\n", vcap.capabilities); printf("%s", cap2s(vcap.capabilities).c_str()); if (vcap.capabilities & V4L2_CAP_DEVICE_CAPS) { printf("\tDevice Caps : 0x%08X\n", vcap.device_caps); printf("%s", cap2s(vcap.device_caps).c_str()); } printf("\nCompliance test for device %s (%susing libv4l2):\n\n", device, wrapper ? "" : "not "); /* Required ioctls */ printf("Required ioctls:\n"); printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&node))); printf("\n"); /* Multiple opens */ printf("Allow for multiple opens:\n"); if (video_device) { video_node2 = node; printf("\ttest second video open: %s\n", ok((video_node2.fd = test_open(video_device, O_RDWR)) < 0)); if (video_node2.fd >= 0) { printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&video_node2))); printf("\ttest VIDIOC_G/S_PRIORITY: %s\n", ok(testPrio(&node, &video_node2))); node.node2 = &video_node2; } } if (radio_device) { radio_node2 = node; printf("\ttest second radio open: %s\n", ok((radio_node2.fd = test_open(radio_device, O_RDWR)) < 0)); if (radio_node2.fd >= 0) { printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&radio_node2))); printf("\ttest VIDIOC_G/S_PRIORITY: %s\n", ok(testPrio(&node, &radio_node2))); node.node2 = &video_node2; } } if (vbi_device) { vbi_node2 = node; printf("\ttest second vbi open: %s\n", ok((vbi_node2.fd = test_open(vbi_device, O_RDWR)) < 0)); if (vbi_node2.fd >= 0) { printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&vbi_node2))); printf("\ttest VIDIOC_G/S_PRIORITY: %s\n", ok(testPrio(&node, &vbi_node2))); node.node2 = &video_node2; } } printf("\n"); /* Debug ioctls */ printf("Debug ioctls:\n"); printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(&node))); printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(&node))); printf("\n"); /* Input ioctls */ printf("Input ioctls:\n"); printf("\ttest VIDIOC_G/S_TUNER: %s\n", ok(testTuner(&node))); printf("\ttest VIDIOC_G/S_FREQUENCY: %s\n", ok(testTunerFreq(&node))); printf("\ttest VIDIOC_S_HW_FREQ_SEEK: %s\n", ok(testTunerHwSeek(&node))); printf("\ttest VIDIOC_ENUMAUDIO: %s\n", ok(testEnumInputAudio(&node))); printf("\ttest VIDIOC_G/S/ENUMINPUT: %s\n", ok(testInput(&node))); printf("\ttest VIDIOC_G/S_AUDIO: %s\n", ok(testInputAudio(&node))); printf("\tInputs: %d Audio Inputs: %d Tuners: %d\n", node.inputs, node.audio_inputs, node.tuners); printf("\n"); /* Output ioctls */ printf("Output ioctls:\n"); printf("\ttest VIDIOC_G/S_MODULATOR: %s\n", ok(testModulator(&node))); printf("\ttest VIDIOC_G/S_FREQUENCY: %s\n", ok(testModulatorFreq(&node))); printf("\ttest VIDIOC_ENUMAUDOUT: %s\n", ok(testEnumOutputAudio(&node))); printf("\ttest VIDIOC_G/S/ENUMOUTPUT: %s\n", ok(testOutput(&node))); printf("\ttest VIDIOC_G/S_AUDOUT: %s\n", ok(testOutputAudio(&node))); printf("\tOutputs: %d Audio Outputs: %d Modulators: %d\n", node.outputs, node.audio_outputs, node.modulators); printf("\n"); /* Control ioctls */ printf("Control ioctls:\n"); printf("\ttest VIDIOC_QUERYCTRL/MENU: %s\n", ok(testQueryControls(&node))); printf("\ttest VIDIOC_G/S_CTRL: %s\n", ok(testSimpleControls(&node))); printf("\ttest VIDIOC_G/S/TRY_EXT_CTRLS: %s\n", ok(testExtendedControls(&node))); printf("\ttest VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: %s\n", ok(testControlEvents(&node))); printf("\ttest VIDIOC_G/S_JPEGCOMP: %s\n", ok(testJpegComp(&node))); printf("\tStandard Controls: %d Private Controls: %d\n", node.std_controls, node.priv_controls); printf("\n"); /* I/O configuration ioctls */ printf("Input/Output configuration ioctls:\n"); printf("\ttest VIDIOC_ENUM/G/S/QUERY_STD: %s\n", ok(testStd(&node))); printf("\ttest VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: %s\n", ok(testTimings(&node))); printf("\ttest VIDIOC_DV_TIMINGS_CAP: %s\n", ok(testTimingsCap(&node))); printf("\n"); /* Format ioctls */ printf("Format ioctls:\n"); printf("\ttest VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: %s\n", ok(testEnumFormats(&node))); printf("\ttest VIDIOC_G/S_PARM: %s\n", ok(testParm(&node))); printf("\ttest VIDIOC_G_FBUF: %s\n", ok(testFBuf(&node))); printf("\ttest VIDIOC_G_FMT: %s\n", ok(testGetFormats(&node))); printf("\ttest VIDIOC_TRY_FMT: %s\n", ok(testTryFormats(&node))); printf("\ttest VIDIOC_S_FMT: %s\n", ok(testSetFormats(&node))); printf("\ttest VIDIOC_G_SLICED_VBI_CAP: %s\n", ok(testSlicedVBICap(&node))); printf("\n"); /* Codec ioctls */ printf("Codec ioctls:\n"); printf("\ttest VIDIOC_(TRY_)ENCODER_CMD: %s\n", ok(testEncoder(&node))); printf("\ttest VIDIOC_G_ENC_INDEX: %s\n", ok(testEncIndex(&node))); printf("\ttest VIDIOC_(TRY_)DECODER_CMD: %s\n", ok(testDecoder(&node))); printf("\n"); /* Buffer ioctls */ printf("Buffer ioctls:\n"); printf("\ttest VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: %s\n", ok(testReqBufs(&node))); //printf("\ttest read/write: %s\n", ok(testReadWrite(&node))); printf("\n"); /* TODO: VIDIOC_CROPCAP, VIDIOC_G/S_CROP, VIDIOC_G/S_SELECTION VIDIOC_S_FBUF/OVERLAY VIDIOC_QBUF/DQBUF/QUERYBUF/PREPARE_BUFS/EXPBUF VIDIOC_STREAMON/OFF */ /* Final test report */ test_close(node.fd); if (node.node2) test_close(node.node2->fd); printf("Total: %d, Succeeded: %d, Failed: %d, Warnings: %d\n", tests_total, tests_ok, tests_total - tests_ok, warnings); exit(app_result); }