[media] v4l2-ctrls: add new RDS TX controls
[deliverable/linux.git] / drivers / media / v4l2-core / v4l2-ctrls.c
index 6ed2d5608611c9a8dda2e440de866672df509b38..4863cf05175dc4627d092c242557e11ea53f7a22 100644 (file)
@@ -462,6 +462,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
                "RGB full range (0-255)",
                NULL,
        };
+       static const char * const detect_md_mode[] = {
+               "Disabled",
+               "Global",
+               "Threshold Grid",
+               "Region Grid",
+               NULL,
+       };
 
 
        switch (id) {
@@ -553,6 +560,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
        case V4L2_CID_DV_TX_RGB_RANGE:
        case V4L2_CID_DV_RX_RGB_RANGE:
                return dv_rgb_range;
+       case V4L2_CID_DETECT_MD_MODE:
+               return detect_md_mode;
 
        default:
                return NULL;
@@ -592,7 +601,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 {
        switch (id) {
        /* USER controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_USER_CLASS:               return "User Controls";
        case V4L2_CID_BRIGHTNESS:               return "Brightness";
        case V4L2_CID_CONTRAST:                 return "Contrast";
@@ -754,7 +763,7 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:                   return "VPX Profile";
 
        /* CAMERA controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_CAMERA_CLASS:             return "Camera Controls";
        case V4L2_CID_EXPOSURE_AUTO:            return "Auto Exposure";
        case V4L2_CID_EXPOSURE_ABSOLUTE:        return "Exposure Time, Absolute";
@@ -788,14 +797,23 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_AUTO_FOCUS_STATUS:        return "Auto Focus, Status";
        case V4L2_CID_AUTO_FOCUS_RANGE:         return "Auto Focus, Range";
 
-       /* FM Radio Modulator control */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
+       /* FM Radio Modulator controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_FM_TX_CLASS:              return "FM Radio Modulator Controls";
        case V4L2_CID_RDS_TX_DEVIATION:         return "RDS Signal Deviation";
        case V4L2_CID_RDS_TX_PI:                return "RDS Program ID";
        case V4L2_CID_RDS_TX_PTY:               return "RDS Program Type";
        case V4L2_CID_RDS_TX_PS_NAME:           return "RDS PS Name";
        case V4L2_CID_RDS_TX_RADIO_TEXT:        return "RDS Radio Text";
+       case V4L2_CID_RDS_TX_MONO_STEREO:       return "RDS Stereo";
+       case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD:   return "RDS Artificial Head";
+       case V4L2_CID_RDS_TX_COMPRESSED:        return "RDS Compressed";
+       case V4L2_CID_RDS_TX_DYNAMIC_PTY:       return "RDS Dynamic PTY";
+       case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement";
+       case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:   return "RDS Traffic Program";
+       case V4L2_CID_RDS_TX_MUSIC_SPEECH:      return "RDS Music";
+       case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:  return "RDS Enable Alt Frequencies";
+       case V4L2_CID_RDS_TX_ALT_FREQS:         return "RDS Alternate Frequencies";
        case V4L2_CID_AUDIO_LIMITER_ENABLED:    return "Audio Limiter Feature Enabled";
        case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
        case V4L2_CID_AUDIO_LIMITER_DEVIATION:  return "Audio Limiter Deviation";
@@ -812,6 +830,7 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_TUNE_ANTENNA_CAPACITOR:   return "Tune Antenna Capacitor";
 
        /* Flash controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_FLASH_CLASS:              return "Flash Controls";
        case V4L2_CID_FLASH_LED_MODE:           return "LED Mode";
        case V4L2_CID_FLASH_STROBE_SOURCE:      return "Strobe Source";
@@ -827,7 +846,7 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_FLASH_READY:              return "Ready to Strobe";
 
        /* JPEG encoder controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_JPEG_CLASS:               return "JPEG Compression Controls";
        case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:  return "Chroma Subsampling";
        case V4L2_CID_JPEG_RESTART_INTERVAL:    return "Restart Interval";
@@ -835,18 +854,21 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_JPEG_ACTIVE_MARKER:       return "Active Markers";
 
        /* Image source controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_IMAGE_SOURCE_CLASS:       return "Image Source Controls";
        case V4L2_CID_VBLANK:                   return "Vertical Blanking";
        case V4L2_CID_HBLANK:                   return "Horizontal Blanking";
        case V4L2_CID_ANALOGUE_GAIN:            return "Analogue Gain";
 
        /* Image processing controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_IMAGE_PROC_CLASS:         return "Image Processing Controls";
        case V4L2_CID_LINK_FREQ:                return "Link Frequency";
        case V4L2_CID_PIXEL_RATE:               return "Pixel Rate";
        case V4L2_CID_TEST_PATTERN:             return "Test Pattern";
 
        /* DV controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
        case V4L2_CID_DV_CLASS:                 return "Digital Video Controls";
        case V4L2_CID_DV_TX_HOTPLUG:            return "Hotplug Present";
        case V4L2_CID_DV_TX_RXSENSE:            return "RxSense Present";
@@ -870,6 +892,15 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:  return "Bandwidth, Auto";
        case V4L2_CID_RF_TUNER_BANDWIDTH:       return "Bandwidth";
        case V4L2_CID_RF_TUNER_PLL_LOCK:        return "PLL Lock";
+
+       /* Detection controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
+       case V4L2_CID_DETECT_CLASS:             return "Detection Controls";
+       case V4L2_CID_DETECT_MD_MODE:           return "Motion Detection Mode";
+       case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold";
+       case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid";
+       case V4L2_CID_DETECT_MD_REGION_GRID:    return "MD Region Grid";
+
        default:
                return NULL;
        }
@@ -924,6 +955,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
        case V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
        case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
        case V4L2_CID_RF_TUNER_PLL_LOCK:
+       case V4L2_CID_RDS_TX_MONO_STEREO:
+       case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD:
+       case V4L2_CID_RDS_TX_COMPRESSED:
+       case V4L2_CID_RDS_TX_DYNAMIC_PTY:
+       case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT:
+       case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
+       case V4L2_CID_RDS_TX_MUSIC_SPEECH:
+       case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:
                *type = V4L2_CTRL_TYPE_BOOLEAN;
                *min = 0;
                *max = *step = 1;
@@ -988,6 +1027,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
        case V4L2_CID_TEST_PATTERN:
        case V4L2_CID_TUNE_DEEMPHASIS:
        case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
+       case V4L2_CID_DETECT_MD_MODE:
                *type = V4L2_CTRL_TYPE_MENU;
                break;
        case V4L2_CID_LINK_FREQ:
@@ -1014,6 +1054,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
        case V4L2_CID_DV_CLASS:
        case V4L2_CID_FM_RX_CLASS:
        case V4L2_CID_RF_TUNER_CLASS:
+       case V4L2_CID_DETECT_CLASS:
                *type = V4L2_CTRL_TYPE_CTRL_CLASS;
                /* You can neither read not write these */
                *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -1059,6 +1100,15 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
                *type = V4L2_CTRL_TYPE_INTEGER64;
                *flags |= V4L2_CTRL_FLAG_READ_ONLY;
                break;
+       case V4L2_CID_DETECT_MD_REGION_GRID:
+               *type = V4L2_CTRL_TYPE_U8;
+               break;
+       case V4L2_CID_DETECT_MD_THRESHOLD_GRID:
+               *type = V4L2_CTRL_TYPE_U16;
+               break;
+       case V4L2_CID_RDS_TX_ALT_FREQS:
+               *type = V4L2_CTRL_TYPE_U32;
+               break;
        default:
                *type = V4L2_CTRL_TYPE_INTEGER;
                break;
@@ -1099,6 +1149,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
        case V4L2_CID_RF_TUNER_MIXER_GAIN:
        case V4L2_CID_RF_TUNER_IF_GAIN:
        case V4L2_CID_RF_TUNER_BANDWIDTH:
+       case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD:
                *flags |= V4L2_CTRL_FLAG_SLIDER;
                break;
        case V4L2_CID_PAN_RELATIVE:
@@ -1169,14 +1220,22 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
        case V4L2_CTRL_TYPE_BUTTON:
                return false;
        case V4L2_CTRL_TYPE_STRING:
+               idx *= ctrl->elem_size;
                /* strings are always 0-terminated */
-               return !strcmp(ptr1.p_char, ptr2.p_char);
+               return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx);
        case V4L2_CTRL_TYPE_INTEGER64:
-               return *ptr1.p_s64 == *ptr2.p_s64;
+               return ptr1.p_s64[idx] == ptr2.p_s64[idx];
+       case V4L2_CTRL_TYPE_U8:
+               return ptr1.p_u8[idx] == ptr2.p_u8[idx];
+       case V4L2_CTRL_TYPE_U16:
+               return ptr1.p_u16[idx] == ptr2.p_u16[idx];
+       case V4L2_CTRL_TYPE_U32:
+               return ptr1.p_u32[idx] == ptr2.p_u32[idx];
        default:
-               if (ctrl->is_ptr)
-                       return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size);
-               return *ptr1.p_s32 == *ptr2.p_s32;
+               if (ctrl->is_int)
+                       return ptr1.p_s32[idx] == ptr2.p_s32[idx];
+               idx *= ctrl->elem_size;
+               return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size);
        }
 }
 
