-/* $OpenBSD: video.c,v 1.48 2021/01/31 19:32:01 mglocker Exp $ */
+/* $OpenBSD: video.c,v 1.49 2021/02/16 13:50:46 mglocker Exp $ */
/*
* Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
struct device *sc_dev; /* hardware device struct */
struct video_hw_if *hw_if; /* hardware interface */
char sc_dying; /* device detached */
- struct process *sc_owner; /* owner process */
+ struct process *sc_owner; /* owner process */
+ uint8_t sc_open; /* device opened */
int sc_fsize;
uint8_t *sc_fbuffer;
int videoprint(void *, const char *);
void video_intr(void *);
+int video_stop(struct video_softc *);
+int video_claim(struct video_softc *, struct process *);
struct cfattach video_ca = {
sizeof(struct video_softc), videoprobe, videoattach,
{
int unit;
struct video_softc *sc;
+ int error = 0;
+
+ KERNEL_ASSERT_LOCKED();
unit = VIDEOUNIT(dev);
if (unit >= video_cd.cd_ndevs ||
sc->hw_if == NULL)
return (ENXIO);
- if (sc->sc_owner != NULL) {
- if (sc->sc_owner == p->p_p)
- return (0);
- else
- return (EBUSY);
- } else
- sc->sc_owner = p->p_p;
+ if (sc->sc_open) {
+ DPRINTF(("%s: device already open\n", __func__));
+ return (0);
+ } else {
+ sc->sc_open = 1;
+ DPRINTF(("%s: set device to open\n", __func__));
+ }
sc->sc_vidmode = VIDMODE_NONE;
sc->sc_frames_ready = 0;
if (sc->hw_if->open != NULL)
- return (sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
- sc->sc_fbuffer, video_intr, sc));
- else
- return (0);
+ error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
+ sc->sc_fbuffer, video_intr, sc);
+
+ return (error);
}
int
videoclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct video_softc *sc;
- int r = 0;
+ int error = 0;
- sc = video_cd.cd_devs[VIDEOUNIT(dev)];
+ KERNEL_ASSERT_LOCKED();
- if (sc->hw_if->close != NULL)
- r = sc->hw_if->close(sc->hw_hdl);
+ DPRINTF(("%s: last close\n", __func__));
- sc->sc_owner = NULL;
+ sc = video_cd.cd_devs[VIDEOUNIT(dev)];
+
+ error = video_stop(sc);
+ sc->sc_open = 0;
- return (r);
+ return (error);
}
int
int unit, error;
size_t size;
+ KERNEL_ASSERT_LOCKED();
+
unit = VIDEOUNIT(dev);
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
if (sc->sc_vidmode == VIDMODE_MMAP)
return (EBUSY);
+ if ((error = video_claim(sc, curproc->p_p)))
+ return (error);
+
/* start the stream if not already started */
if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
error = sc->hw_if->start_read(sc->hw_hdl);
struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
int unit, error;
+ KERNEL_ASSERT_LOCKED();
+
unit = VIDEOUNIT(dev);
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
error = EOPNOTSUPP;
switch (cmd) {
+ case VIDIOC_G_CTRL:
+ if (sc->hw_if->g_ctrl)
+ error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
+ (struct v4l2_control *)data);
+ break;
+ case VIDIOC_S_CTRL:
+ if (sc->hw_if->s_ctrl)
+ error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
+ (struct v4l2_control *)data);
+ break;
+ default:
+ error = (ENOTTY);
+ }
+ if (error != ENOTTY)
+ return (error);
+
+ if ((error = video_claim(sc, p->p_p)))
+ return (error);
+
+ /*
+ * The following IOCTLs can only be called by the device owner.
+ * For further shared IOCTLs please move it up.
+ */
+ error = EOPNOTSUPP;
+ switch (cmd) {
case VIDIOC_QUERYCAP:
if (sc->hw_if->querycap)
error = (sc->hw_if->querycap)(sc->hw_hdl,
if (sc->hw_if->streamoff)
error = (sc->hw_if->streamoff)(sc->hw_hdl,
(int)*data);
+ if (!error) {
+ /* Release device ownership and streaming buffers. */
+ video_stop(sc);
+ }
break;
case VIDIOC_TRY_FMT:
if (sc->hw_if->try_fmt)
error = (sc->hw_if->queryctrl)(sc->hw_hdl,
(struct v4l2_queryctrl *)data);
break;
- case VIDIOC_G_CTRL:
- if (sc->hw_if->g_ctrl)
- error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
- (struct v4l2_control *)data);
- break;
- case VIDIOC_S_CTRL:
- if (sc->hw_if->s_ctrl)
- error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
- (struct v4l2_control *)data);
- break;
default:
error = (ENOTTY);
}
struct video_softc *sc;
int error, revents = 0;
+ KERNEL_ASSERT_LOCKED();
+
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
return (POLLERR);
if (sc->sc_dying)
return (POLLERR);
+ if ((error = video_claim(sc, p->p_p)))
+ return (error);
+
DPRINTF(("%s: events=0x%x\n", __func__, events));
if (events & (POLLIN | POLLRDNORM)) {
caddr_t p;
paddr_t pa;
+ KERNEL_ASSERT_LOCKED();
+
DPRINTF(("%s: off=%lld, prot=%d\n", __func__, off, prot));
unit = VIDEOUNIT(dev);
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
- int s;
+ int s, error;
+
+ KERNEL_ASSERT_LOCKED();
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
return (EINVAL);
}
+ if ((error = video_claim(sc, curproc->p_p)))
+ return (error);
+
/*
* Start the stream in read() mode if not already started. If
* the user wanted mmap() mode, he should have called mmap()
selwakeup(&sc->sc_rsel);
}
+int
+video_stop(struct video_softc *sc)
+{
+ int error = 0;
+
+ DPRINTF(("%s: stream close\n", __func__));
+
+ if (sc->hw_if->close != NULL)
+ error = sc->hw_if->close(sc->hw_hdl);
+
+ sc->sc_vidmode = VIDMODE_NONE;
+ sc->sc_frames_ready = 0;
+ sc->sc_owner = NULL;
+
+ return (error);
+}
+
+int
+video_claim(struct video_softc *sc, struct process *p)
+{
+ if (sc->sc_owner != NULL && sc->sc_owner != p) {
+ DPRINTF(("%s: already owned=%p\n", __func__, sc->sc_owner));
+ return (EBUSY);
+ }
+
+ if (sc->sc_owner == NULL) {
+ sc->sc_owner = p;
+ DPRINTF(("%s: new owner=%p\n", __func__, sc->sc_owner));
+ }
+
+ return (0);
+}
+
int
videoprint(void *aux, const char *pnp)
{