Files
video-setup/dev/cli/v4l2_ctrl_cli.c
mikael-lovqvists-claude-agent a29c556851 Add common, media_ctrl and v4l2_ctrl modules with CLI drivers and docs
Modules (src/modules/):
- common/error: App_Error struct with structured detail union, error codes,
  app_error_print(); designed for future upgrade to preprocessor-generated
  location codes
- media_ctrl: media device enumeration, topology query (entities/pads/links),
  link enable/disable via Media Controller API (/dev/media*)
- v4l2_ctrl: control enumeration (with menu item fetching), get/set via
  V4L2 ext controls API, device discovery (/dev/video*)

All modules use -std=c11 -D_GNU_SOURCE, build artifacts go to build/ only.
Kernel-version-dependent constants guarded with #ifdef + #warning.

CLI drivers (dev/cli/):
- media_ctrl_cli: list, info, topology, set-link subcommands
- v4l2_ctrl_cli: list, controls, get, set subcommands

Docs (docs/cli/):
- media_ctrl_cli.md and v4l2_ctrl_cli.md with usage, examples, and
  context within the video routing system

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:40:37 +00:00

207 lines
4.4 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
#include "v4l2_ctrl.h"
static void usage(const char *prog) {
fprintf(stderr,
"Usage: %s <command> [args]\n"
"\n"
"Commands:\n"
" list List all /dev/video* devices\n"
" controls <device> List all controls with current values\n"
" get <device> <id> Get a control value by ID (decimal or 0x hex)\n"
" set <device> <id>=<value>\n"
" Set a control value\n"
"\n"
"Examples:\n"
" %s list\n"
" %s controls /dev/video0\n"
" %s get /dev/video0 0x00980900\n"
" %s set /dev/video0 0x00980900=128\n",
prog, prog, prog, prog, prog);
}
/* --- list --- */
static void on_device_found(const char *path, void *userdata) {
(void)userdata;
printf(" %s\n", path);
}
static int cmd_list(void) {
printf("Video devices:\n");
struct App_Error err = v4l2_ctrl_find_devices(on_device_found, NULL);
if (!APP_IS_OK(err)) {
app_error_print(&err);
return 1;
}
return 0;
}
/* --- controls --- */
static void on_control(
const struct V4l2_Ctrl_Desc *desc,
uint32_t menu_count,
const struct V4l2_Menu_Item *menu_items,
void *userdata)
{
(void)userdata;
printf(" 0x%08x %-32s %-9s current=%-6d default=%-6d min=%-6d max=%-6d",
desc->id,
desc->name,
v4l2_ctrl_type_name(desc->type),
desc->current_value,
desc->default_value,
desc->min,
desc->max);
if (desc->type == CTRL_TYPE_INTEGER || desc->type == CTRL_TYPE_INTEGER64) {
printf(" step=%d", desc->step);
}
printf("\n");
for (uint32_t i = 0; i < menu_count; i++) {
if (desc->type == CTRL_TYPE_INTEGER_MENU) {
printf(" [%u] %lld\n", menu_items[i].index, (long long)menu_items[i].value);
} else {
printf(" [%u] %s\n", menu_items[i].index, menu_items[i].name);
}
}
}
static int cmd_controls(const char *device) {
struct V4l2_Ctrl_Handle *handle = NULL;
struct App_Error err = v4l2_ctrl_open(device, &handle);
if (!APP_IS_OK(err)) {
app_error_print(&err);
return 1;
}
printf("Controls on %s:\n", device);
err = v4l2_ctrl_enumerate(handle, on_control, NULL);
if (!APP_IS_OK(err)) {
app_error_print(&err);
v4l2_ctrl_close(handle);
return 1;
}
v4l2_ctrl_close(handle);
return 0;
}
/* --- get --- */
static int cmd_get(const char *device, const char *id_arg) {
uint32_t id = (uint32_t)strtoul(id_arg, NULL, 0);
struct V4l2_Ctrl_Handle *handle = NULL;
struct App_Error err = v4l2_ctrl_open(device, &handle);
if (!APP_IS_OK(err)) {
app_error_print(&err);
return 1;
}
int32_t value;
err = v4l2_ctrl_get(handle, id, &value);
if (!APP_IS_OK(err)) {
app_error_print(&err);
v4l2_ctrl_close(handle);
return 1;
}
printf("0x%08x = %d\n", id, value);
v4l2_ctrl_close(handle);
return 0;
}
/* --- set --- */
static int cmd_set(const char *device, const char *assignment) {
char id_str[64];
const char *eq = strchr(assignment, '=');
if (!eq) {
fprintf(stderr, "Expected <id>=<value>, got: %s\n", assignment);
return 1;
}
size_t id_len = (size_t)(eq - assignment);
if (id_len >= sizeof(id_str)) {
fprintf(stderr, "ID string too long\n");
return 1;
}
memcpy(id_str, assignment, id_len);
id_str[id_len] = '\0';
uint32_t id = (uint32_t)strtoul(id_str, NULL, 0);
int32_t value = (int32_t)strtol(eq + 1, NULL, 0);
struct V4l2_Ctrl_Handle *handle = NULL;
struct App_Error err = v4l2_ctrl_open(device, &handle);
if (!APP_IS_OK(err)) {
app_error_print(&err);
return 1;
}
err = v4l2_ctrl_set(handle, id, value);
if (!APP_IS_OK(err)) {
app_error_print(&err);
v4l2_ctrl_close(handle);
return 1;
}
printf("0x%08x set to %d\n", id, value);
v4l2_ctrl_close(handle);
return 0;
}
/* --- main --- */
int main(int argc, char **argv) {
if (argc < 2) {
usage(argv[0]);
return 1;
}
const char *cmd = argv[1];
if (strcmp(cmd, "list") == 0) {
return cmd_list();
}
if (strcmp(cmd, "controls") == 0) {
if (argc < 3) {
fprintf(stderr, "controls requires a device argument\n");
return 1;
}
return cmd_controls(argv[2]);
}
if (strcmp(cmd, "get") == 0) {
if (argc < 4) {
fprintf(stderr, "get requires: <device> <id>\n");
return 1;
}
return cmd_get(argv[2], argv[3]);
}
if (strcmp(cmd, "set") == 0) {
if (argc < 4) {
fprintf(stderr, "set requires: <device> <id>=<value>\n");
return 1;
}
return cmd_set(argv[2], argv[3]);
}
fprintf(stderr, "Unknown command: %s\n\n", cmd);
usage(argv[0]);
return 1;
}