@@ -1185,20 +1244,32 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
 {
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_STRING:
-               memset(ptr.p_char, ' ', ctrl->minimum);
-               ptr.p_char[ctrl->minimum] = '\0';
+               idx *= ctrl->elem_size;
+               memset(ptr.p_char + idx, ' ', ctrl->minimum);
+               ptr.p_char[idx + ctrl->minimum] = '\0';
                break;
        case V4L2_CTRL_TYPE_INTEGER64:
-               *ptr.p_s64 = ctrl->default_value;
+               ptr.p_s64[idx] = ctrl->default_value;
                break;
        case V4L2_CTRL_TYPE_INTEGER:
        case V4L2_CTRL_TYPE_INTEGER_MENU:
        case V4L2_CTRL_TYPE_MENU:
        case V4L2_CTRL_TYPE_BITMASK:
        case V4L2_CTRL_TYPE_BOOLEAN:
-               *ptr.p_s32 = ctrl->default_value;
+               ptr.p_s32[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               ptr.p_u8[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               ptr.p_u16[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_U32:
+               ptr.p_u32[idx] = ctrl->default_value;
                break;
        default:
+               idx *= ctrl->elem_size;
+               memset(ptr.p + idx, 0, ctrl->elem_size);
                break;
        }
 }
@@ -1237,21 +1308,38 @@ static void std_log(const struct v4l2_ctrl *ctrl)
        case V4L2_CTRL_TYPE_STRING:
                pr_cont("%s", ptr.p_char);
                break;
+       case V4L2_CTRL_TYPE_U8:
+               pr_cont("%u", (unsigned)*ptr.p_u8);
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               pr_cont("%u", (unsigned)*ptr.p_u16);
+               break;
+       case V4L2_CTRL_TYPE_U32:
+               pr_cont("%u", (unsigned)*ptr.p_u32);
+               break;
        default:
                pr_cont("unknown type %d", ctrl->type);
                break;
        }
 }
 
