|
@@ -0,0 +1,648 @@
|
|
|
+Introduction
|
|
|
+============
|
|
|
+
|
|
|
+The V4L2 control API seems simple enough, but quickly becomes very hard to
|
|
|
+implement correctly in drivers. But much of the code needed to handle controls
|
|
|
+is actually not driver specific and can be moved to the V4L core framework.
|
|
|
+
|
|
|
+After all, the only part that a driver developer is interested in is:
|
|
|
+
|
|
|
+1) How do I add a control?
|
|
|
+2) How do I set the control's value? (i.e. s_ctrl)
|
|
|
+
|
|
|
+And occasionally:
|
|
|
+
|
|
|
+3) How do I get the control's value? (i.e. g_volatile_ctrl)
|
|
|
+4) How do I validate the user's proposed control value? (i.e. try_ctrl)
|
|
|
+
|
|
|
+All the rest is something that can be done centrally.
|
|
|
+
|
|
|
+The control framework was created in order to implement all the rules of the
|
|
|
+V4L2 specification with respect to controls in a central place. And to make
|
|
|
+life as easy as possible for the driver developer.
|
|
|
+
|
|
|
+Note that the control framework relies on the presence of a struct v4l2_device
|
|
|
+for V4L2 drivers and struct v4l2_subdev for sub-device drivers.
|
|
|
+
|
|
|
+
|
|
|
+Objects in the framework
|
|
|
+========================
|
|
|
+
|
|
|
+There are two main objects:
|
|
|
+
|
|
|
+The v4l2_ctrl object describes the control properties and keeps track of the
|
|
|
+control's value (both the current value and the proposed new value).
|
|
|
+
|
|
|
+v4l2_ctrl_handler is the object that keeps track of controls. It maintains a
|
|
|
+list of v4l2_ctrl objects that it owns and another list of references to
|
|
|
+controls, possibly to controls owned by other handlers.
|
|
|
+
|
|
|
+
|
|
|
+Basic usage for V4L2 and sub-device drivers
|
|
|
+===========================================
|
|
|
+
|
|
|
+1) Prepare the driver:
|
|
|
+
|
|
|
+1.1) Add the handler to your driver's top-level struct:
|
|
|
+
|
|
|
+ struct foo_dev {
|
|
|
+ ...
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+ ...
|
|
|
+ };
|
|
|
+
|
|
|
+ struct foo_dev *foo;
|
|
|
+
|
|
|
+1.2) Initialize the handler:
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
|
|
|
+
|
|
|
+ The second argument is a hint telling the function how many controls this
|
|
|
+ handler is expected to handle. It will allocate a hashtable based on this
|
|
|
+ information. It is a hint only.
|
|
|
+
|
|
|
+1.3) Hook the control handler into the driver:
|
|
|
+
|
|
|
+1.3.1) For V4L2 drivers do this:
|
|
|
+
|
|
|
+ struct foo_dev {
|
|
|
+ ...
|
|
|
+ struct v4l2_device v4l2_dev;
|
|
|
+ ...
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+ ...
|
|
|
+ };
|
|
|
+
|
|
|
+ foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
|
|
|
+
|
|
|
+ Where foo->v4l2_dev is of type struct v4l2_device.
|
|
|
+
|
|
|
+ Finally, remove all control functions from your v4l2_ioctl_ops:
|
|
|
+ vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
|
|
|
+ vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
|
|
|
+ Those are now no longer needed.
|
|
|
+
|
|
|
+1.3.2) For sub-device drivers do this:
|
|
|
+
|
|
|
+ struct foo_dev {
|
|
|
+ ...
|
|
|
+ struct v4l2_subdev sd;
|
|
|
+ ...
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+ ...
|
|
|
+ };
|
|
|
+
|
|
|
+ foo->sd.ctrl_handler = &foo->ctrl_handler;
|
|
|
+
|
|
|
+ Where foo->sd is of type struct v4l2_subdev.
|
|
|
+
|
|
|
+ And set all core control ops in your struct v4l2_subdev_core_ops to these
|
|
|
+ helpers:
|
|
|
+
|
|
|
+ .queryctrl = v4l2_subdev_queryctrl,
|
|
|
+ .querymenu = v4l2_subdev_querymenu,
|
|
|
+ .g_ctrl = v4l2_subdev_g_ctrl,
|
|
|
+ .s_ctrl = v4l2_subdev_s_ctrl,
|
|
|
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
|
|
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
|
|
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
|
|
+
|
|
|
+ Note: this is a temporary solution only. Once all V4L2 drivers that depend
|
|
|
+ on subdev drivers are converted to the control framework these helpers will
|
|
|
+ no longer be needed.
|
|
|
+
|
|
|
+1.4) Clean up the handler at the end:
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
|
|
|
+
|
|
|
+
|
|
|
+2) Add controls:
|
|
|
+
|
|
|
+You add non-menu controls by calling v4l2_ctrl_new_std:
|
|
|
+
|
|
|
+ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
|
|
|
+ const struct v4l2_ctrl_ops *ops,
|
|
|
+ u32 id, s32 min, s32 max, u32 step, s32 def);
|
|
|
+
|
|
|
+Menu controls are added by calling v4l2_ctrl_new_std_menu:
|
|
|
+
|
|
|
+ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
|
|
|
+ const struct v4l2_ctrl_ops *ops,
|
|
|
+ u32 id, s32 max, s32 skip_mask, s32 def);
|
|
|
+
|
|
|
+These functions are typically called right after the v4l2_ctrl_handler_init:
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
|
|
|
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
|
|
|
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
|
|
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
|
|
|
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
|
|
|
+ v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
|
|
|
+ V4L2_CID_POWER_LINE_FREQUENCY,
|
|
|
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
|
|
|
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
|
|
|
+ ...
|
|
|
+ if (foo->ctrl_handler.error) {
|
|
|
+ int err = foo->ctrl_handler.error;
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
|
|
|
+control, but if you do not need to access the pointer outside the control ops,
|
|
|
+then there is no need to store it.
|
|
|
+
|
|
|
+The v4l2_ctrl_new_std function will fill in most fields based on the control
|
|
|
+ID except for the min, max, step and default values. These are passed in the
|
|
|
+last four arguments. These values are driver specific while control attributes
|
|
|
+like type, name, flags are all global. The control's current value will be set
|
|
|
+to the default value.
|
|
|
+
|
|
|
+The v4l2_ctrl_new_std_menu function is very similar but it is used for menu
|
|
|
+controls. There is no min argument since that is always 0 for menu controls,
|
|
|
+and instead of a step there is a skip_mask argument: if bit X is 1, then menu
|
|
|
+item X is skipped.
|
|
|
+
|
|
|
+Note that if something fails, the function will return NULL or an error and
|
|
|
+set ctrl_handler->error to the error code. If ctrl_handler->error was already
|
|
|
+set, then it will just return and do nothing. This is also true for
|
|
|
+v4l2_ctrl_handler_init if it cannot allocate the internal data structure.
|
|
|
+
|
|
|
+This makes it easy to init the handler and just add all controls and only check
|
|
|
+the error code at the end. Saves a lot of repetitive error checking.
|
|
|
+
|
|
|
+It is recommended to add controls in ascending control ID order: it will be
|
|
|
+a bit faster that way.
|
|
|
+
|
|
|
+3) Optionally force initial control setup:
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_setup(&foo->ctrl_handler);
|
|
|
+
|
|
|
+This will call s_ctrl for all controls unconditionally. Effectively this
|
|
|
+initializes the hardware to the default control values. It is recommended
|
|
|
+that you do this as this ensures that both the internal data structures and
|
|
|
+the hardware are in sync.
|
|
|
+
|
|
|
+4) Finally: implement the v4l2_ctrl_ops
|
|
|
+
|
|
|
+ static const struct v4l2_ctrl_ops foo_ctrl_ops = {
|
|
|
+ .s_ctrl = foo_s_ctrl,
|
|
|
+ };
|
|
|
+
|
|
|
+Usually all you need is s_ctrl:
|
|
|
+
|
|
|
+ static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
+ {
|
|
|
+ struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
|
|
|
+
|
|
|
+ switch (ctrl->id) {
|
|
|
+ case V4L2_CID_BRIGHTNESS:
|
|
|
+ write_reg(0x123, ctrl->val);
|
|
|
+ break;
|
|
|
+ case V4L2_CID_CONTRAST:
|
|
|
+ write_reg(0x456, ctrl->val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+The control ops are called with the v4l2_ctrl pointer as argument.
|
|
|
+The new control value has already been validated, so all you need to do is
|
|
|
+to actually update the hardware registers.
|
|
|
+
|
|
|
+You're done! And this is sufficient for most of the drivers we have. No need
|
|
|
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And
|
|
|
+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
|
|
|
+
|
|
|
+
|
|
|
+==============================================================================
|
|
|
+
|
|
|
+The remainder of this document deals with more advanced topics and scenarios.
|
|
|
+In practice the basic usage as described above is sufficient for most drivers.
|
|
|
+
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+
|
|
|
+Inheriting Controls
|
|
|
+===================
|
|
|
+
|
|
|
+When a sub-device is registered with a V4L2 driver by calling
|
|
|
+v4l2_device_register_subdev() and the ctrl_handler fields of both v4l2_subdev
|
|
|
+and v4l2_device are set, then the controls of the subdev will become
|
|
|
+automatically available in the V4L2 driver as well. If the subdev driver
|
|
|
+contains controls that already exist in the V4L2 driver, then those will be
|
|
|
+skipped (so a V4L2 driver can always override a subdev control).
|
|
|
+
|
|
|
+What happens here is that v4l2_device_register_subdev() calls
|
|
|
+v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
|
|
|
+of v4l2_device.
|
|
|
+
|
|
|
+
|
|
|
+Accessing Control Values
|
|
|
+========================
|
|
|
+
|
|
|
+The v4l2_ctrl struct contains these two unions:
|
|
|
+
|
|
|
+ /* The current control value. */
|
|
|
+ union {
|
|
|
+ s32 val;
|
|
|
+ s64 val64;
|
|
|
+ char *string;
|
|
|
+ } cur;
|
|
|
+
|
|
|
+ /* The new control value. */
|
|
|
+ union {
|
|
|
+ s32 val;
|
|
|
+ s64 val64;
|
|
|
+ char *string;
|
|
|
+ };
|
|
|
+
|
|
|
+Within the control ops you can freely use these. The val and val64 speak for
|
|
|
+themselves. The string pointers point to character buffers of length
|
|
|
+ctrl->maximum + 1, and are always 0-terminated.
|
|
|
+
|
|
|
+In most cases 'cur' contains the current cached control value. When you create
|
|
|
+a new control this value is made identical to the default value. After calling
|
|
|
+v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally
|
|
|
+a good idea to call this function.
|
|
|
+
|
|
|
+Whenever a new value is set that new value is automatically cached. This means
|
|
|
+that most drivers do not need to implement the g_volatile_ctrl() op. The
|
|
|
+exception is for controls that return a volatile register such as a signal
|
|
|
+strength read-out that changes continuously. In that case you will need to
|
|
|
+implement g_volatile_ctrl like this:
|
|
|
+
|
|
|
+ static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
+ {
|
|
|
+ switch (ctrl->id) {
|
|
|
+ case V4L2_CID_BRIGHTNESS:
|
|
|
+ ctrl->cur.val = read_reg(0x123);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+The 'new value' union is not used in g_volatile_ctrl. In general controls
|
|
|
+that need to implement g_volatile_ctrl are read-only controls.
|
|
|
+
|
|
|
+To mark a control as volatile you have to set the is_volatile flag:
|
|
|
+
|
|
|
+ ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
|
|
|
+ if (ctrl)
|
|
|
+ ctrl->is_volatile = 1;
|
|
|
+
|
|
|
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
|
|
|
+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
|
|
|
+contains the current value, which you can use (but not change!) as well.
|
|
|
+
|
|
|
+If s_ctrl returns 0 (OK), then the control framework will copy the new final
|
|
|
+values to the 'cur' union.
|
|
|
+
|
|
|
+While in g_volatile/s/try_ctrl you can access the value of all controls owned
|
|
|
+by the same handler since the handler's lock is held. If you need to access
|
|
|
+the value of controls owned by other handlers, then you have to be very careful
|
|
|
+not to introduce deadlocks.
|
|
|
+
|
|
|
+Outside of the control ops you have to go through to helper functions to get
|
|
|
+or set a single control value safely in your driver:
|
|
|
+
|
|
|
+ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
|
|
|
+ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
|
|
|
+
|
|
|
+These functions go through the control framework just as VIDIOC_G/S_CTRL ioctls
|
|
|
+do. Don't use these inside the control ops g_volatile/s/try_ctrl, though, that
|
|
|
+will result in a deadlock since these helpers lock the handler as well.
|
|
|
+
|
|
|
+You can also take the handler lock yourself:
|
|
|
+
|
|
|
+ mutex_lock(&state->ctrl_handler.lock);
|
|
|
+ printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
|
|
|
+ printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
|
|
|
+ mutex_unlock(&state->ctrl_handler.lock);
|
|
|
+
|
|
|
+
|
|
|
+Menu Controls
|
|
|
+=============
|
|
|
+
|
|
|
+The v4l2_ctrl struct contains this union:
|
|
|
+
|
|
|
+ union {
|
|
|
+ u32 step;
|
|
|
+ u32 menu_skip_mask;
|
|
|
+ };
|
|
|
+
|
|
|
+For menu controls menu_skip_mask is used. What it does is that it allows you
|
|
|
+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
|
|
|
+implementation where you can return -EINVAL if a certain menu item is not
|
|
|
+present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
|
|
|
+menu controls.
|
|
|
+
|
|
|
+A good example is the MPEG Audio Layer II Bitrate menu control where the
|
|
|
+menu is a list of standardized possible bitrates. But in practice hardware
|
|
|
+implementations will only support a subset of those. By setting the skip
|
|
|
+mask you can tell the framework which menu items should be skipped. Setting
|
|
|
+it to 0 means that all menu items are supported.
|
|
|
+
|
|
|
+You set this mask either through the v4l2_ctrl_config struct for a custom
|
|
|
+control, or by calling v4l2_ctrl_new_std_menu().
|
|
|
+
|
|
|
+
|
|
|
+Custom Controls
|
|
|
+===============
|
|
|
+
|
|
|
+Driver specific controls can be created using v4l2_ctrl_new_custom():
|
|
|
+
|
|
|
+ static const struct v4l2_ctrl_config ctrl_filter = {
|
|
|
+ .ops = &ctrl_custom_ops,
|
|
|
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
|
|
|
+ .name = "Spatial Filter",
|
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
+ .flags = V4L2_CTRL_FLAG_SLIDER,
|
|
|
+ .max = 15,
|
|
|
+ .step = 1,
|
|
|
+ };
|
|
|
+
|
|
|
+ ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
|
|
|
+
|
|
|
+The last argument is the priv pointer which can be set to driver-specific
|
|
|
+private data.
|
|
|
+
|
|
|
+The v4l2_ctrl_config struct also has fields to set the is_private and is_volatile
|
|
|
+flags.
|
|
|
+
|
|
|
+If the name field is not set, then the framework will assume this is a standard
|
|
|
+control and will fill in the name, type and flags fields accordingly.
|
|
|
+
|
|
|
+
|
|
|
+Active and Grabbed Controls
|
|
|
+===========================
|
|
|
+
|
|
|
+If you get more complex relationships between controls, then you may have to
|
|
|
+activate and deactivate controls. For example, if the Chroma AGC control is
|
|
|
+on, then the Chroma Gain control is inactive. That is, you may set it, but
|
|
|
+the value will not be used by the hardware as long as the automatic gain
|
|
|
+control is on. Typically user interfaces can disable such input fields.
|
|
|
+
|
|
|
+You can set the 'active' status using v4l2_ctrl_activate(). By default all
|
|
|
+controls are active. Note that the framework does not check for this flag.
|
|
|
+It is meant purely for GUIs. The function is typically called from within
|
|
|
+s_ctrl.
|
|
|
+
|
|
|
+The other flag is the 'grabbed' flag. A grabbed control means that you cannot
|
|
|
+change it because it is in use by some resource. Typical examples are MPEG
|
|
|
+bitrate controls that cannot be changed while capturing is in progress.
|
|
|
+
|
|
|
+If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework
|
|
|
+will return -EBUSY if an attempt is made to set this control. The
|
|
|
+v4l2_ctrl_grab() function is typically called from the driver when it
|
|
|
+starts or stops streaming.
|
|
|
+
|
|
|
+
|
|
|
+Control Clusters
|
|
|
+================
|
|
|
+
|
|
|
+By default all controls are independent from the others. But in more
|
|
|
+complex scenarios you can get dependencies from one control to another.
|
|
|
+In that case you need to 'cluster' them:
|
|
|
+
|
|
|
+ struct foo {
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+#define AUDIO_CL_VOLUME (0)
|
|
|
+#define AUDIO_CL_MUTE (1)
|
|
|
+ struct v4l2_ctrl *audio_cluster[2];
|
|
|
+ ...
|
|
|
+ };
|
|
|
+
|
|
|
+ state->audio_cluster[AUDIO_CL_VOLUME] =
|
|
|
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
|
|
|
+ state->audio_cluster[AUDIO_CL_MUTE] =
|
|
|
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
|
|
|
+ v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
|
|
|
+
|
|
|
+From now on whenever one or more of the controls belonging to the same
|
|
|
+cluster is set (or 'gotten', or 'tried'), only the control ops of the first
|
|
|
+control ('volume' in this example) is called. You effectively create a new
|
|
|
+composite control. Similar to how a 'struct' works in C.
|
|
|
+
|
|
|
+So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set
|
|
|
+all two controls belonging to the audio_cluster:
|
|
|
+
|
|
|
+ static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
+ {
|
|
|
+ struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
|
|
|
+
|
|
|
+ switch (ctrl->id) {
|
|
|
+ case V4L2_CID_AUDIO_VOLUME: {
|
|
|
+ struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
|
|
|
+
|
|
|
+ write_reg(0x123, mute->val ? 0 : ctrl->val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case V4L2_CID_CONTRAST:
|
|
|
+ write_reg(0x456, ctrl->val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+In the example above the following are equivalent for the VOLUME case:
|
|
|
+
|
|
|
+ ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
|
|
|
+ ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
|
|
|
+
|
|
|
+Note that controls in a cluster may be NULL. For example, if for some
|
|
|
+reason mute was never added (because the hardware doesn't support that
|
|
|
+particular feature), then mute will be NULL. So in that case we have a
|
|
|
+cluster of 2 controls, of which only 1 is actually instantiated. The
|
|
|
+only restriction is that the first control of the cluster must always be
|
|
|
+present, since that is the 'master' control of the cluster. The master
|
|
|
+control is the one that identifies the cluster and that provides the
|
|
|
+pointer to the v4l2_ctrl_ops struct that is used for that cluster.
|
|
|
+
|
|
|
+Obviously, all controls in the cluster array must be initialized to either
|
|
|
+a valid control or to NULL.
|
|
|
+
|
|
|
+
|
|
|
+VIDIOC_LOG_STATUS Support
|
|
|
+=========================
|
|
|
+
|
|
|
+This ioctl allow you to dump the current status of a driver to the kernel log.
|
|
|
+The v4l2_ctrl_handler_log_status(ctrl_handler, prefix) can be used to dump the
|
|
|
+value of the controls owned by the given handler to the log. You can supply a
|
|
|
+prefix as well. If the prefix didn't end with a space, then ': ' will be added
|
|
|
+for you.
|
|
|
+
|
|
|
+
|
|
|
+Different Handlers for Different Video Nodes
|
|
|
+============================================
|
|
|
+
|
|
|
+Usually the V4L2 driver has just one control handler that is global for
|
|
|
+all video nodes. But you can also specify different control handlers for
|
|
|
+different video nodes. You can do that by manually setting the ctrl_handler
|
|
|
+field of struct video_device.
|
|
|
+
|
|
|
+That is no problem if there are no subdevs involved but if there are, then
|
|
|
+you need to block the automatic merging of subdev controls to the global
|
|
|
+control handler. You do that by simply setting the ctrl_handler field in
|
|
|
+struct v4l2_device to NULL. Now v4l2_device_register_subdev() will no longer
|
|
|
+merge subdev controls.
|
|
|
+
|
|
|
+After each subdev was added, you will then have to call v4l2_ctrl_add_handler
|
|
|
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
|
|
|
+control handler. This control handler may be specific to the video_device or
|
|
|
+for a subset of video_device's. For example: the radio device nodes only have
|
|
|
+audio controls, while the video and vbi device nodes share the same control
|
|
|
+handler for the audio and video controls.
|
|
|
+
|
|
|
+If you want to have one handler (e.g. for a radio device node) have a subset
|
|
|
+of another handler (e.g. for a video device node), then you should first add
|
|
|
+the controls to the first handler, add the other controls to the second
|
|
|
+handler and finally add the first handler to the second. For example:
|
|
|
+
|
|
|
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
|
|
|
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
|
|
|
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
|
|
|
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
|
|
|
+ v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
|
|
|
+
|
|
|
+Or you can add specific controls to a handler:
|
|
|
+
|
|
|
+ volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
|
|
|
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
|
|
|
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
|
|
|
+ v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
|
|
|
+
|
|
|
+What you should not do is make two identical controls for two handlers.
|
|
|
+For example:
|
|
|
+
|
|
|
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
|
|
|
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
|
|
|
+
|
|
|
+This would be bad since muting the radio would not change the video mute
|
|
|
+control. The rule is to have one control for each hardware 'knob' that you
|
|
|
+can twiddle.
|
|
|
+
|
|
|
+
|
|
|
+Finding Controls
|
|
|
+================
|
|
|
+
|
|
|
+Normally you have created the controls yourself and you can store the struct
|
|
|
+v4l2_ctrl pointer into your own struct.
|
|
|
+
|
|
|
+But sometimes you need to find a control from another handler that you do
|
|
|
+not own. For example, if you have to find a volume control from a subdev.
|
|
|
+
|
|
|
+You can do that by calling v4l2_ctrl_find:
|
|
|
+
|
|
|
+ struct v4l2_ctrl *volume;
|
|
|
+
|
|
|
+ volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
|
|
|
+
|
|
|
+Since v4l2_ctrl_find will lock the handler you have to be careful where you
|
|
|
+use it. For example, this is not a good idea:
|
|
|
+
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+
|
|
|
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
|
|
|
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
|
|
|
+
|
|
|
+...and in video_ops.s_ctrl:
|
|
|
+
|
|
|
+ case V4L2_CID_BRIGHTNESS:
|
|
|
+ contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
|
|
|
+ ...
|
|
|
+
|
|
|
+When s_ctrl is called by the framework the ctrl_handler.lock is already taken, so
|
|
|
+attempting to find another control from the same handler will deadlock.
|
|
|
+
|
|
|
+It is recommended not to use this function from inside the control ops.
|
|
|
+
|
|
|
+
|
|
|
+Inheriting Controls
|
|
|
+===================
|
|
|
+
|
|
|
+When one control handler is added to another using v4l2_ctrl_add_handler, then
|
|
|
+by default all controls from one are merged to the other. But a subdev might
|
|
|
+have low-level controls that make sense for some advanced embedded system, but
|
|
|
+not when it is used in consumer-level hardware. In that case you want to keep
|
|
|
+those low-level controls local to the subdev. You can do this by simply
|
|
|
+setting the 'is_private' flag of the control to 1:
|
|
|
+
|
|
|
+ static const struct v4l2_ctrl_config ctrl_private = {
|
|
|
+ .ops = &ctrl_custom_ops,
|
|
|
+ .id = V4L2_CID_...,
|
|
|
+ .name = "Some Private Control",
|
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
+ .max = 15,
|
|
|
+ .step = 1,
|
|
|
+ .is_private = 1,
|
|
|
+ };
|
|
|
+
|
|
|
+ ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);
|
|
|
+
|
|
|
+These controls will now be skipped when v4l2_ctrl_add_handler is called.
|
|
|
+
|
|
|
+
|
|
|
+V4L2_CTRL_TYPE_CTRL_CLASS Controls
|
|
|
+==================================
|
|
|
+
|
|
|
+Controls of this type can be used by GUIs to get the name of the control class.
|
|
|
+A fully featured GUI can make a dialog with multiple tabs with each tab
|
|
|
+containing the controls belonging to a particular control class. The name of
|
|
|
+each tab can be found by querying a special control with ID <control class | 1>.
|
|
|
+
|
|
|
+Drivers do not have to care about this. The framework will automatically add
|
|
|
+a control of this type whenever the first control belonging to a new control
|
|
|
+class is added.
|
|
|
+
|
|
|
+
|
|
|
+Differences from the Spec
|
|
|
+=========================
|
|
|
+
|
|
|
+There are a few places where the framework acts slightly differently from the
|
|
|
+V4L2 Specification. Those differences are described in this section. We will
|
|
|
+have to see whether we need to adjust the spec or not.
|
|
|
+
|
|
|
+1) It is no longer required to have all controls contained in a
|
|
|
+v4l2_ext_control array be from the same control class. The framework will be
|
|
|
+able to handle any type of control in the array. You need to set ctrl_class
|
|
|
+to 0 in order to enable this. If ctrl_class is non-zero, then it will still
|
|
|
+check that all controls belong to that control class.
|
|
|
+
|
|
|
+If you set ctrl_class to 0 and count to 0, then it will only return an error
|
|
|
+if there are no controls at all.
|
|
|
+
|
|
|
+2) Clarified the way error_idx works. For get and set it will be equal to
|
|
|
+count if nothing was done yet. If it is less than count then only the controls
|
|
|
+up to error_idx-1 were successfully applied.
|
|
|
+
|
|
|
+3) When attempting to read a button control the framework will return -EACCES
|
|
|
+instead of -EINVAL as stated in the spec. It seems to make more sense since
|
|
|
+button controls are write-only controls.
|
|
|
+
|
|
|
+4) Attempting to write to a read-only control will return -EACCES instead of
|
|
|
+-EINVAL as the spec says.
|
|
|
+
|
|
|
+5) The spec does not mention what should happen when you try to set/get a
|
|
|
+control class controls. ivtv currently returns -EINVAL (indicating that the
|
|
|
+control ID does not exist) while the framework will return -EACCES, which
|
|
|
+makes more sense.
|
|
|
+
|
|
|
+
|
|
|
+Proposals for Extensions
|
|
|
+========================
|
|
|
+
|
|
|
+Some ideas for future extensions to the spec:
|
|
|
+
|
|
|
+1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of
|
|
|
+decimal. Useful for e.g. video_mute_yuv.
|
|
|
+
|
|
|
+2) It is possible to mark in the controls array which controls have been
|
|
|
+successfully written and which failed by for example adding a bit to the
|
|
|
+control ID. Not sure if it is worth the effort, though.
|
|
|
+
|
|
|
+3) Trying to set volatile inactive controls should result in -EACCESS.
|
|
|
+
|
|
|
+4) Add a new flag to mark volatile controls. Any application that wants
|
|
|
+to store the state of the controls can then skip volatile inactive controls.
|
|
|
+Currently it is not possible to detect such controls.
|