return -ENOMEM;
}
v4l2_fh_init(&item->fh, s->vdev);
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV ||
- s->type == IVTV_DEC_STREAM_TYPE_MPG) {
- res = v4l2_event_alloc(&item->fh, 60);
- }
if (res < 0) {
v4l2_fh_exit(&item->fh);
kfree(item);
switch (sub->type) {
case V4L2_EVENT_VSYNC:
case V4L2_EVENT_EOS:
- break;
case V4L2_EVENT_CTRL:
- return v4l2_ctrl_subscribe_fh(fh, sub, 0);
+ return v4l2_event_subscribe(fh, sub, 0);
default:
return -EINVAL;
}
- return v4l2_event_subscribe(fh, sub);
}
static int ivtv_log_status(struct file *file, void *fh)
if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub);
+ return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
}
static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
sd->grp_id = 1 << 16; /* group ID for isp subdevs */
v4l2_set_subdevdata(sd, ccdc);
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- sd->nevents = OMAP3ISP_CCDC_NEVENTS;
pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- subdev->nevents = STAT_NEVENTS;
v4l2_set_subdevdata(subdev, stat);
stat->pad.flags = MEDIA_PAD_FL_SINK;
if (sub->type != stat->event_type)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub);
+ return v4l2_event_subscribe(fh, sub, STAT_NEVENTS);
}
int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
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);
- hdl->nr_of_refs++;
goto insert_in_hash;
}
v4l2_ctrl_unlock(ctrl);
}
EXPORT_SYMBOL(v4l2_ctrl_del_event);
-
-int v4l2_ctrl_subscribe_fh(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub, unsigned n)
-{
- struct v4l2_ctrl_handler *hdl = fh->ctrl_handler;
- int ret = 0;
-
- if (!ret) {
- if (hdl->nr_of_refs * 2 > n)
- n = hdl->nr_of_refs * 2;
- ret = v4l2_event_alloc(fh, n);
- }
- if (!ret)
- ret = v4l2_event_subscribe(fh, sub);
- return ret;
-}
-EXPORT_SYMBOL(v4l2_ctrl_subscribe_fh);
#include <linux/sched.h>
#include <linux/slab.h>
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
-
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
-{
- unsigned long flags;
-
- while (fh->nallocated < n) {
- struct v4l2_kevent *kev;
-
- kev = kzalloc(sizeof(*kev), GFP_KERNEL);
- if (kev == NULL)
- return -ENOMEM;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- list_add_tail(&kev->list, &fh->free);
- fh->nallocated++;
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_alloc);
-
-#define list_kfree(list, type, member) \
- while (!list_empty(list)) { \
- type *hi; \
- hi = list_first_entry(list, type, member); \
- list_del(&hi->member); \
- kfree(hi); \
- }
-
-void v4l2_event_free(struct v4l2_fh *fh)
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
{
- list_kfree(&fh->free, struct v4l2_kevent, list);
- list_kfree(&fh->available, struct v4l2_kevent, list);
- v4l2_event_unsubscribe_all(fh);
+ idx += sev->first;
+ return idx >= sev->elems ? idx - sev->elems : idx;
}
-EXPORT_SYMBOL_GPL(v4l2_event_free);
static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
{
WARN_ON(fh->navailable == 0);
kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
- list_move(&kev->list, &fh->free);
+ list_del(&kev->list);
fh->navailable--;
kev->event.pending = fh->navailable;
*event = kev->event;
+ kev->sev->first = sev_pos(kev->sev, 1);
+ kev->sev->in_use--;
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
fh->sequence++;
/* Do we have any free events? */
- if (list_empty(&fh->free))
- return;
+ if (sev->in_use == sev->elems) {
+ /* no, remove the oldest one */
+ kev = sev->events + sev_pos(sev, 0);
+ list_del(&kev->list);
+ sev->in_use--;
+ sev->first = sev_pos(sev, 1);
+ fh->navailable--;
+ }
/* Take one and fill it. */
- kev = list_first_entry(&fh->free, struct v4l2_kevent, list);
+ kev = sev->events + sev_pos(sev, sev->in_use);
kev->event.type = ev->type;
kev->event.u = ev->u;
kev->event.id = ev->id;
kev->event.timestamp = *ts;
kev->event.sequence = fh->sequence;
- list_move_tail(&kev->list, &fh->available);
+ sev->in_use++;
+ list_add_tail(&kev->list, &fh->available);
fh->navailable++;
EXPORT_SYMBOL_GPL(v4l2_event_pending);
int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
+ struct v4l2_event_subscription *sub, unsigned elems)
{
struct v4l2_subscribed_event *sev, *found_ev;
struct v4l2_ctrl *ctrl = NULL;
unsigned long flags;
+ unsigned i;
+ if (elems < 1)
+ elems = 1;
if (sub->type == V4L2_EVENT_CTRL) {
ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
if (ctrl == NULL)
return -EINVAL;
}
- sev = kzalloc(sizeof(*sev), GFP_KERNEL);
+ sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
if (!sev)
return -ENOMEM;
+ for (i = 0; i < elems; i++)
+ sev->events[i].sev = sev;
+ sev->type = sub->type;
+ sev->id = sub->id;
+ sev->flags = sub->flags;
+ sev->fh = fh;
+ sev->elems = elems;
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
- if (!found_ev) {
- INIT_LIST_HEAD(&sev->list);
- sev->type = sub->type;
- sev->id = sub->id;
- sev->fh = fh;
- sev->flags = sub->flags;
-
+ if (!found_ev)
list_add(&sev->list, &fh->subscribed);
- }
-
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- /* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
+ /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
if (found_ev)
kfree(sev);
else if (ctrl)
}
EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
{
struct v4l2_event_subscription sub;
struct v4l2_subscribed_event *sev;
v4l2_event_unsubscribe(fh, &sub);
} while (sev);
}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
INIT_LIST_HEAD(&fh->list);
set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
fh->prio = V4L2_PRIORITY_UNSET;
-
init_waitqueue_head(&fh->wait);
- INIT_LIST_HEAD(&fh->free);
INIT_LIST_HEAD(&fh->available);
INIT_LIST_HEAD(&fh->subscribed);
fh->sequence = -1;
{
if (fh->vdev == NULL)
return;
- v4l2_event_free(fh);
+ v4l2_event_unsubscribe_all(fh);
fh->vdev = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_fh_exit);
}
v4l2_fh_init(&subdev_fh->vfh, vdev);
-
- if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) {
- ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents);
- if (ret)
- goto err;
- }
-
v4l2_fh_add(&subdev_fh->vfh);
file->private_data = &subdev_fh->vfh;
#if defined(CONFIG_MEDIA_CONTROLLER)
{
switch (sub->type) {
case V4L2_EVENT_CTRL:
- return v4l2_ctrl_subscribe_fh(fh, sub, 0);
+ return v4l2_event_subscribe(fh, sub, 0);
default:
return -EINVAL;
}
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_file_handle *handle;
- int ret;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (handle == NULL)
return -ENOMEM;
v4l2_fh_init(&handle->vfh, vdev);
-
- ret = v4l2_event_alloc(&handle->vfh, 8);
- if (ret < 0)
- goto error;
-
v4l2_fh_add(&handle->vfh);
handle->device = &uvc->video;
uvc_function_connect(uvc);
return 0;
-
-error:
- v4l2_fh_exit(&handle->vfh);
- return ret;
}
static int
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
- return v4l2_event_subscribe(&handle->vfh, arg);
+ return v4l2_event_subscribe(&handle->vfh, arg, 2);
}
case VIDIOC_UNSUBSCRIBE_EVENT:
* control is needed multiple times, so this is a simple
* optimization.
* @buckets: Buckets for the hashing. Allows for quick control lookup.
- * @nr_of_refs: Total number of control references in the list.
* @nr_of_buckets: Total number of buckets in the array.
* @error: The error code of the first failed control addition.
*/
struct list_head ctrl_refs;
struct v4l2_ctrl_ref *cached;
struct v4l2_ctrl_ref **buckets;
- u16 nr_of_refs;
u16 nr_of_buckets;
int error;
};
void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
struct v4l2_subscribed_event *sev);
-/** v4l2_ctrl_subscribe_fh() - Helper function that subscribes a control event.
- * @fh: The file handler that subscribed the control event.
- * @sub: The event to subscribe (type must be V4L2_EVENT_CTRL).
- * @n: How many events should be allocated? (Passed to v4l2_event_alloc).
- * Recommended to set to twice the number of controls plus whatever
- * is needed for other events. This function will set n to
- * max(n, 2 * fh->ctrl_handler->nr_of_refs).
- *
- * A helper function that initializes the fh for events, allocates the
- * list of events and subscribes the control event.
- *
- * Typically called in the handler of VIDIOC_SUBSCRIBE_EVENT in the
- * V4L2_EVENT_CTRL case.
- */
-int v4l2_ctrl_subscribe_fh(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub, unsigned n);
-
/* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
#include <linux/wait.h>
struct v4l2_fh;
+struct v4l2_subscribed_event;
struct video_device;
struct v4l2_kevent {
+ /* list node for the v4l2_fh->available list */
struct list_head list;
+ /* pointer to parent v4l2_subscribed_event */
+ struct v4l2_subscribed_event *sev;
+ /* event itself */
struct v4l2_event event;
};
struct v4l2_fh *fh;
/* list node that hooks into the object's event list (if there is one) */
struct list_head node;
+ /* the number of elements in the events array */
+ unsigned elems;
+ /* the index of the events containing the oldest available event */
+ unsigned first;
+ /* the number of queued events */
+ unsigned in_use;
+ /* an array of elems events */
+ struct v4l2_kevent events[];
};
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);
-void v4l2_event_free(struct v4l2_fh *fh);
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
int nonblocking);
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev);
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev);
int v4l2_event_pending(struct v4l2_fh *fh);
int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
+ struct v4l2_event_subscription *sub, unsigned elems);
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub);
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
#endif /* V4L2_EVENT_H */
/* Events */
wait_queue_head_t wait;
struct list_head subscribed; /* Subscribed events */
- struct list_head free; /* Events ready for use */
struct list_head available; /* Dequeueable event */
unsigned int navailable;
- unsigned int nallocated; /* Number of allocated events */
u32 sequence;
};
void *host_priv;
/* subdev device node */
struct video_device devnode;
- /* number of events to be allocated on open */
- unsigned int nevents;
};
#define media_entity_to_v4l2_subdev(ent) \