-/* Round towards the closest legal value */
+/*
+ * Round towards the closest legal value. Be careful when we are
+ * close to the maximum range of the control type to prevent
+ * wrap-arounds.
+ */
 #define ROUND_TO_RANGE(val, offset_type, ctrl)                 \
 ({                                                             \
        offset_type offset;                                     \
-       val += (ctrl)->step / 2;                                \
+       if ((ctrl)->maximum >= 0 &&                             \
+           val >= (ctrl)->maximum - ((ctrl)->step / 2))        \
+               val = (ctrl)->maximum;                          \
+       else                                                    \
+               val += (ctrl)->step / 2;                        \
        val = clamp_t(typeof(val), val,                         \
                      (ctrl)->minimum, (ctrl)->maximum);        \
        offset = (val) - (ctrl)->minimum;                       \
-       offset = (ctrl)->step * (offset / (ctrl)->step);        \
+       offset = (ctrl)->step * (offset / (s32)(ctrl)->step);   \
        val = (ctrl)->minimum + offset;                         \
        0;                                                      \
 })
@@ -1261,42 +1349,64 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
                        union v4l2_ctrl_ptr ptr)
 {
        size_t len;
+       u64 offset;
+       s64 val;
 
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER:
-               return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl);
+               return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl);
        case V4L2_CTRL_TYPE_INTEGER64:
