From 2ee56f535d231e3eaa2108a39ed0e9995490ace8 Mon Sep 17 00:00:00 2001 From: mglocker Date: Sun, 24 Aug 2008 11:05:02 +0000 Subject: [PATCH] Add support for processing unit (e.g. brightness) controls. New V4L2 ioctls therefore are VIDIOC_QUERYCTRL, VIDIOC_G_CTRL, and VIDIOC_S_CTRL. --- sys/dev/usb/uvideo.c | 263 ++++++++++++++++++++++++++++++++++++++++++- sys/dev/usb/uvideo.h | 81 ++++++++++++- sys/dev/video.c | 12 +- sys/dev/video_if.h | 4 +- 4 files changed, 354 insertions(+), 6 deletions(-) diff --git a/sys/dev/usb/uvideo.c b/sys/dev/usb/uvideo.c index 9766bbce15f..ed252c54c21 100644 --- a/sys/dev/usb/uvideo.c +++ b/sys/dev/usb/uvideo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.c,v 1.82 2008/08/16 18:56:07 mglocker Exp $ */ +/* $OpenBSD: uvideo.c,v 1.83 2008/08/24 11:05:02 mglocker Exp $ */ /* * Copyright (c) 2008 Robert Nagy @@ -68,7 +68,15 @@ int uvideo_activate(struct device *, enum devact); usbd_status uvideo_vc_parse_desc(struct uvideo_softc *); usbd_status uvideo_vc_parse_desc_header(struct uvideo_softc *, + const usb_descriptor_t *); +usbd_status uvideo_vc_parse_desc_pu(struct uvideo_softc *, const usb_descriptor_t *); +usbd_status uvideo_vc_get_ctrl(struct uvideo_softc *, uint8_t *, uint8_t, + uint8_t, uint16_t, uint16_t); +usbd_status uvideo_vc_set_ctrl(struct uvideo_softc *, uint8_t *, uint8_t, + uint8_t, uint16_t, uint16_t); +int uvideo_find_ctrl(struct uvideo_softc *, int); + usbd_status uvideo_vs_parse_desc(struct uvideo_softc *, struct usb_attach_arg *, usb_config_descriptor_t *); usbd_status uvideo_vs_parse_desc_input_header(struct uvideo_softc *, @@ -137,6 +145,8 @@ void uvideo_dump_desc_format_uncompressed(struct uvideo_softc *, const usb_descriptor_t *); void uvideo_dump_desc_frame_uncompressed(struct uvideo_softc *, const usb_descriptor_t *); +void uvideo_dump_desc_processing(struct uvideo_softc *, + const usb_descriptor_t *); void uvideo_dump_desc_extension(struct uvideo_softc *, const usb_descriptor_t *); void uvideo_hexdump(void *, int, int); @@ -162,6 +172,9 @@ int uvideo_dqbuf(void *, struct v4l2_buffer *); int uvideo_streamon(void *, int); int uvideo_streamoff(void *, int); int uvideo_try_fmt(void *, struct v4l2_format *); +int uvideo_queryctrl(void *, struct v4l2_queryctrl *); +int uvideo_g_ctrl(void *, struct v4l2_control *); +int uvideo_s_ctrl(void *, struct v4l2_control *); /* * Other hardware interface related functions @@ -200,7 +213,9 @@ struct video_hw_if uvideo_hw_if = { uvideo_streamon, /* VIDIOC_STREAMON */ uvideo_streamoff, /* VIDIOC_STREAMOFF */ uvideo_try_fmt, /* VIDIOC_TRY_FMT */ - NULL, /* VIDIOC_QUERYCTRL */ + uvideo_queryctrl, /* VIDIOC_QUERYCTRL */ + uvideo_g_ctrl, /* VIDIOC_G_CTRL */ + uvideo_s_ctrl, /* VIDIOC_S_CTRL */ uvideo_mappage, /* mmap */ uvideo_get_bufsize, /* read */ uvideo_start_read /* start stream for read */ @@ -443,6 +458,12 @@ uvideo_vc_parse_desc(struct uvideo_softc *sc) return (error); vc_header_found = 1; break; + case UDESCSUB_VC_PROCESSING_UNIT: + /* XXX do correct length calculation */ + if (desc->bLength < 25) { + (void)uvideo_vc_parse_desc_pu(sc, desc); + } + break; /* TODO: which VC descriptors do we need else? */ } @@ -478,6 +499,121 @@ uvideo_vc_parse_desc_header(struct uvideo_softc *sc, return (USBD_NORMAL_COMPLETION); } +usbd_status +uvideo_vc_parse_desc_pu(struct uvideo_softc *sc, + const usb_descriptor_t *desc) +{ + struct usb_video_vc_processing_desc *d; + + d = (struct usb_video_vc_processing_desc *)(uint8_t *)desc; + + if (sc->sc_desc_vc_pu_num == UVIDEO_MAX_PU) { + printf("%s: too many PU descriptors found!\n", DEVNAME(sc)); + return (USBD_INVAL); + } + + /* XXX support variable bmControls fields */ + if (d->bControlSize != 2) { + printf("%s: just 2 bytes bmControls supported yet!\n", + DEVNAME(sc)); + return (USBD_INVAL); + } + + sc->sc_desc_vc_pu[sc->sc_desc_vc_pu_num] = d; + sc->sc_desc_vc_pu_num++; + + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uvideo_vc_get_ctrl(struct uvideo_softc *sc, uint8_t *ctrl_data, + uint8_t request, uint8_t unitid, uint16_t ctrl_selector, uint16_t ctrl_len) +{ + usb_device_request_t req; + usbd_status error; + + req.bmRequestType = UVIDEO_GET_IF; + req.bRequest = request; + USETW(req.wValue, (ctrl_selector << 8)); + USETW(req.wIndex, (unitid << 8)); + USETW(req.wLength, ctrl_len); + + error = usbd_do_request(sc->sc_udev, &req, ctrl_data); + if (error) { + DPRINTF(1, "%s: %s: could not GET ctrl request: %s\n", + DEVNAME(sc), __func__, usbd_errstr(error)); + return (USBD_INVAL); + } + + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uvideo_vc_set_ctrl(struct uvideo_softc *sc, uint8_t *ctrl_data, + uint8_t request, uint8_t unitid, uint16_t ctrl_selector, uint16_t ctrl_len) +{ + usb_device_request_t req; + usbd_status error; + + req.bmRequestType = UVIDEO_SET_IF; + req.bRequest = request; + USETW(req.wValue, (ctrl_selector << 8)); + USETW(req.wIndex, (unitid << 8)); + USETW(req.wLength, ctrl_len); + + error = usbd_do_request(sc->sc_udev, &req, ctrl_data); + if (error) { + DPRINTF(1, "%s: %s: could not SET ctrl request: %s\n", + DEVNAME(sc), __func__, usbd_errstr(error)); + return (USBD_INVAL); + } + + return (USBD_NORMAL_COMPLETION); +} + +int +uvideo_find_ctrl(struct uvideo_softc *sc, int id) +{ + int i, j, found; + + if (sc->sc_desc_vc_pu_num == 0) { + /* no processing unit descriptors found */ + DPRINTF(1, "%s: %s: no processing unit descriptors found!\n", + DEVNAME(sc), __func__); + return (EINVAL); + } + + /* do we support this control? */ + for (found = 0, i = 0; uvideo_ctrls[i].cid != 0; i++) { + if (id == uvideo_ctrls[i].cid) { + found = 1; + break; + } + } + if (found == 0) { + DPRINTF(1, "%s: %s: control not supported by driver!\n", + DEVNAME(sc), __func__); + return (EINVAL); + } + + /* does the device support this control? */ + for (found = 0, j = 0; i < sc->sc_desc_vc_pu_num; j++) { + if (UGETW(sc->sc_desc_vc_pu[j]->bmControls) & + uvideo_ctrls[i].ctrl_bitmap) { + found = 1; + break; + } + } + if (found == 0) { + DPRINTF(1, "%s: %s: control not supported by device!\n", + DEVNAME(sc), __func__); + return (EINVAL); + } + sc->sc_desc_vc_pu_cur = sc->sc_desc_vc_pu[j]; + + return (i); +} + usbd_status uvideo_vs_parse_desc(struct uvideo_softc *sc, struct usb_attach_arg *uaa, usb_config_descriptor_t *cdesc) @@ -1647,7 +1783,8 @@ uvideo_dump_desc_all(struct uvideo_softc *sc) } else { printf(" (UDESCSUB_VC_PROCESSING_" "UNIT)\n"); - /* TODO */ + printf("|\n"); + uvideo_dump_desc_processing(sc, desc); } break; case UDESCSUB_VC_EXTENSION_UNIT: @@ -1999,6 +2136,26 @@ uvideo_dump_desc_format_uncompressed(struct uvideo_softc *sc, printf("bCopyProtect=0x%02x\n", d->bCopyProtect); } +void +uvideo_dump_desc_processing(struct uvideo_softc *sc, + const usb_descriptor_t *desc) +{ + struct usb_video_vc_processing_desc *d; + + d = (struct usb_video_vc_processing_desc *)(uint8_t *)desc; + + printf("bLength=%d\n", d->bLength); + printf("bDescriptorType=0x%02x\n", d->bDescriptorType); + printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype); + printf("bUnitID=0x%02x\n", d->bUnitID); + printf("bSourceID=0x%02x\n", d->bSourceID); + printf("wMaxMultiplier=%d\n", UGETW(d->wMaxMultiplier)); + printf("bControlSize=%d\n", d->bControlSize); + printf("bmControls=0x%02x\n", UGETW(d->bmControls)); + printf("iProcessing=0x%02x\n", d->iProcessing); + printf("bmVideoStandards=0x%02x\n", d->bmVideoStandards); +} + void uvideo_dump_desc_extension(struct uvideo_softc *sc, const usb_descriptor_t *desc) @@ -2460,6 +2617,106 @@ uvideo_streamoff(void *v, int type) return (0); } +int +uvideo_queryctrl(void *v, struct v4l2_queryctrl *qctrl) +{ + struct uvideo_softc *sc = v; + int i; + usbd_status error; + uint8_t ctrl_data[2]; + + i = uvideo_find_ctrl(sc, qctrl->id); + if (i == EINVAL) + return (i); + + /* set type */ + qctrl->type = uvideo_ctrls[i].type; + + /* set description name */ + strlcpy(qctrl->name, uvideo_ctrls[i].name, sizeof(qctrl->name)); + + /* set minimum */ + error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_MIN, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + qctrl->minimum = letoh16(*(uint16_t *)ctrl_data); + + /* set maximum */ + error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_MAX, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + qctrl->maximum = letoh16(*(uint16_t *)ctrl_data); + + /* set resolution */ + error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_RES, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + qctrl->step = letoh16(*(uint16_t *)ctrl_data); + + /* set default */ + error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_DEF, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + qctrl->default_value = letoh16(*(uint16_t *)ctrl_data); + + /* set flags */ + qctrl->flags = 0; + + return (0); +} + +int +uvideo_g_ctrl(void *v, struct v4l2_control *gctrl) +{ + struct uvideo_softc *sc = v; + int i; + usbd_status error; + uint8_t ctrl_data[2]; + + i = uvideo_find_ctrl(sc, gctrl->id); + if (i == EINVAL) + return (i); + + error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_CUR, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + gctrl->value = letoh16(*(uint16_t *)ctrl_data); + + return (0); +} + +int +uvideo_s_ctrl(void *v, struct v4l2_control *sctrl) +{ + struct uvideo_softc *sc = v; + int i; + usbd_status error; + uint8_t ctrl_data[2]; + + i = uvideo_find_ctrl(sc, sctrl->id); + if (i == EINVAL) + return (i); + + *(uint16_t *)ctrl_data = htole16(sctrl->value); + error = uvideo_vc_set_ctrl(sc, ctrl_data, SET_CUR, + sc->sc_desc_vc_pu_cur->bUnitID, + uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len); + if (error != USBD_NORMAL_COMPLETION) + return (EINVAL); + + return (0); +} + int uvideo_try_fmt(void *v, struct v4l2_format *fmt) { diff --git a/sys/dev/usb/uvideo.h b/sys/dev/usb/uvideo.h index 0132e82150e..0fead8a5781 100644 --- a/sys/dev/usb/uvideo.h +++ b/sys/dev/usb/uvideo.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.h,v 1.27 2008/08/02 21:52:37 mglocker Exp $ */ +/* $OpenBSD: uvideo.h,v 1.28 2008/08/24 11:05:03 mglocker Exp $ */ /* * Copyright (c) 2007 Robert Nagy @@ -208,6 +208,20 @@ struct usb_video_camera_terminal_desc { uByte *bmControls; }; +/* Table 3-8: VC Processing Unit Descriptor */ +struct usb_video_vc_processing_desc { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitID; + uByte bSourceID; + uWord wMaxMultiplier; + uByte bControlSize; + uWord bmControls; /* XXX must be variable size of bControlSize */ + uByte iProcessing; + uByte bmVideoStandards; +} __packed; + /* Table 3-9: VC Extension Unit Descriptor */ struct usb_video_vc_extension_desc { uByte bLength; @@ -453,6 +467,66 @@ struct uvideo_res { int fidx; } __packed; +struct uvideo_controls { + int cid; + int type; + char name[32]; + uint16_t ctrl_bitmap; + uint16_t ctrl_selector; + uint16_t ctrl_len; +} uvideo_ctrls[] = { + /* TODO complete control list */ + { + V4L2_CID_BRIGHTNESS, + V4L2_CTRL_TYPE_INTEGER, + "Brightness", + (1 << 0), + PU_BRIGHTNESS_CONTROL, + 2 + }, + { + V4L2_CID_CONTRAST, + V4L2_CTRL_TYPE_INTEGER, + "Contrast", + (1 << 1), + PU_CONTRAST_CONTROL, + 2 + }, + { + V4L2_CID_HUE, + V4L2_CTRL_TYPE_INTEGER, + "Hue", + (1 << 2), + PU_HUE_CONTROL, + 2 + }, + { + V4L2_CID_SATURATION, + V4L2_CTRL_TYPE_INTEGER, + "Saturation", + (1 << 3), + PU_SATURATION_CONTROL, + 2 + }, + { + V4L2_CID_GAMMA, + V4L2_CTRL_TYPE_INTEGER, + "Gamma", + (1 << 5), + PU_GAMMA_CONTROL, + 2 + }, + { + V4L2_CID_GAIN, + V4L2_CTRL_TYPE_INTEGER, + "Gain", + (1 << 9), + PU_GAIN_CONTROL, + 2, + }, + { 0, 0, "", 0, 0, 0 } +}; + struct uvideo_softc { struct device sc_dev; usbd_device_handle sc_udev; @@ -500,6 +574,11 @@ struct uvideo_softc { struct usb_video_header_desc_all sc_desc_vc_header; struct usb_video_input_header_desc_all sc_desc_vs_input_header; +#define UVIDEO_MAX_PU 8 + int sc_desc_vc_pu_num; + struct usb_video_vc_processing_desc *sc_desc_vc_pu_cur; + struct usb_video_vc_processing_desc *sc_desc_vc_pu[UVIDEO_MAX_PU]; + #define UVIDEO_MAX_FORMAT 8 int sc_fmtgrp_idx; int sc_fmtgrp_num; diff --git a/sys/dev/video.c b/sys/dev/video.c index 0f8e3cc2414..61067d2df06 100644 --- a/sys/dev/video.c +++ b/sys/dev/video.c @@ -1,4 +1,4 @@ -/* $OpenBSD: video.c,v 1.21 2008/08/13 20:29:34 mglocker Exp $ */ +/* $OpenBSD: video.c,v 1.22 2008/08/24 11:05:02 mglocker Exp $ */ /* * Copyright (c) 2008 Robert Nagy * Copyright (c) 2008 Marcus Glocker @@ -271,6 +271,16 @@ videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) error = (sc->hw_if->queryctrl)(sc->hw_hdl, (struct v4l2_queryctrl *)data); break; + case VIDIOC_G_CTRL: + if (sc->hw_if->g_ctrl) + error = (sc->hw_if->g_ctrl)(sc->hw_hdl, + (struct v4l2_control *)data); + break; + case VIDIOC_S_CTRL: + if (sc->hw_if->s_ctrl) + error = (sc->hw_if->s_ctrl)(sc->hw_hdl, + (struct v4l2_control *)data); + break; default: error = (ENOTTY); } diff --git a/sys/dev/video_if.h b/sys/dev/video_if.h index ea8836aa61a..bef02d9e1ae 100644 --- a/sys/dev/video_if.h +++ b/sys/dev/video_if.h @@ -1,4 +1,4 @@ -/* $OpenBSD: video_if.h,v 1.15 2008/08/13 20:29:34 mglocker Exp $ */ +/* $OpenBSD: video_if.h,v 1.16 2008/08/24 11:05:02 mglocker Exp $ */ /* * Copyright (c) 2008 Robert Nagy * Copyright (c) 2008 Marcus Glocker @@ -50,6 +50,8 @@ struct video_hw_if { int (*streamoff)(void *, int); int (*try_fmt)(void *, struct v4l2_format *); int (*queryctrl)(void *, struct v4l2_queryctrl *); + int (*g_ctrl)(void *, struct v4l2_control *); + int (*s_ctrl)(void *, struct v4l2_control *); caddr_t (*mappage)(void *, off_t, int); /* other functions */ -- 2.20.1