The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=94f24c0ee23964f751fabc908a7564a388c9d1a1
commit 94f24c0ee23964f751fabc908a7564a388c9d1a1 Author: Abdelkader Boudih <[email protected]> AuthorDate: 2026-07-08 05:31:46 +0000 Commit: Adrian Chadd <[email protected]> CommitDate: 2026-07-08 05:32:44 +0000 fwcamctl: add control utility for fwcam(4) IIDC FireWire cameras fwcamctl provides userland access to /dev/fwcam0. Supported subcommands: info (camera state, format, mode, rate, features), snap (capture a frame as PPM), mode (set format/mode/rate), and feat (get/set camera feature registers). snap converts YUV422, YUV411, YUV444, RGB8, and Mono8 pixel formats to RGB24 PPM with no external dependencies. A configurable frame skip (default 5) allows auto-exposure and auto-white-balance to settle before capture. (from adrian - yes, I've successfully captured images from an Apple isight camera on firewire with this tool and in-tree support.) Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D57914 --- sys/dev/firewire/fwcam.c | 21 -- sys/dev/firewire/fwcam.h | 64 ++++++ usr.sbin/Makefile | 1 + usr.sbin/fwcamctl/Makefile | 9 + usr.sbin/fwcamctl/fwcamctl.8 | 156 +++++++++++++++ usr.sbin/fwcamctl/fwcamctl.c | 450 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 680 insertions(+), 21 deletions(-) diff --git a/sys/dev/firewire/fwcam.c b/sys/dev/firewire/fwcam.c index 226295154e6e..27451316d613 100644 --- a/sys/dev/firewire/fwcam.c +++ b/sys/dev/firewire/fwcam.c @@ -149,27 +149,6 @@ fwcam_write_quadlet(struct fwcam_softc *sc, uint32_t offset, uint32_t val) return (err); } -#if 0 -static const char * -fwcam_format_name(int format) -{ - static const char *names[] = { - "VGA (Format_0)", - "Super VGA 1 (Format_1)", - "Super VGA 2 (Format_2)", - "Reserved", - "Reserved", - "Reserved", - "Still Image (Format_6)", - "Partial Image (Format_7)", - }; - - if (format >= 0 && format <= 7) - return (names[format]); - return ("Unknown"); -} -#endif - static int fwcam_read_capabilities(struct fwcam_softc *sc) { diff --git a/sys/dev/firewire/fwcam.h b/sys/dev/firewire/fwcam.h index 841683bbc37d..79b0dd3b5ec6 100644 --- a/sys/dev/firewire/fwcam.h +++ b/sys/dev/firewire/fwcam.h @@ -207,6 +207,70 @@ struct fwcam_info { #define FWCAM_STATE_STREAMING 2 #define FWCAM_STATE_DETACHING 3 +/* IIDC format/mode/rate/feature/state name tables for userland and driver */ +#define FWCAM_FMT_NMAX 8 +#define FWCAM_RATE_NMAX 8 + +static const char * const fwcam_fmt_names[FWCAM_FMT_NMAX] = { + "VGA (Format_0)", + "Super VGA 1 (Format_1)", + "Super VGA 2 (Format_2)", + "Reserved", + "Reserved", + "Reserved", + "Still Image (Format_6)", + "Partial Image (Format_7)", +}; + +static const char * const fwcam_rate_names[FWCAM_RATE_NMAX] = { + "1.875 fps", "3.75 fps", "7.5 fps", "15 fps", + "30 fps", "60 fps", "120 fps", "240 fps", +}; + +static const char * const fwcam_feat_names[FWCAM_FEAT_MAX] = { + [FWCAM_FEAT_BRIGHTNESS] = "brightness", + [FWCAM_FEAT_AUTO_EXPOSURE] = "auto_exposure", + [FWCAM_FEAT_SHARPNESS] = "sharpness", + [FWCAM_FEAT_WHITE_BALANCE] = "white_balance", + [FWCAM_FEAT_HUE] = "hue", + [FWCAM_FEAT_SATURATION] = "saturation", + [FWCAM_FEAT_GAMMA] = "gamma", + [FWCAM_FEAT_SHUTTER] = "shutter", + [FWCAM_FEAT_GAIN] = "gain", + [FWCAM_FEAT_IRIS] = "iris", + [FWCAM_FEAT_FOCUS] = "focus", + [FWCAM_FEAT_TEMPERATURE] = "temperature", + [FWCAM_FEAT_TRIGGER] = "trigger", + [FWCAM_FEAT_ZOOM] = "zoom", + [FWCAM_FEAT_PAN] = "pan", + [FWCAM_FEAT_TILT] = "tilt", +}; + +static const char * const fwcam_state_names[] = { + [FWCAM_STATE_IDLE] = "idle", + [FWCAM_STATE_PROBED] = "probed", + [FWCAM_STATE_STREAMING] = "streaming", + [FWCAM_STATE_DETACHING] = "detaching", +}; + +/* Format_0 (VGA non-compressed) mode geometry */ +struct fwcam_fmt0_mode { + int w, h; + const char *pixfmt; +}; + +#define FWCAM_FMT0_NMODES 7 + +static const struct fwcam_fmt0_mode fwcam_fmt0_modes[FWCAM_FMT0_NMODES] = { + { 160, 120, "YUV444" }, /* mode 0 */ + { 320, 240, "YUV422" }, /* mode 1 */ + { 640, 480, "YUV411" }, /* mode 2 */ + { 640, 480, "YUV422" }, /* mode 3 */ + { 640, 480, "RGB8" }, /* mode 4 */ + { 640, 480, "Mono8" }, /* mode 5 */ + { 640, 480, "Mono16" }, /* mode 6 */ +}; + /* * Internal constants */ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 9e885d5d45c5..c55592c926a4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -27,6 +27,7 @@ SUBDIR= adduser \ extattrctl \ fifolog \ fstyp \ + fwcamctl \ fwcontrol \ fwget \ getfmac \ diff --git a/usr.sbin/fwcamctl/Makefile b/usr.sbin/fwcamctl/Makefile new file mode 100644 index 000000000000..127639c7e91f --- /dev/null +++ b/usr.sbin/fwcamctl/Makefile @@ -0,0 +1,9 @@ +PROG= fwcamctl +SRCS= fwcamctl.c +MAN= fwcamctl.8 +WARNS?= 3 + +SDIR= ${SRCTOP}/sys +CFLAGS+= -I${SDIR} + +.include <bsd.prog.mk> diff --git a/usr.sbin/fwcamctl/fwcamctl.8 b/usr.sbin/fwcamctl/fwcamctl.8 new file mode 100644 index 000000000000..389117bcc9d0 --- /dev/null +++ b/usr.sbin/fwcamctl/fwcamctl.8 @@ -0,0 +1,156 @@ +.\" Copyright (c) 2026 Abdelkader Boudih <[email protected]> +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd May 27, 2026 +.Dt FWCAMCTL 8 +.Os +.Sh NAME +.Nm fwcamctl +.Nd control utility for IIDC FireWire cameras +.Sh SYNOPSIS +.Nm +.Op Fl f Ar device +.Cm info +.Nm +.Op Fl f Ar device +.Cm snap +.Op Fl o Ar output.ppm +.Op Fl s Ar skip +.Nm +.Op Fl f Ar device +.Cm mode +.Ar format mode rate +.Nm +.Op Fl f Ar device +.Cm feat get +.Ar id +.Nm +.Op Fl f Ar device +.Cm feat set +.Ar id value Op Ar value2 +.Sh DESCRIPTION +The +.Nm +utility controls IIDC 1394-based digital cameras via the +.Xr fwcam 4 +driver. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f Ar device +Specify the camera device. +Defaults to +.Pa /dev/fwcam0 . +.El +.Pp +The subcommands are: +.Bl -tag -width indent +.It Cm info +Display camera state, current format/mode/framerate, supported formats, +basic function capabilities, and present features. +.It Cm snap Op Fl o Ar output.ppm Op Fl s Ar skip +Capture a single video frame and write it as a PPM image. +.Pp +.Bl -tag -width indent -compact +.It Fl o Ar output.ppm +Output file path. +Use +.Sq - +for stdout. +Defaults to +.Pa snap.ppm . +.It Fl s Ar skip +Number of frames to skip before capturing, to allow auto-exposure +and auto-white-balance to settle. +Defaults to 5. +.El +.Pp +Supported pixel formats: YUV444, YUV422, YUV411, RGB8, Mono8. +The frame is converted to RGB24 and written as a binary PPM (P6). +.It Cm mode Ar format mode rate +Set the camera video format, mode, and frame rate. +All arguments are integer indices per the IIDC specification. +Only Format_0 (VGA) is currently supported by the +.Xr fwcam 4 +frame assembly path. +.Pp +Format_0 modes: +.Bl -column "Mode" "Resolution" "Pixel format" -compact +.It Em Mode Ta Em Resolution Ta Em Pixel format +.It 0 Ta 160x120 Ta YUV444 +.It 1 Ta 320x240 Ta YUV422 +.It 2 Ta 640x480 Ta YUV411 +.It 3 Ta 640x480 Ta YUV422 +.It 4 Ta 640x480 Ta RGB8 +.It 5 Ta 640x480 Ta Mono8 +.El +.Pp +Frame rates: 0=1.875 1=3.75 2=7.5 3=15 4=30 5=60 fps. +.It Cm feat get Ar id +Query the camera feature with the given numeric ID and display its +inquiry (presence, on/off, auto, manual, range) and current value. +.It Cm feat set Ar id value Op Ar value2 +Set the camera feature with the given numeric ID to +.Ar value . +For the +.Sq white_balance +feature (id 3), +.Ar value +is the U/B component and +.Ar value2 +is the V/R component. +.El +.Pp +Feature IDs: +.Bl -column "ID" "Name" -compact +.It Em ID Ta Em Name +.It 0 Ta brightness +.It 1 Ta auto_exposure +.It 2 Ta sharpness +.It 3 Ta white_balance +.It 4 Ta hue +.It 5 Ta saturation +.It 6 Ta gamma +.It 7 Ta shutter +.It 8 Ta gain +.It 9 Ta iris +.It 10 Ta focus +.It 11 Ta temperature +.It 12 Ta trigger +.It 13 Ta zoom +.It 14 Ta pan +.It 15 Ta tilt +.El +.Sh EXAMPLES +Show camera information: +.Bd -literal -offset indent +fwcamctl info +.Ed +.Pp +Capture a photo to +.Pa photo.ppm , +skipping 10 frames for AE to settle: +.Bd -literal -offset indent +fwcamctl snap -o photo.ppm -s 10 +.Ed +.Pp +Switch to 320x240 YUV422 at 30 fps: +.Bd -literal -offset indent +fwcamctl mode 0 1 4 +.Ed +.Pp +Read the current gain value: +.Bd -literal -offset indent +fwcamctl feat get 8 +.Ed +.Pp +Set brightness to 200: +.Bd -literal -offset indent +fwcamctl feat set 0 200 +.Ed +.Sh SEE ALSO +.Xr fwcam 4 , +.Xr fwcontrol 8 +.Sh AUTHORS +.An Abdelkader Boudih Aq Mt [email protected] diff --git a/usr.sbin/fwcamctl/fwcamctl.c b/usr.sbin/fwcamctl/fwcamctl.c new file mode 100644 index 000000000000..af484c39dee6 --- /dev/null +++ b/usr.sbin/fwcamctl/fwcamctl.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih <[email protected]> + * + * SPDX-License-Identifier: BSD-2-Clause + * + * fwcamctl - control utility for fwcam(4) IIDC FireWire cameras + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <dev/firewire/fwcam.h> + +static const char *default_dev = "/dev/fwcam0"; + +static void +usage(void) +{ + fprintf(stderr, + "usage: fwcamctl [-f device] info\n" + " fwcamctl [-f device] snap [-o output.ppm] [-s skip]\n" + " fwcamctl [-f device] mode <format> <mode> <rate>\n" + " fwcamctl [-f device] feat get <id>\n" + " fwcamctl [-f device] feat set <id> <value> [value2]\n" + "\n" + " Feature IDs: 0=brightness 1=auto_exposure 2=sharpness\n" + " 3=white_balance 4=hue 5=saturation 6=gamma\n" + " 7=shutter 8=gain 9=iris 10=focus 11=temperature\n" + " 12=trigger 13=zoom 14=pan 15=tilt\n"); + exit(1); +} + +static inline int +clamp_u8(int v) +{ + if (v < 0) return (0); + if (v > 255) return (255); + return (v); +} + +/* + * IIDC YUV 4:2:2 → RGB24 + * IIDC spec layout: U0 Y0 V0 Y1 (UYVY, 2 pixels per 4 bytes) + */ +static void +yuv422_to_rgb(const uint8_t *src, uint8_t *dst, int w, int h) +{ + int i, n = w * h / 2; + + for (i = 0; i < n; i++) { + int cb = src[0] - 128; + int y0 = src[1]; + int cr = src[2] - 128; + int y1 = src[3]; + src += 4; + + /* pixel 0 */ + dst[0] = clamp_u8(y0 + (int)(1.402f * cr)); + dst[1] = clamp_u8(y0 - (int)(0.34414f * cb) - (int)(0.71414f * cr)); + dst[2] = clamp_u8(y0 + (int)(1.772f * cb)); + /* pixel 1 */ + dst[3] = clamp_u8(y1 + (int)(1.402f * cr)); + dst[4] = clamp_u8(y1 - (int)(0.34414f * cb) - (int)(0.71414f * cr)); + dst[5] = clamp_u8(y1 + (int)(1.772f * cb)); + dst += 6; + } +} + +/* + * IIDC YUV 4:1:1 → RGB24 + * Layout: U0 Y0 Y1 V0 Y2 Y3 (4 pixels per 6 bytes) + */ +static void +yuv411_to_rgb(const uint8_t *src, uint8_t *dst, int w, int h) +{ + int i, j, n = w * h / 4; + + for (i = 0; i < n; i++) { + int cb = src[0] - 128; + int y[4]; + y[0] = src[1]; + y[1] = src[2]; + int cr = src[3] - 128; + y[2] = src[4]; + y[3] = src[5]; + src += 6; + + for (j = 0; j < 4; j++) { + *dst++ = clamp_u8(y[j] + (int)(1.402f * cr)); + *dst++ = clamp_u8(y[j] - (int)(0.34414f * cb) - (int)(0.71414f * cr)); + *dst++ = clamp_u8(y[j] + (int)(1.772f * cb)); + } + } +} + +/* + * IIDC YUV 4:4:4 → RGB24 + * Layout: U Y V (3 bytes/pixel) + */ +static void +yuv444_to_rgb(const uint8_t *src, uint8_t *dst, int w, int h) +{ + int i, n = w * h; + + for (i = 0; i < n; i++) { + int cb = src[0] - 128; + int y = src[1]; + int cr = src[2] - 128; + src += 3; + + *dst++ = clamp_u8(y + (int)(1.402f * cr)); + *dst++ = clamp_u8(y - (int)(0.34414f * cb) - (int)(0.71414f * cr)); + *dst++ = clamp_u8(y + (int)(1.772f * cb)); + } +} + +static int +write_ppm(const char *path, const uint8_t *rgb, int w, int h) +{ + FILE *f; + + if (strcmp(path, "-") == 0) { + f = stdout; + } else { + f = fopen(path, "wb"); + if (f == NULL) { + warn("fopen %s", path); + return (-1); + } + } + + fprintf(f, "P6\n%d %d\n255\n", w, h); + if (fwrite(rgb, 1, (size_t)(w * h * 3), f) != (size_t)(w * h * 3)) { + warn("fwrite"); + if (f != stdout) + fclose(f); + return (-1); + } + + if (f != stdout) + fclose(f); + return (0); +} + +static int +do_snap(int fd, const char *outpath, int skip) +{ + struct fwcam_info info; + uint8_t *frame_buf, *rgb_buf; + uint32_t rgb_size; + ssize_t n; + int w, h, i; + const char *pixfmt; + + if (ioctl(fd, FWCAM_GINFO, &info) < 0) + err(1, "FWCAM_GINFO"); + + if (info.frame_size == 0) + errx(1, "frame_size is 0 — camera not yet probed?"); + + if (info.cur_format != IIDC_FMT_VGA || + info.cur_mode >= nitems(fwcam_fmt0_modes)) + errx(1, "unsupported format %d mode %d for snap " + "(only Format_0 VGA supported)", + info.cur_format, info.cur_mode); + + w = fwcam_fmt0_modes[info.cur_mode].w; + h = fwcam_fmt0_modes[info.cur_mode].h; + pixfmt = fwcam_fmt0_modes[info.cur_mode].pixfmt; + + if (strcmp(pixfmt, "Mono16") == 0) + errx(1, "Mono16 not supported for PPM output"); + + rgb_size = (uint32_t)(w * h * 3); + frame_buf = malloc(info.frame_size); + rgb_buf = malloc(rgb_size); + if (frame_buf == NULL || rgb_buf == NULL) + err(1, "malloc"); + + /* Read frames; skip the first N to let AE/AWB settle */ + for (i = 0; i <= skip; i++) { + n = read(fd, frame_buf, info.frame_size); + if (n < 0) + err(1, "read"); + if ((uint32_t)n < info.frame_size) + errx(1, "short read: got %zd of %u bytes", + n, info.frame_size); + if (i < skip) + fprintf(stderr, "skipping frame %d/%d...\n", i + 1, skip); + } + + /* Convert to RGB24 */ + if (strcmp(pixfmt, "YUV422") == 0) { + yuv422_to_rgb(frame_buf, rgb_buf, w, h); + } else if (strcmp(pixfmt, "YUV411") == 0) { + yuv411_to_rgb(frame_buf, rgb_buf, w, h); + } else if (strcmp(pixfmt, "YUV444") == 0) { + yuv444_to_rgb(frame_buf, rgb_buf, w, h); + } else if (strcmp(pixfmt, "RGB8") == 0) { + memcpy(rgb_buf, frame_buf, rgb_size); + } else if (strcmp(pixfmt, "Mono8") == 0) { + for (i = 0; i < w * h; i++) { + rgb_buf[i * 3 + 0] = frame_buf[i]; + rgb_buf[i * 3 + 1] = frame_buf[i]; + rgb_buf[i * 3 + 2] = frame_buf[i]; + } + } else { + errx(1, "no converter for pixel format %s", pixfmt); + } + + if (write_ppm(outpath, rgb_buf, w, h) < 0) { + free(frame_buf); + free(rgb_buf); + return (1); + } + + fprintf(stderr, "saved %s (%dx%d %s, %u bytes raw)\n", + outpath, w, h, pixfmt, info.frame_size); + + free(frame_buf); + free(rgb_buf); + return (0); +} + +static void +do_info(int fd) +{ + struct fwcam_info info; + int i; + + if (ioctl(fd, FWCAM_GINFO, &info) < 0) + err(1, "FWCAM_GINFO"); + + printf("state: %s\n", + info.state < nitems(fwcam_state_names) ? + fwcam_state_names[info.state] : "unknown"); + printf("cur format: %d (%s)\n", info.cur_format, + info.cur_format < nitems(fwcam_fmt_names) ? + fwcam_fmt_names[info.cur_format] : "?"); + + if (info.cur_format == IIDC_FMT_VGA && + info.cur_mode < nitems(fwcam_fmt0_modes)) { + printf("cur mode: %d (%dx%d %s)\n", info.cur_mode, + fwcam_fmt0_modes[info.cur_mode].w, + fwcam_fmt0_modes[info.cur_mode].h, + fwcam_fmt0_modes[info.cur_mode].pixfmt); + } else { + printf("cur mode: %d\n", info.cur_mode); + } + + printf("cur rate: %d (%s)\n", info.cur_framerate, + info.cur_framerate < nitems(fwcam_rate_names) ? + fwcam_rate_names[info.cur_framerate] : "?"); + printf("iso_channel: %u\n", info.iso_channel); + printf("frame_size: %u bytes\n", info.frame_size); + printf("dropped: %u frames\n", info.frame_dropped); + + printf("formats: 0x%08x (", info.formats); + for (i = 0; i < 8; i++) { + if (info.formats & (1 << (31 - i))) + printf("F%d ", i); + } + printf(")\n"); + + printf("basic_func: 0x%08x", info.basic_func); + if (info.basic_func & IIDC_CAM_POWER_CTRL) printf(" [power_ctrl]"); + if (info.basic_func & IIDC_ONE_SHOT_INQ) printf(" [one_shot]"); + if (info.basic_func & IIDC_MULTI_SHOT_INQ) printf(" [multi_shot]"); + printf("\n"); + + printf("features: hi=0x%08x lo=0x%08x\n", + info.features_hi, info.features_lo); + if (info.features_hi) { + printf(" "); + if (info.features_hi & IIDC_HAS_BRIGHTNESS) printf("brightness "); + if (info.features_hi & IIDC_HAS_AUTO_EXPOSURE) printf("auto_exp "); + if (info.features_hi & IIDC_HAS_SHARPNESS) printf("sharpness "); + if (info.features_hi & IIDC_HAS_WHITE_BALANCE) printf("white_bal "); + if (info.features_hi & IIDC_HAS_HUE) printf("hue "); + if (info.features_hi & IIDC_HAS_SATURATION) printf("saturation "); + if (info.features_hi & IIDC_HAS_GAMMA) printf("gamma "); + if (info.features_hi & IIDC_HAS_SHUTTER) printf("shutter "); + if (info.features_hi & IIDC_HAS_GAIN) printf("gain "); + if (info.features_hi & IIDC_HAS_IRIS) printf("iris "); + if (info.features_hi & IIDC_HAS_FOCUS) printf("focus "); + if (info.features_hi & IIDC_HAS_TEMPERATURE) printf("temperature "); + if (info.features_hi & IIDC_HAS_TRIGGER) printf("trigger "); + printf("\n"); + } +} + +static void +do_mode(int fd, int argc, char **argv) +{ + struct fwcam_mode mode; + + if (argc < 3) + usage(); + + mode.format = (uint8_t)atoi(argv[0]); + mode.mode = (uint8_t)atoi(argv[1]); + mode.framerate = (uint8_t)atoi(argv[2]); + mode.frame_size = 0; + mode._pad = 0; + + if (ioctl(fd, FWCAM_SMODE, &mode) < 0) + err(1, "FWCAM_SMODE"); + + printf("mode set: format=%d mode=%d framerate=%d frame_size=%u\n", + mode.format, mode.mode, mode.framerate, mode.frame_size); +} + +static void +do_feat_get(int fd, int feat_id) +{ + struct fwcam_feature feat; + + if (feat_id < 0 || feat_id >= FWCAM_FEAT_MAX) + errx(1, "invalid feature id %d (0-%d)", feat_id, FWCAM_FEAT_MAX - 1); + + memset(&feat, 0, sizeof(feat)); + feat.id = (uint32_t)feat_id; + + if (ioctl(fd, FWCAM_GFEAT, &feat) < 0) + err(1, "FWCAM_GFEAT"); + + printf("feature %d (%s):\n", feat_id, + fwcam_feat_names[feat_id] != NULL ? fwcam_feat_names[feat_id] : "?"); + printf(" present: %s\n", + (feat.flags & FWCAM_FEATF_PRESENT) ? "yes" : "no"); + printf(" flags: 0x%x", feat.flags); + if (feat.flags & FWCAM_FEATF_ONOFF) printf(" [on/off]"); + if (feat.flags & FWCAM_FEATF_AUTO) printf(" [auto]"); + if (feat.flags & FWCAM_FEATF_MANUAL) printf(" [manual]"); + printf("\n"); + printf(" range: %u - %u\n", feat.min, feat.max); + + if (feat_id == FWCAM_FEAT_WHITE_BALANCE) + printf(" value: U/B=%u V/R=%u\n", feat.value, feat.value2); + else + printf(" value: %u\n", feat.value); +} + +static void +do_feat_set(int fd, int feat_id, uint32_t value, uint32_t value2) +{ + struct fwcam_feature feat; + + if (feat_id < 0 || feat_id >= FWCAM_FEAT_MAX) + errx(1, "invalid feature id %d (0-%d)", feat_id, FWCAM_FEAT_MAX - 1); + + memset(&feat, 0, sizeof(feat)); + feat.id = (uint32_t)feat_id; + feat.value = value; + feat.value2 = value2; + + if (ioctl(fd, FWCAM_SFEAT, &feat) < 0) + err(1, "FWCAM_SFEAT"); + + printf("feature %d (%s) set to %u\n", feat_id, + fwcam_feat_names[feat_id] != NULL ? fwcam_feat_names[feat_id] : "?", value); +} + +int +main(int argc, char *argv[]) +{ + const char *devpath = default_dev; + int fd, ch; + + while ((ch = getopt(argc, argv, "f:")) != -1) { + switch (ch) { + case 'f': + devpath = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + fd = open(devpath, O_RDWR); + if (fd < 0) + err(1, "open %s", devpath); + + if (strcmp(argv[0], "info") == 0) { + do_info(fd); + + } else if (strcmp(argv[0], "snap") == 0) { + const char *out = "snap.ppm"; + int skip = 5; + + /* re-parse the snap subcommand's own flags */ + optreset = 1; + optind = 1; + while ((ch = getopt(argc, argv, "o:s:")) != -1) { + switch (ch) { + case 'o': + out = optarg; + break; + case 's': + skip = atoi(optarg); + break; + default: + usage(); + } + } + do_snap(fd, out, skip); + + } else if (strcmp(argv[0], "mode") == 0) { + do_mode(fd, argc - 1, argv + 1); + + } else if (strcmp(argv[0], "feat") == 0) { + if (argc < 3) + usage(); + + int feat_id = atoi(argv[2]); + + if (strcmp(argv[1], "get") == 0) { + do_feat_get(fd, feat_id); + } else if (strcmp(argv[1], "set") == 0) { + if (argc < 4) + usage(); + uint32_t val = (uint32_t)atoi(argv[3]); + uint32_t val2 = (argc > 4) ? (uint32_t)atoi(argv[4]) : 0; + do_feat_set(fd, feat_id, val, val2); + } else { + usage(); + } + + } else { + warnx("unknown command: %s", argv[0]); + usage(); + } + + close(fd); + return (0); +}