-               return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl);
+               /*
+                * We can't use the ROUND_TO_RANGE define here due to
+                * the u64 divide that needs special care.
+                */
+               val = ptr.p_s64[idx];
+               if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2)
+                       val = ctrl->maximum;
+               else
+                       val += ctrl->step / 2;
+               val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum);
+               offset = val - ctrl->minimum;
+               do_div(offset, ctrl->step);
+               ptr.p_s64[idx] = ctrl->minimum + offset * ctrl->step;
+               return 0;
+       case V4L2_CTRL_TYPE_U8:
+               return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl);
+       case V4L2_CTRL_TYPE_U16:
+               return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl);
+       case V4L2_CTRL_TYPE_U32:
+               return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl);
 
        case V4L2_CTRL_TYPE_BOOLEAN:
-               *ptr.p_s32 = !!*ptr.p_s32;
+               ptr.p_s32[idx] = !!ptr.p_s32[idx];
                return 0;
 
        case V4L2_CTRL_TYPE_MENU:
        case V4L2_CTRL_TYPE_INTEGER_MENU:
-               if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum)
+               if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum)
                        return -ERANGE;
-               if (ctrl->menu_skip_mask & (1 << *ptr.p_s32))
+               if (ctrl->menu_skip_mask & (1 << ptr.p_s32[idx]))
                        return -EINVAL;
                if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
-                   ctrl->qmenu[*ptr.p_s32][0] == '\0')
+                   ctrl->qmenu[ptr.p_s32[idx]][0] == '\0')
                        return -EINVAL;
                return 0;
 
        case V4L2_CTRL_TYPE_BITMASK:
-               *ptr.p_s32 &= ctrl->maximum;
+               ptr.p_s32[idx] &= ctrl->maximum;
                return 0;
 
        case V4L2_CTRL_TYPE_BUTTON:
        case V4L2_CTRL_TYPE_CTRL_CLASS:
-               *ptr.p_s32 = 0;
+               ptr.p_s32[idx] = 0;
                return 0;
 
        case V4L2_CTRL_TYPE_STRING:
-               len = strlen(ptr.p_char);
+               idx *= ctrl->elem_size;
+               len = strlen(ptr.p_char + idx);
                if (len < ctrl->minimum)
                        return -ERANGE;
-               if ((len - ctrl->minimum) % ctrl->step)
+               if ((len - (u32)ctrl->minimum) % (u32)ctrl->step)
                        return -ERANGE;
                return 0;
 
