bool v4l2_ctrl_type_op_equal(conststruct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2)
{ unsignedint i;
switch (ctrl->type) { case V4L2_CTRL_TYPE_BUTTON: returnfalse; case V4L2_CTRL_TYPE_STRING: for (i = 0; i < ctrl->elems; i++) { unsignedint idx = i * ctrl->elem_size;
switch (which) { case V4L2_CTRL_WHICH_DEF_VAL:
value = ctrl->default_value; break; case V4L2_CTRL_WHICH_MAX_VAL:
value = ctrl->maximum; break; case V4L2_CTRL_WHICH_MIN_VAL:
value = ctrl->minimum; break; default: return;
}
switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: if (which == V4L2_CTRL_WHICH_DEF_VAL)
value = ctrl->minimum;
for (i = from_idx; i < tot_elems; i++) { unsignedint offset = i * ctrl->elem_size;
memset(ptr.p_char + offset, ' ', value);
ptr.p_char[offset + value] = '\0';
} break; case V4L2_CTRL_TYPE_INTEGER64: if (value) { for (i = from_idx; i < tot_elems; i++)
ptr.p_s64[i] = value;
} else {
memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
} 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: if (value) { for (i = from_idx; i < tot_elems; i++)
ptr.p_s32[i] = value;
} else {
memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
} break; case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS:
memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); break; case V4L2_CTRL_TYPE_U8:
memset(ptr.p_u8 + from_idx, value, elems); break; case V4L2_CTRL_TYPE_U16: if (value) { for (i = from_idx; i < tot_elems; i++)
ptr.p_u16[i] = value;
} else {
memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
} break; case V4L2_CTRL_TYPE_U32: if (value) { for (i = from_idx; i < tot_elems; i++)
ptr.p_u32[i] = value;
} else {
memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
} break; default: for (i = from_idx; i < tot_elems; i++) { switch (which) { case V4L2_CTRL_WHICH_DEF_VAL:
std_init_compound(ctrl, i, ptr); break; case V4L2_CTRL_WHICH_MAX_VAL:
std_max_compound(ctrl, i, ptr); break; case V4L2_CTRL_WHICH_MIN_VAL:
std_min_compound(ctrl, i, ptr); break;
}
} break;
}
}
void v4l2_ctrl_type_op_log(conststruct v4l2_ctrl *ctrl)
{ union v4l2_ctrl_ptr ptr = ctrl->p_cur;
if (ctrl->is_array) { unsigned i;
for (i = 0; i < ctrl->nr_of_dims; i++)
pr_cont("[%u]", ctrl->dims[i]);
pr_cont(" ");
}
switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER:
pr_cont("%d", *ptr.p_s32); break; case V4L2_CTRL_TYPE_BOOLEAN:
pr_cont("%s", *ptr.p_s32 ? "true" : "false"); break; case V4L2_CTRL_TYPE_MENU:
pr_cont("%s", ctrl->qmenu[*ptr.p_s32]); break; case V4L2_CTRL_TYPE_INTEGER_MENU:
pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]); break; case V4L2_CTRL_TYPE_BITMASK:
pr_cont("0x%08x", *ptr.p_s32); break; case V4L2_CTRL_TYPE_INTEGER64:
pr_cont("%lld", *ptr.p_s64); break; 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; case V4L2_CTRL_TYPE_AREA:
pr_cont("%ux%u", ptr.p_area->width, ptr.p_area->height); break; case V4L2_CTRL_TYPE_H264_SPS:
pr_cont("H264_SPS"); break; case V4L2_CTRL_TYPE_H264_PPS:
pr_cont("H264_PPS"); break; case V4L2_CTRL_TYPE_H264_SCALING_MATRIX:
pr_cont("H264_SCALING_MATRIX"); break; case V4L2_CTRL_TYPE_H264_SLICE_PARAMS:
pr_cont("H264_SLICE_PARAMS"); break; case V4L2_CTRL_TYPE_H264_DECODE_PARAMS:
pr_cont("H264_DECODE_PARAMS"); break; case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
pr_cont("H264_PRED_WEIGHTS"); break; case V4L2_CTRL_TYPE_FWHT_PARAMS:
pr_cont("FWHT_PARAMS"); break; case V4L2_CTRL_TYPE_VP8_FRAME:
pr_cont("VP8_FRAME"); break; case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
pr_cont("HDR10_CLL_INFO"); break; case V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY:
pr_cont("HDR10_MASTERING_DISPLAY"); break; case V4L2_CTRL_TYPE_MPEG2_QUANTISATION:
pr_cont("MPEG2_QUANTISATION"); break; case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
pr_cont("MPEG2_SEQUENCE"); break; case V4L2_CTRL_TYPE_MPEG2_PICTURE:
pr_cont("MPEG2_PICTURE"); break; case V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR:
pr_cont("VP9_COMPRESSED_HDR"); break; case V4L2_CTRL_TYPE_VP9_FRAME:
pr_cont("VP9_FRAME"); break; case V4L2_CTRL_TYPE_HEVC_SPS:
pr_cont("HEVC_SPS"); break; case V4L2_CTRL_TYPE_HEVC_PPS:
pr_cont("HEVC_PPS"); break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
pr_cont("HEVC_SLICE_PARAMS"); break; case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
pr_cont("HEVC_SCALING_MATRIX"); break; case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
pr_cont("HEVC_DECODE_PARAMS"); break; case V4L2_CTRL_TYPE_AV1_SEQUENCE:
pr_cont("AV1_SEQUENCE"); break; case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
pr_cont("AV1_TILE_GROUP_ENTRY"); break; case V4L2_CTRL_TYPE_AV1_FRAME:
pr_cont("AV1_FRAME"); break; case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
pr_cont("AV1_FILM_GRAIN"); break; case V4L2_CTRL_TYPE_RECT:
pr_cont("(%d,%d)/%ux%u",
ptr.p_rect->left, ptr.p_rect->top,
ptr.p_rect->width, ptr.p_rect->height); break; default:
pr_cont("unknown type %d", ctrl->type); break;
}
}
EXPORT_SYMBOL(v4l2_ctrl_type_op_log);
/* * 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; \ if ((ctrl)->maximum >= 0 && \
val >= (ctrl)->maximum - (s32)((ctrl)->step / 2)) \
val = (ctrl)->maximum; \ else \
val += (s32)((ctrl)->step / 2); \
val = clamp_t(typeof(val), val, \
(ctrl)->minimum, (ctrl)->maximum); \
offset = (val) - (ctrl)->minimum; \
offset = (ctrl)->step * (offset / (u32)(ctrl)->step); \
val = (ctrl)->minimum + offset; \
0; \
})
if (!(fg->flags & V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN)) return 0;
for (i = 1; i < fg->num_y_points; i++) if (fg->point_y_value[i] <= fg->point_y_value[i - 1]) return -EINVAL;
for (i = 1; i < fg->num_cb_points; i++) if (fg->point_cb_value[i] <= fg->point_cb_value[i - 1]) return -EINVAL;
for (i = 1; i < fg->num_cr_points; i++) if (fg->point_cr_value[i] <= fg->point_cr_value[i - 1]) return -EINVAL;
return 0;
}
staticint validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
{ int ret = 0;
ret = validate_av1_quantization(&f->quantization); if (ret) return ret;
ret = validate_av1_segmentation(&f->segmentation); if (ret) return ret;
ret = validate_av1_loop_filter(&f->loop_filter); if (ret) return ret;
ret = validate_av1_cdef(&f->cdef); if (ret) return ret;
ret = validate_av1_loop_restauration(&f->loop_restoration); if (ret) return ret;
switch ((u32)ctrl->type) { case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
p_mpeg2_sequence = p;
switch (p_mpeg2_sequence->chroma_format) { case 1: /* 4:2:0 */ case 2: /* 4:2:2 */ case 3: /* 4:4:4 */ break; default: return -EINVAL;
} break;
case V4L2_CTRL_TYPE_MPEG2_PICTURE:
p_mpeg2_picture = p;
switch (p_mpeg2_picture->intra_dc_precision) { case 0: /* 8 bits */ case 1: /* 9 bits */ case 2: /* 10 bits */ case 3: /* 11 bits */ break; default: return -EINVAL;
}
switch (p_mpeg2_picture->picture_structure) { case V4L2_MPEG2_PIC_TOP_FIELD: case V4L2_MPEG2_PIC_BOTTOM_FIELD: case V4L2_MPEG2_PIC_FRAME: break; default: return -EINVAL;
}
switch (p_mpeg2_picture->picture_coding_type) { case V4L2_MPEG2_PIC_CODING_TYPE_I: case V4L2_MPEG2_PIC_CODING_TYPE_P: case V4L2_MPEG2_PIC_CODING_TYPE_B: break; default: return -EINVAL;
}
zero_reserved(*p_mpeg2_picture); break;
case V4L2_CTRL_TYPE_MPEG2_QUANTISATION: break;
case V4L2_CTRL_TYPE_FWHT_PARAMS:
p_fwht_params = p; if (p_fwht_params->version < V4L2_FWHT_VERSION) return -EINVAL; if (!p_fwht_params->width || !p_fwht_params->height) return -EINVAL; break;
case V4L2_CTRL_TYPE_H264_SPS:
p_h264_sps = p;
/* Some syntax elements are only conditionally valid */ if (p_h264_sps->pic_order_cnt_type != 0) {
p_h264_sps->log2_max_pic_order_cnt_lsb_minus4 = 0;
} elseif (p_h264_sps->pic_order_cnt_type != 1) {
p_h264_sps->num_ref_frames_in_pic_order_cnt_cycle = 0;
p_h264_sps->offset_for_non_ref_pic = 0;
p_h264_sps->offset_for_top_to_bottom_field = 0;
memset(&p_h264_sps->offset_for_ref_frame, 0, sizeof(p_h264_sps->offset_for_ref_frame));
}
if (p_h264_sps->chroma_format_idc < 3)
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
if (p_h264_sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
/* * Chroma 4:2:2 format require at least High 4:2:2 profile. * * The H264 specification and well-known parser implementations * use profile-idc values directly, as that is clearer and * less ambiguous. We do the same here.
*/ if (p_h264_sps->profile_idc < 122 &&
p_h264_sps->chroma_format_idc > 1) return -EINVAL; /* Chroma 4:4:4 format require at least High 4:2:2 profile */ if (p_h264_sps->profile_idc < 244 &&
p_h264_sps->chroma_format_idc > 2) return -EINVAL; if (p_h264_sps->chroma_format_idc > 3) return -EINVAL;
if (p_h264_sps->bit_depth_luma_minus8 > 6) return -EINVAL; if (p_h264_sps->bit_depth_chroma_minus8 > 6) return -EINVAL; if (p_h264_sps->log2_max_frame_num_minus4 > 12) return -EINVAL; if (p_h264_sps->pic_order_cnt_type > 2) return -EINVAL; if (p_h264_sps->log2_max_pic_order_cnt_lsb_minus4 > 12) return -EINVAL; if (p_h264_sps->max_num_ref_frames > V4L2_H264_REF_LIST_LEN) return -EINVAL; break;
case V4L2_CTRL_TYPE_H264_PPS:
p_h264_pps = p;
if (p_h264_pps->num_slice_groups_minus1 > 7) return -EINVAL; if (p_h264_pps->num_ref_idx_l0_default_active_minus1 >
(V4L2_H264_REF_LIST_LEN - 1)) return -EINVAL; if (p_h264_pps->num_ref_idx_l1_default_active_minus1 >
(V4L2_H264_REF_LIST_LEN - 1)) return -EINVAL; if (p_h264_pps->weighted_bipred_idc > 2) return -EINVAL; /* * pic_init_qp_minus26 shall be in the range of * -(26 + QpBdOffset_y) to +25, inclusive, * where QpBdOffset_y is 6 * bit_depth_luma_minus8
*/ if (p_h264_pps->pic_init_qp_minus26 < -62 ||
p_h264_pps->pic_init_qp_minus26 > 25) return -EINVAL; if (p_h264_pps->pic_init_qs_minus26 < -26 ||
p_h264_pps->pic_init_qs_minus26 > 25) return -EINVAL; if (p_h264_pps->chroma_qp_index_offset < -12 ||
p_h264_pps->chroma_qp_index_offset > 12) return -EINVAL; if (p_h264_pps->second_chroma_qp_index_offset < -12 ||
p_h264_pps->second_chroma_qp_index_offset > 12) return -EINVAL; break;
case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: break;
case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
p_h264_pred_weights = p;
if (p_h264_pred_weights->luma_log2_weight_denom > 7) return -EINVAL; if (p_h264_pred_weights->chroma_log2_weight_denom > 7) return -EINVAL; break;
case V4L2_CTRL_TYPE_H264_SLICE_PARAMS:
p_h264_slice_params = p;
if (p_h264_slice_params->slice_type != V4L2_H264_SLICE_TYPE_B)
p_h264_slice_params->flags &=
~V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED;
if (p_h264_slice_params->colour_plane_id > 2) return -EINVAL; if (p_h264_slice_params->cabac_init_idc > 2) return -EINVAL; if (p_h264_slice_params->disable_deblocking_filter_idc > 2) return -EINVAL; if (p_h264_slice_params->slice_alpha_c0_offset_div2 < -6 ||
p_h264_slice_params->slice_alpha_c0_offset_div2 > 6) return -EINVAL; if (p_h264_slice_params->slice_beta_offset_div2 < -6 ||
p_h264_slice_params->slice_beta_offset_div2 > 6) return -EINVAL;
if (p_h264_slice_params->slice_type == V4L2_H264_SLICE_TYPE_I ||
p_h264_slice_params->slice_type == V4L2_H264_SLICE_TYPE_SI)
p_h264_slice_params->num_ref_idx_l0_active_minus1 = 0; if (p_h264_slice_params->slice_type != V4L2_H264_SLICE_TYPE_B)
p_h264_slice_params->num_ref_idx_l1_active_minus1 = 0;
if (p_h264_slice_params->num_ref_idx_l0_active_minus1 >
(V4L2_H264_REF_LIST_LEN - 1)) return -EINVAL; if (p_h264_slice_params->num_ref_idx_l1_active_minus1 >
(V4L2_H264_REF_LIST_LEN - 1)) return -EINVAL;
zero_reserved(*p_h264_slice_params); break;
case V4L2_CTRL_TYPE_H264_DECODE_PARAMS:
p_h264_dec_params = p;
if (p_h264_dec_params->nal_ref_idc > 3) return -EINVAL; for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { struct v4l2_h264_dpb_entry *dpb_entry =
&p_h264_dec_params->dpb[i];
/* The following restriction comes from ITU-T Rec. H.265 spec */ if (p_hdr10_mastering->max_display_mastering_luminance ==
V4L2_HDR10_MASTERING_MAX_LUMA_LOW &&
p_hdr10_mastering->min_display_mastering_luminance ==
V4L2_HDR10_MASTERING_MIN_LUMA_HIGH) return -EINVAL;
break;
case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: break;
case V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR: return validate_vp9_compressed_hdr(p);
case V4L2_CTRL_TYPE_VP9_FRAME: return validate_vp9_frame(p); case V4L2_CTRL_TYPE_AV1_FRAME: return validate_av1_frame(p); case V4L2_CTRL_TYPE_AV1_SEQUENCE: return validate_av1_sequence(p); case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY: break; case V4L2_CTRL_TYPE_AV1_FILM_GRAIN: return validate_av1_film_grain(p);
case V4L2_CTRL_TYPE_AREA:
area = p; if (!area->width || !area->height) return -EINVAL; break;
case V4L2_CTRL_TYPE_RECT:
rect = p; if (!rect->width || !rect->height) return -EINVAL; break;
switch ((u32)ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: /* * 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 - (s64)(ctrl->step / 2))
val = ctrl->maximum; else
val += (s64)(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[idx] = !!ptr.p_s32[idx]; return 0;
case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum) return -ERANGE; if (ptr.p_s32[idx] < BITS_PER_LONG_LONG &&
(ctrl->menu_skip_mask & BIT_ULL(ptr.p_s32[idx]))) return -EINVAL; if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
ctrl->qmenu[ptr.p_s32[idx]][0] == '\0') return -EINVAL; return 0;
case V4L2_CTRL_TYPE_BITMASK:
ptr.p_s32[idx] &= ctrl->maximum; return 0;
case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS:
ptr.p_s32[idx] = 0; return 0;
case V4L2_CTRL_TYPE_STRING:
idx *= ctrl->elem_size;
len = strlen(ptr.p_char + idx); if (len < ctrl->minimum) return -ERANGE; if ((len - (u32)ctrl->minimum) % (u32)ctrl->step) return -ERANGE; return 0;
/* Copy the one value to another. */ staticvoid ptr_to_ptr(struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to, unsignedint elems)
{ if (ctrl == NULL) return;
memcpy(to.p, from.p_const, elems * ctrl->elem_size);
}
/* Copy the new value to the current value. */ void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
{ bool changed;
if (ctrl == NULL) return;
/* has_changed is set by cluster_changed */
changed = ctrl->has_changed; if (changed) { if (ctrl->is_dyn_array)
ctrl->elems = ctrl->new_elems;
ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur, ctrl->elems);
}
if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */
ctrl->flags &=
~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE); if (!is_cur_manual(ctrl->cluster[0])) {
ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; if (ctrl->cluster[0]->has_volatiles)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
fh = NULL;
} if (changed || ch_flags) { /* If a control was changed that was not one of the controls
modified by the application, then send the event to all. */ if (!ctrl->is_new)
fh = NULL;
send_event(fh, ctrl,
(changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | ch_flags); if (ctrl->call_notify && changed && ctrl->handler->notify)
ctrl->handler->notify(ctrl, ctrl->handler->notify_priv);
}
}
/* Copy the current value to the new value */ void cur_to_new(struct v4l2_ctrl *ctrl)
{ if (ctrl == NULL) return; if (ctrl->is_dyn_array)
ctrl->new_elems = ctrl->elems;
ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems);
}
/* Copy the request value to the new value */ int req_to_new(struct v4l2_ctrl_ref *ref)
{ struct v4l2_ctrl *ctrl;
if (!ref) return 0;
ctrl = ref->ctrl;
/* * This control was never set in the request, so just use the current * value.
*/ if (!ref->p_req_valid) { if (ctrl->is_dyn_array)
ctrl->new_elems = ctrl->elems;
ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); return 0;
}
/* Not an array, so just copy the request value */ if (!ctrl->is_array) {
ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); return 0;
}
/* Sanity check, should never happen */ if (WARN_ON(!ref->p_req_array_alloc_elems)) return -ENOMEM;
if (!ctrl->is_dyn_array &&
ref->p_req_elems != ctrl->p_array_alloc_elems) return -ENOMEM;
/* * Check if the number of elements in the request is more than the * elements in ctrl->p_array. If so, attempt to realloc ctrl->p_array. * Note that p_array is allocated with twice the number of elements * in the dynamic array since it has to store both the current and * new value of such a control.
*/ if (ref->p_req_elems > ctrl->p_array_alloc_elems) { unsignedint sz = ref->p_req_elems * ctrl->elem_size; void *old = ctrl->p_array; void *tmp = kvzalloc(2 * sz, GFP_KERNEL);
/* Control range checking */ int check_range(enum v4l2_ctrl_type type,
s64 min, s64 max, u64 step, s64 def)
{ switch (type) { case V4L2_CTRL_TYPE_BOOLEAN: if (step != 1 || max > 1 || min < 0) return -ERANGE;
fallthrough; 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) return -ERANGE; return 0; case V4L2_CTRL_TYPE_BITMASK: if (step || min || !max || (def & ~max)) return -ERANGE; return 0; case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: if (min > max || def < min || def > max ||
min < 0 || (step && max >= BITS_PER_LONG_LONG)) return -ERANGE; /* Note: step == menu_skip_mask for menu controls.
So here we check if the default value is masked out. */ if (def < BITS_PER_LONG_LONG && (step & BIT_ULL(def))) return -EINVAL; return 0; case V4L2_CTRL_TYPE_STRING: if (min > max || min < 0 || step < 1 || def) return -ERANGE; return 0; default: return 0;
}
}
/* Set the handler's error code if it wasn't set earlier already */ staticinlineint handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
{ if (hdl->error == 0)
hdl->error = err; return err;
}
/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing with applications that do not use the NEXT_CTRL flag.
We just find the n-th private user control. It's O(N), but that should not
be an issue in this particular case. */ staticstruct v4l2_ctrl_ref *find_private_ref( struct v4l2_ctrl_handler *hdl, u32 id)
{ struct v4l2_ctrl_ref *ref;
id -= V4L2_CID_PRIVATE_BASE;
list_for_each_entry(ref, &hdl->ctrl_refs, node) { /* Search for private user controls that are compatible with
VIDIOC_G/S_CTRL. */ if (V4L2_CTRL_ID2WHICH(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { if (!ref->ctrl->is_int) continue; if (id == 0) return ref;
id--;
}
} return NULL;
}
/* Find a control with the given ID. */ struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
{ struct v4l2_ctrl_ref *ref; int bucket;
id &= V4L2_CTRL_ID_MASK;
/* Old-style private controls need special handling */ if (id >= V4L2_CID_PRIVATE_BASE) return find_private_ref(hdl, id);
bucket = id % hdl->nr_of_buckets;
/* Simple optimization: cache the last control found */ if (hdl->cached && hdl->cached->ctrl->id == id) return hdl->cached;
/* Not in cache, search the hash */
ref = hdl->buckets ? hdl->buckets[bucket] : NULL; while (ref && ref->ctrl->id != id)
ref = ref->next;
/* Find a control with the given ID. Take the handler's lock first. */ struct v4l2_ctrl_ref *find_ref_lock(struct v4l2_ctrl_handler *hdl, u32 id)
{ struct v4l2_ctrl_ref *ref = NULL;
/* Find a control with the given ID. */ struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
{ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */ int handler_new_ref(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl, struct v4l2_ctrl_ref **ctrl_ref, bool from_other_dev, bool allocate_req)
{ struct v4l2_ctrl_ref *ref; struct v4l2_ctrl_ref *new_ref;
u32 id = ctrl->id;
u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1; int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ unsignedint size_extra_req = 0;
if (ctrl_ref)
*ctrl_ref = NULL;
/* * Automatically add the control class if it is not yet present and * the new control is not a compound control.
*/ if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES &&
id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) return hdl->error;
/* Add immediately at the end of the list if the list is empty, or if the last element in the list has a lower ID. This ensures that when elements are added in ascending order the
insertion is an O(1) operation. */ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
list_add_tail(&new_ref->node, &hdl->ctrl_refs); goto insert_in_hash;
}
/* Find insert position in sorted list */
list_for_each_entry(ref, &hdl->ctrl_refs, node) { if (ref->ctrl->id < id) continue; /* Don't add duplicates */ if (ref->ctrl->id == id) {
kfree(new_ref); goto unlock;
}
list_add(&new_ref->node, ref->node.prev); break;
}
insert_in_hash: /* Insert the control node in the hash */
new_ref->next = hdl->buckets[bucket];
hdl->buckets[bucket] = new_ref; if (ctrl_ref)
*ctrl_ref = new_ref; if (ctrl->handler == hdl) { /* By default each control starts in a cluster of its own. * new_ref->ctrl is basically a cluster array with one * element, so that's perfect to use as the cluster pointer. * But only do this for the handler that owns the control.
*/
ctrl->cluster = &new_ref->ctrl;
ctrl->ncontrols = 1;
}
while (dims && dims[nr_of_dims]) {
elems *= dims[nr_of_dims];
nr_of_dims++; if (nr_of_dims == V4L2_CTRL_MAX_DIMS) break;
}
is_array = nr_of_dims > 0;
/* Prefill elem_size for all types handled by std_type_ops */ switch ((u32)type) { case V4L2_CTRL_TYPE_INTEGER64:
elem_size = sizeof(s64); break; case V4L2_CTRL_TYPE_STRING:
elem_size = max + 1; 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; case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
elem_size = sizeof(struct v4l2_ctrl_mpeg2_sequence); break; case V4L2_CTRL_TYPE_MPEG2_PICTURE:
elem_size = sizeof(struct v4l2_ctrl_mpeg2_picture); break; case V4L2_CTRL_TYPE_MPEG2_QUANTISATION:
elem_size = sizeof(struct v4l2_ctrl_mpeg2_quantisation); break; case V4L2_CTRL_TYPE_FWHT_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_fwht_params); break; case V4L2_CTRL_TYPE_H264_SPS:
elem_size = sizeof(struct v4l2_ctrl_h264_sps); break; case V4L2_CTRL_TYPE_H264_PPS:
elem_size = sizeof(struct v4l2_ctrl_h264_pps); break; case V4L2_CTRL_TYPE_H264_SCALING_MATRIX:
elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix); break; case V4L2_CTRL_TYPE_H264_SLICE_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_h264_slice_params); break; case V4L2_CTRL_TYPE_H264_DECODE_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_h264_decode_params); break; case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
elem_size = sizeof(struct v4l2_ctrl_h264_pred_weights); break; case V4L2_CTRL_TYPE_VP8_FRAME:
elem_size = sizeof(struct v4l2_ctrl_vp8_frame); break; case V4L2_CTRL_TYPE_HEVC_SPS:
elem_size = sizeof(struct v4l2_ctrl_hevc_sps); break; case V4L2_CTRL_TYPE_HEVC_PPS:
elem_size = sizeof(struct v4l2_ctrl_hevc_pps); break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); break; case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix); break; case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_hevc_decode_params); break; case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
elem_size = sizeof(struct v4l2_ctrl_hdr10_cll_info); break; case V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY:
elem_size = sizeof(struct v4l2_ctrl_hdr10_mastering_display); break; case V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR:
elem_size = sizeof(struct v4l2_ctrl_vp9_compressed_hdr); break; case V4L2_CTRL_TYPE_VP9_FRAME:
elem_size = sizeof(struct v4l2_ctrl_vp9_frame); break; case V4L2_CTRL_TYPE_AV1_SEQUENCE:
elem_size = sizeof(struct v4l2_ctrl_av1_sequence); break; case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
elem_size = sizeof(struct v4l2_ctrl_av1_tile_group_entry); break; case V4L2_CTRL_TYPE_AV1_FRAME:
elem_size = sizeof(struct v4l2_ctrl_av1_frame); break; case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
elem_size = sizeof(struct v4l2_ctrl_av1_film_grain); break; case V4L2_CTRL_TYPE_AREA:
elem_size = sizeof(struct v4l2_area); break; case V4L2_CTRL_TYPE_RECT:
elem_size = sizeof(struct v4l2_rect); break; default: if (type < V4L2_CTRL_COMPOUND_TYPES)
elem_size = sizeof(s32); break;
}
if (type < V4L2_CTRL_COMPOUND_TYPES &&
type != V4L2_CTRL_TYPE_BUTTON &&
type != V4L2_CTRL_TYPE_CTRL_CLASS &&
type != V4L2_CTRL_TYPE_STRING)
flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
/* Sanity checks */ if (id == 0 || name == NULL || !elem_size ||
id >= V4L2_CID_PRIVATE_BASE ||
(type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
(type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {
handler_set_err(hdl, -ERANGE); return NULL;
}
err = check_range(type, min, max, step, def); if (err) {
handler_set_err(hdl, err); return NULL;
} if (is_array &&
(type == V4L2_CTRL_TYPE_BUTTON ||
type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
handler_set_err(hdl, -EINVAL); return NULL;
} if (flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) { /* * For now only support this for one-dimensional arrays only. * * This can be relaxed in the future, but this will * require more effort.
*/ if (nr_of_dims != 1) {
handler_set_err(hdl, -EINVAL); return NULL;
} /* Start with just 1 element */
elems = 1;
}
/* Helper function for standard menu controls with driver defined menu */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, conststruct v4l2_ctrl_ops *ops, u32 id, u8 _max,
u64 mask, u8 _def, constchar * const *qmenu)
{ enum v4l2_ctrl_type type; constchar *name;
u32 flags;
u64 step;
s64 min;
s64 max = _max;
s64 def = _def;
/* v4l2_ctrl_new_std_menu_items() should only be called for * standard controls without a standard menu.
*/ if (v4l2_ctrl_get_menu(id)) {
handler_set_err(hdl, -EINVAL); return NULL;
}
/* Add the controls from another handler to our own. */ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add, bool (*filter)(conststruct v4l2_ctrl *ctrl), bool from_other_dev)
{ struct v4l2_ctrl_ref *ref; int ret = 0;
/* Do nothing if either handler is NULL or if they are the same */ if (!hdl || !add || hdl == add) return 0; if (hdl->error) return hdl->error;
mutex_lock(add->lock);
list_for_each_entry(ref, &add->ctrl_refs, node) { struct v4l2_ctrl *ctrl = ref->ctrl;
/* Skip handler-private controls. */ if (ctrl->is_private) continue; /* And control classes */ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) continue; /* Filter any unwanted controls */ if (filter && !filter(ctrl)) continue;
ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false); if (ret) break;
}
mutex_unlock(add->lock); return ret;
}
EXPORT_SYMBOL(v4l2_ctrl_add_handler);
bool v4l2_ctrl_radio_filter(conststruct v4l2_ctrl *ctrl)
{ if (V4L2_CTRL_ID2WHICH(ctrl->id) == V4L2_CTRL_CLASS_FM_TX) returntrue; if (V4L2_CTRL_ID2WHICH(ctrl->id) == V4L2_CTRL_CLASS_FM_RX) returntrue; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: returntrue; default: break;
} returnfalse;
}
EXPORT_SYMBOL(v4l2_ctrl_radio_filter);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.