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>
This commit is contained in:
206
dev/cli/v4l2_ctrl_cli.c
Normal file
206
dev/cli/v4l2_ctrl_cli.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user