@@ -1320,17 +1430,18 @@ static int ptr_to_user(struct v4l2_ext_control *c,
        u32 len;
 
        if (ctrl->is_ptr && !ctrl->is_string)
-               return copy_to_user(c->ptr, ptr.p, ctrl->elem_size);
+               return copy_to_user(c->ptr, ptr.p, c->size) ?
+                      -EFAULT : 0;
 
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_STRING:
                len = strlen(ptr.p_char);
                if (c->size < len + 1) {
-                       c->size = len + 1;
+                       c->size = ctrl->elem_size;
                        return -ENOSPC;
                }
                return copy_to_user(c->string, ptr.p_char, len + 1) ?
-                                                               -EFAULT : 0;
+                      -EFAULT : 0;
        case V4L2_CTRL_TYPE_INTEGER64:
                c->value64 = *ptr.p_s64;
                break;
@@ -1364,8 +1475,16 @@ static int user_to_ptr(struct v4l2_ext_control *c,
        u32 size;
 
        ctrl->is_new = 1;
-       if (ctrl->is_ptr && !ctrl->is_string)
-               return copy_from_user(ptr.p, c->ptr, ctrl->elem_size);
+       if (ctrl->is_ptr && !ctrl->is_string) {
+               unsigned idx;
+
+               ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0;
+               if (ret || !ctrl->is_array)
+                       return ret;
+               for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++)
+                       ctrl->type_ops->init(ctrl, idx, ptr);
+               return 0;
+       }
 
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER64:
@@ -1377,7 +1496,7 @@ static int user_to_ptr(struct v4l2_ext_control *c,
                        return -ERANGE;
                if (size > ctrl->maximum + 1)
                        size = ctrl->maximum + 1;
-               ret = copy_from_user(ptr.p_char, c->string, size);
+               ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0;
                if (!ret) {
                        char last = ptr.p_char[size - 1];
 
@@ -1387,7 +1506,7 @@ static int user_to_ptr(struct v4l2_ext_control *c,
                        if (strlen(ptr.p_char) == ctrl->maximum && last)
                                return -ERANGE;
                }
-               return ret ? -EFAULT : 0;
+               return ret;
        default:
                *ptr.p_s32 = c->value;
                break;
@@ -1408,21 +1527,7 @@ static void ptr_to_ptr(struct v4l2_ctrl *ctrl,
 {
        if (ctrl == NULL)
                return;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               /* strings are always 0-terminated */
-               strcpy(to.p_char, from.p_char);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               *to.p_s64 = *from.p_s64;
-               break;
-       default:
-               if (ctrl->is_ptr)
-                       memcpy(to.p, from.p, ctrl->elem_size);
-               else
-                       *to.p_s32 = *from.p_s32;
-               break;
-       }
+       memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size);
 }
 
 /* Copy the new value to the current value. */
@@ -1474,15 +1579,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
 static int cluster_changed(struct v4l2_ctrl *master)
 {
        bool changed = false;
+       unsigned idx;
        int i;
 
        for (i = 0; i < master->ncontrols; i++) {
                struct v4l2_ctrl *ctrl = master->cluster[i];
+               bool ctrl_changed = false;
 
                if (ctrl == NULL)
                        continue;
-               ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0,
+               for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
+                       ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
                                ctrl->p_cur, ctrl->p_new);
+               ctrl->has_changed = ctrl_changed;
                changed |= ctrl->has_changed;
        }
        return changed;
@@ -1497,6 +1606,9 @@ static int check_range(enum v4l2_ctrl_type type,
                if (step != 1 || max > 1 || min < 0)
                        return -ERANGE;
                /* fall through */
+       case V4L2_CTRL_TYPE_U8:
+       case V4L2_CTRL_TYPE_U16:
+       case V4L2_CTRL_TYPE_U32:
        case V4L2_CTRL_TYPE_INTEGER:
        case V4L2_CTRL_TYPE_INTEGER64:
                if (step == 0 || min > max || def < min || def > max)
@@ -1529,26 +1641,32 @@ static int validate_new(const struct v4l2_ctrl *ctrl,
                        struct v4l2_ext_control *c)
 {
        union v4l2_ctrl_ptr ptr;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-       case V4L2_CTRL_TYPE_MENU:
-       case V4L2_CTRL_TYPE_BITMASK:
-       case V4L2_CTRL_TYPE_BOOLEAN:
-       case V4L2_CTRL_TYPE_BUTTON:
-       case V4L2_CTRL_TYPE_CTRL_CLASS:
-               ptr.p_s32 = &c->value;
-               return ctrl->type_ops->validate(ctrl, 0, ptr);
-
-       case V4L2_CTRL_TYPE_INTEGER64:
-               ptr.p_s64 = &c->value64;
-               return ctrl->type_ops->validate(ctrl, 0, ptr);
-
-       default:
-               ptr.p = c->ptr;
-               return ctrl->type_ops->validate(ctrl, 0, ptr);
+       unsigned idx;
+       int err = 0;
+
+       if (!ctrl->is_ptr) {
+               switch (ctrl->type) {
+               case V4L2_CTRL_TYPE_INTEGER:
+               case V4L2_CTRL_TYPE_INTEGER_MENU:
+               case V4L2_CTRL_TYPE_MENU:
+               case V4L2_CTRL_TYPE_BITMASK:
+               case V4L2_CTRL_TYPE_BOOLEAN:
+               case V4L2_CTRL_TYPE_BUTTON:
+               case V4L2_CTRL_TYPE_CTRL_CLASS:
+                       ptr.p_s32 = &c->value;
+                       return ctrl->type_ops->validate(ctrl, 0, ptr);
+
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       ptr.p_s64 = &c->value64;
+                       return ctrl->type_ops->validate(ctrl, 0, ptr);
+               default:
+                       break;
+               }
        }
+       ptr.p = c->ptr;
+       for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++)
+               err = ctrl->type_ops->validate(ctrl, idx, ptr);
+       return err;
 }
 
 static inline u32 node2id(struct list_head *node)
@@ -1777,6 +1895,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
        unsigned elems = 1;
        bool is_array;
        unsigned tot_ctrl_size;
+       unsigned idx;
        void *data;
        int err;
 
@@ -1791,12 +1910,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
        }
        is_array = nr_of_dims > 0;
 
-       if (type == V4L2_CTRL_TYPE_INTEGER64)
+       /* Prefill elem_size for all types handled by std_type_ops */
+       switch (type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
                elem_size = sizeof(s64);
-       else if (type == V4L2_CTRL_TYPE_STRING)
+               break;
+       case V4L2_CTRL_TYPE_STRING:
                elem_size = max + 1;
-       else if (type < V4L2_CTRL_COMPOUND_TYPES)
-               elem_size = sizeof(s32);
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               elem_size = sizeof(u8);
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               elem_size = sizeof(u16);
+               break;
+       case V4L2_CTRL_TYPE_U32:
+               elem_size = sizeof(u32);
+               break;
+       default:
+               if (type < V4L2_CTRL_COMPOUND_TYPES)
+                       elem_size = sizeof(s32);
+               break;
+       }
        tot_ctrl_size = elem_size * elems;
 
        /* Sanity checks */
@@ -1877,8 +2012,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
                ctrl->p_new.p = &ctrl->val;
                ctrl->p_cur.p = &ctrl->cur.val;
        }
