Add support for processing unit (e.g. brightness) controls. New V4L2
authormglocker <mglocker@openbsd.org>
Sun, 24 Aug 2008 11:05:02 +0000 (11:05 +0000)
committermglocker <mglocker@openbsd.org>
Sun, 24 Aug 2008 11:05:02 +0000 (11:05 +0000)
ioctls therefore are VIDIOC_QUERYCTRL, VIDIOC_G_CTRL, and VIDIOC_S_CTRL.

sys/dev/usb/uvideo.c
sys/dev/usb/uvideo.h
sys/dev/video.c
sys/dev/video_if.h

index 9766bbc..ed252c5 100644 (file)
@@ -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 <robert@openbsd.org>
@@ -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)
 {
index 0132e82..0fead8a 100644 (file)
@@ -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 <robert@openbsd.org>
@@ -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;
index 0f8e3cc..61067d2 100644 (file)
@@ -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 <robert@openbsd.org>
  * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
@@ -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);
        }
index ea8836a..bef02d9 100644 (file)
@@ -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 <robert@openbsd.org>
  * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
@@ -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 */