/* Note: these utility functions all assume that vfl_type is in the range
[0, VFL_TYPE_MAX-1]. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Return the bitmap corresponding to vfl_type. */ staticinlineunsignedlong *devnode_bits(enum vfl_devnode_type vfl_type)
{ /* Any types not assigned to fixed minor ranges must be mapped to onesinglebitmapforthepurposesoffindingafreenodenumber
since all those unassigned types use the same minor range. */ int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
/* Mark device node number vdev->num as used */ staticinlinevoid devnode_set(struct video_device *vdev)
{
set_bit(vdev->num, devnode_bits(vdev->vfl_type));
}
/* Mark device node number vdev->num as unused */ staticinlinevoid devnode_clear(struct video_device *vdev)
{
clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
}
/* Try to find a free device node number in the range [from, to> */ staticinlineint devnode_find(struct video_device *vdev, int from, int to)
{ return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
}
void video_device_release_empty(struct video_device *vdev)
{ /* Do nothing */ /* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL(video_device_release_empty);
/* Called when the last user of the video device exits. */ staticvoid v4l2_device_release(struct device *cd)
{ struct video_device *vdev = to_video_device(cd); struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
mutex_lock(&videodev_lock); if (WARN_ON(video_devices[vdev->minor] != vdev)) { /* should not happen */
mutex_unlock(&videodev_lock); return;
}
/* Free up this device for reuse */
video_devices[vdev->minor] = NULL;
/* Delete the cdev on this minor as well */
cdev_del(vdev->cdev); /* Just in case some driver tries to access this from
the release() callback. */
vdev->cdev = NULL;
/* Mark device node number as free */
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
#ifdefined(CONFIG_MEDIA_CONTROLLER) if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) { /* Remove interfaces and interface links */
media_devnode_remove(vdev->intf_devnode); if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
media_device_unregister_entity(&vdev->entity);
} #endif
/* Do not call v4l2_device_put if there is no release callback set. *Driversthathavenov4l2_devicereleasecallbackmightfreethe *v4l2_devinstanceinthevideo_devicereleasecallbackbelow,sowe *mustperformthischeckhere. * *TODO:Inthelongrunalldriversthatusev4l2_deviceshouldusethe *v4l2_devicereleasecallback.Thischeckwillthenbeunnecessary.
*/ if (v4l2_dev->release == NULL)
v4l2_dev = NULL;
/* Release video_device and perform other
cleanups as needed. */
vdev->release(vdev);
/* Decrease v4l2_device refcount */ if (v4l2_dev)
v4l2_device_put(v4l2_dev);
}
if (!vdev->fops->get_unmapped_area) return -ENOSYS; if (!video_is_registered(vdev)) return -ENODEV;
ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: get_unmapped_area (%d)\n",
video_device_node_name(vdev), ret); return ret;
} #endif
staticint v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{ struct video_device *vdev = video_devdata(filp); int ret = -ENODEV;
if (!vdev->fops->mmap) return -ENODEV; if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm); if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: mmap (%d)\n",
video_device_node_name(vdev), ret); return ret;
}
/* Override for the open function */ staticint v4l2_open(struct inode *inode, struct file *filp)
{ struct video_device *vdev; int ret = 0;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
vdev = video_devdata(filp); /* return ENODEV if the video device has already been removed. */ if (vdev == NULL || !video_is_registered(vdev)) {
mutex_unlock(&videodev_lock); return -ENODEV;
} /* and increase the device refcount */
video_get(vdev);
mutex_unlock(&videodev_lock); if (vdev->fops->open) { if (video_is_registered(vdev))
ret = vdev->fops->open(filp); else
ret = -ENODEV;
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: open (%d)\n",
video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ if (ret)
video_put(vdev); return ret;
}
/* Override for the release function */ staticint v4l2_release(struct inode *inode, struct file *filp)
{ struct video_device *vdev = video_devdata(filp); int ret = 0;
/* *Weneedtoserializetherelease()withqueueingnewrequests. *Therelease()maytriggerthecancellationofastreaming *operation,andthatshouldnotbemixedwithqueueinganew *requestatthesametime.
*/ if (vdev->fops->release) { if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
ret = vdev->fops->release(filp);
mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
} else {
ret = vdev->fops->release(filp);
}
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: release\n",
video_device_node_name(vdev));
/* decrease the refcount unconditionally since the release()
return value is ignored. */
video_put(vdev); return ret;
}
/** *get_index-assignstreamindexnumberbasedonv4l2_dev *@vdev:video_devicetoassignindexnumberto,vdev->v4l2_devshouldbeassigned * *Notethatwhenthisiscalledthenewdevicehasnotyetbeenregistered *inthevideo_devicearray,butitwasabletoobtainaminornumber. * *Thismeansthatwecanalwaysobtainafreestreamindexnumbersince *theworstcasescenarioisthatthereareVIDEO_NUM_DEVICES-1slotsin *useofthevideo_devicearray. * *Returnsafreeindexnumber.
*/ staticint get_index(struct video_device *vdev)
{ /* This can be static since this function is called with the global
videodev_lock held. */ static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES); int i;
bitmap_zero(used, VIDEO_NUM_DEVICES);
for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_devices[i] != NULL &&
video_devices[i]->v4l2_dev == vdev->v4l2_dev) {
__set_bit(video_devices[i]->index, used);
}
}
staticint video_register_media_controller(struct video_device *vdev)
{ #ifdefined(CONFIG_MEDIA_CONTROLLER)
u32 intf_type; int ret;
/* Memory-to-memory devices are more complex and use *theirownfunctiontoregisteritsmcentities.
*/ if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) return0;
if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) { struct media_link *link;
link = media_create_intf_link(&vdev->entity,
&vdev->intf_devnode->intf,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE); if (!link) {
media_devnode_remove(vdev->intf_devnode);
media_device_unregister_entity(&vdev->entity); return -ENOMEM;
}
}
/* FIXME: how to create the other interface links? */
#endif return0;
}
int __video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr, int warn_if_nr_in_use, struct module *owner)
{ int i = 0; int ret; int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; constchar *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */ if (WARN_ON(!vdev->release)) return -EINVAL; /* the v4l2_dev pointer MUST be present */ if (WARN_ON(!vdev->v4l2_dev)) return -EINVAL; /* the device_caps field MUST be set for all but subdevs */ if (WARN_ON(type != VFL_TYPE_SUBDEV && !vdev->device_caps)) return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */ switch (type) { case VFL_TYPE_VIDEO:
name_base = "video"; break; case VFL_TYPE_VBI:
name_base = "vbi"; break; case VFL_TYPE_RADIO:
name_base = "radio"; break; case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev"; break; case VFL_TYPE_SDR: /* Use device name 'swradio' because 'sdr' was already taken. */
name_base = "swradio"; break; case VFL_TYPE_TOUCH:
name_base = "v4l-touch"; break; default:
pr_err("%s called with unknown type: %d\n",
__func__, type); return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL; if (vdev->dev_parent == NULL)
vdev->dev_parent = vdev->v4l2_dev->dev; if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; /* If the prio state pointer is NULL, then use the v4l2_device
prio state. */ if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
/* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical *reasons. *Newerdevices(notyetinplace)shouldusetherange *of128-191andjustpickthefirstfreeminorthere
* (new style). */ switch (type) { case VFL_TYPE_VIDEO:
minor_offset = 0;
minor_cnt = 64; break; case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64; break; case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32; break; default:
minor_offset = 128;
minor_cnt = 64; break;
} #endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) {
pr_err("could not get a free device node number\n");
mutex_unlock(&videodev_lock); return -ENFILE;
} #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* 1-on-1 mapping of device node number to minor number */
i = nr; #else /* The device node number and minor numbers are independent, so
we just find the first free minor number. */ for (i = 0; i < VIDEO_NUM_DEVICES; i++) if (video_devices[i] == NULL) break; if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
pr_err("could not get a free minor\n"); return -ENFILE;
} #endif
vdev->minor = i + minor_offset;
vdev->num = nr;
/* Should not happen since we thought this minor was free */ if (WARN_ON(video_devices[vdev->minor])) {
mutex_unlock(&videodev_lock);
pr_err("video_device not empty!\n"); return -ENFILE;
}
devnode_set(vdev);
vdev->index = get_index(vdev);
video_devices[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
if (vdev->ioctl_ops)
determine_valid_ioctls(vdev);
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc(); if (vdev->cdev == NULL) {
ret = -ENOMEM; goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) {
pr_err("%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL; goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
vdev->dev.release = v4l2_device_release;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
mutex_lock(&videodev_lock);
ret = device_register(&vdev->dev); if (ret < 0) {
mutex_unlock(&videodev_lock);
pr_err("%s: device_register failed\n", __func__);
put_device(&vdev->dev); return ret;
}
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
pr_warn("%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Part 5: Register the entity. */
ret = video_register_media_controller(vdev);
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock);
return0;
cleanup:
mutex_lock(&videodev_lock); if (vdev->cdev)
cdev_del(vdev->cdev);
video_devices[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */
vdev->minor = -1; return ret;
}
EXPORT_SYMBOL(__video_register_device);
/** *video_unregister_device-unregisteravideo4linuxdevice *@vdev:thedevicetounregister * *Thisunregistersthepasseddevice.Futureopencallswill *bemetwitherrors.
*/ void video_unregister_device(struct video_device *vdev)
{ /* Check if vdev was ever registered at all */ if (!vdev || !video_is_registered(vdev)) return;
mutex_lock(&videodev_lock); /* This must be in a critical section to prevent a race with v4l2_open. *Oncethisbithasbeenclearedvideo_getmayneverbecalledagain.
*/
clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock); if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
v4l2_event_wake_all(vdev);
device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
/* *Initialisevideoforlinux
*/ staticint __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0); int ret;
pr_info("Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); if (ret < 0) {
pr_warn("videodev: unable to get major %d\n",
VIDEO_MAJOR); return ret;
}
ret = class_register(&video_class); if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
pr_warn("video_dev: class_register failed\n"); return -EIO;
}
return0;
}
staticvoid __exit videodev_exit(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
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.