-       ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
-       ctrl->type_ops->init(ctrl, 0, ctrl->p_new);
+       for (idx = 0; idx < elems; idx++) {
+               ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
+               ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
+       }
 
        if (handler_new_ref(hdl, ctrl)) {
                kfree(ctrl);
@@ -2574,12 +2711,17 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
                        have_clusters = true;
                if (ctrl->cluster[0] != ctrl)
                        ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
-               if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) {
-                       if (get) {
-                               c->size = ctrl->elem_size;
-                               return -ENOSPC;
+               if (ctrl->is_ptr && !ctrl->is_string) {
+                       unsigned tot_size = ctrl->elems * ctrl->elem_size;
+
+                       if (c->size < tot_size) {
+                               if (get) {
+                                       c->size = tot_size;
+                                       return -ENOSPC;
+                               }
+                               return -EFAULT;
                        }
-                       return -EFAULT;
+                       c->size = tot_size;
                }
                /* Store the ref to the master control of the cluster */
                h->mref = ref;
@@ -3013,26 +3155,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
        struct v4l2_ctrl *master = ctrl->cluster[0];
        int i;
 
-       /* Compound controls are not supported. The user_to_new() and
-        * cur_to_user() calls below would need to be modified not to access
-        * userspace memory when called from set_ctrl().
-        */
-       if (ctrl->is_ptr)
-               return -EINVAL;
-
        /* Reset the 'is_new' flags of the cluster */
        for (i = 0; i < master->ncontrols; i++)
                if (master->cluster[i])
                        master->cluster[i]->is_new = 0;
 
+       if (c)
+               user_to_new(c, ctrl);
+
        /* For autoclusters with volatiles that are switched from auto to
           manual mode we have to update the current volatile values since
           those will become the initial manual values after such a switch. */
        if (master->is_auto && master->has_volatiles && ctrl == master &&
-           !is_cur_manual(master) && c->value == master->manual_mode_value)
+           !is_cur_manual(master) && ctrl->val == master->manual_mode_value)
                update_from_auto_cluster(master);
 
-       user_to_new(c, ctrl);
+       ctrl->is_new = 1;
        return try_or_set_cluster(fh, master, true, ch_flags);
 }
 
@@ -3078,27 +3216,38 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
 }
 EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
 
-int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
 {
-       struct v4l2_ext_control c;
+       lockdep_assert_held(ctrl->handler->lock);
 
        /* It's a driver bug if this happens. */
        WARN_ON(!ctrl->is_int);
-       c.value = val;
-       return set_ctrl_lock(NULL, ctrl, &c);
+       ctrl->val = val;
+       return set_ctrl(NULL, ctrl, NULL, 0);
 }
-EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl);
 
-int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
+int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
 {
-       struct v4l2_ext_control c;
+       lockdep_assert_held(ctrl->handler->lock);
 
        /* It's a driver bug if this happens. */
        WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
-       c.value64 = val;
-       return set_ctrl_lock(NULL, ctrl, &c);
+       *ctrl->p_new.p_s64 = val;
+       return set_ctrl(NULL, ctrl, NULL, 0);
 }
-EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64);
+EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64);
+
+int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
+{
+       lockdep_assert_held(ctrl->handler->lock);
+
+       /* It's a driver bug if this happens. */
+       WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING);
+       strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1);
+       return set_ctrl(NULL, ctrl, NULL, 0);
+}
+EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
 
 void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
 {
@@ -3116,12 +3265,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void
 }
 EXPORT_SYMBOL(v4l2_ctrl_notify);
 
-int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
+int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
                        s64 min, s64 max, u64 step, s64 def)
 {
-       int ret = check_range(ctrl->type, min, max, step, def);
+       int ret;
        struct v4l2_ext_control c;
 
+       lockdep_assert_held(ctrl->handler->lock);
+
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER:
        case V4L2_CTRL_TYPE_INTEGER64:
@@ -3129,13 +3280,18 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
        case V4L2_CTRL_TYPE_MENU:
        case V4L2_CTRL_TYPE_INTEGER_MENU:
        case V4L2_CTRL_TYPE_BITMASK:
+       case V4L2_CTRL_TYPE_U8:
+       case V4L2_CTRL_TYPE_U16:
+       case V4L2_CTRL_TYPE_U32:
+               if (ctrl->is_array)
+                       return -EINVAL;
+               ret = check_range(ctrl->type, min, max, step, def);
                if (ret)
                        return ret;
                break;
        default:
                return -EINVAL;
        }
-       v4l2_ctrl_lock(ctrl);
        ctrl->minimum = min;
        ctrl->maximum = max;
        ctrl->step = step;
@@ -3147,10 +3303,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
                ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE);
        else
                send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
-       v4l2_ctrl_unlock(ctrl);
        return ret;
 }
-EXPORT_SYMBOL(v4l2_ctrl_modify_range);
+EXPORT_SYMBOL(__v4l2_ctrl_modify_range);
 
 static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
 {
This page took 0.034136 seconds and 5 git commands to generate.