diff -u orig/saa7134-core.c ./saa7134-core.c --- orig/saa7134-core.c Mon May 5 18:26:17 2003 +++ ./saa7134-core.c Sun Jun 15 12:04:05 2003 @@ -877,6 +877,7 @@ /* everything worked */ list_add_tail(&dev->devlist,&saa7134_devlist); pci_set_drvdata(pci_dev,dev); + init_waitqueue_head(&dev->chn_ev_q); saa7134_devcount++; return 0; diff -u orig/saa7134-video.c ./saa7134-video.c --- orig/saa7134-video.c Wed May 14 12:26:29 2003 +++ ./saa7134-video.c Sun Jun 15 12:53:43 2003 @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/wait.h> #include "saa7134-reg.h" #include "saa7134.h" @@ -416,6 +417,151 @@ up(&dev->lock); } +/* ----------------------------------------------------------------------- */ +/* channel event notification */ + +static +int wait_chn_event(struct saa7134_dev *dev, struct saa7134_fh *fh, int non_blocking) +{ + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->chn_ev_q, &wait); + while ( (memcmp(dev->chn_ev_idx, fh->chn_ev_idx, sizeof(dev->chn_ev_idx)) == 0) && + (fh->chn_ev_poll != 0) ) { + + if (non_blocking) { + retval = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + dprintk("S_CHNEVENT waiton(NONBL %d): -EINTR\n", non_blocking); + retval = -EINTR; + break; + } + } + remove_wait_queue(&dev->chn_ev_q, &wait); + + return retval; +} + +static +void post_chn_event(struct saa7134_dev *dev, enum v4l2_chn_event_bit ev) +{ + /* note: dev mutex must be locked by the caller */ + + if (ev < V4L2_CHN_EV_COUNT) { + dev->chn_ev_idx[ev]++; + + wake_up(&dev->chn_ev_q); + } +} + +static +v4l2_chn_event_mask get_chn_events(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + v4l2_chn_event_mask ev_mask; + int i, bit; + + ev_mask = 0; + + down(&dev->lock); + for (i=0, bit=1; i < V4L2_CHN_EV_COUNT; i++, bit<<=1) { + if (fh->chn_ev_idx[i] != dev->chn_ev_idx[i]) { + fh->chn_ev_idx[i] = dev->chn_ev_idx[i]; + ev_mask |= bit; + } + } + up(&dev->lock); + + return ev_mask; +} + +/* ----------------------------------------------------------------------- */ +/* user priority management */ + +static +int check_alloc_prio(struct saa7134_fh *fh, int req_prio) +{ + struct saa7134_dev *dev = fh->dev; + + down(&dev->lock); + if ( (req_prio == V4L2_CHN_PRIO_EXCL) && (dev->user_prios[V4L2_CHN_PRIO_EXCL] != 0) ) { + /* only one client is allowed at exclusive level */ + up(&dev->lock); + return 0; + } + + if (fh->user_prio < V4L2_CHN_PRIO_COUNT) { + /* deregister the previous priority */ + if (dev->user_prios[fh->user_prio] != 0) { + dev->user_prios[fh->user_prio] -= 1; + } else { + printk("saa7134: BUG! (alloc_prio)\n"); + } + } + + /* register the new priority */ + dev->user_prios[req_prio] += 1; + fh->user_prio = req_prio; + + post_chn_event(dev, V4L2_CHN_EV_PRIO); + + up(&dev->lock); + return 1; +} + +static +void release_prio(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + down(&dev->lock); + if (dev->user_prios[fh->user_prio] != 0) { + dev->user_prios[fh->user_prio] -= 1; + } else { + printk("saa7134: BUG! (release_prio)\n"); + } + fh->user_prio = 0; + + post_chn_event(dev, V4L2_CHN_EV_PRIO); + + up(&dev->lock); +} + +static +int get_max_user_prio(struct saa7134_dev *dev) +{ + int idx; + + for (idx = V4L2_CHN_PRIO_COUNT - 1; idx > 0; idx--) + if (dev->user_prios[idx] > 0) + return idx; + + return 0; +} + +static +int check_prio(struct saa7134_dev *dev, struct semaphore * p_lock, + int user_prio, enum v4l2_chn_event_bit ev) +{ + int idx; + + /* note: mutex must be locked when this function is called and + ** will be unlocked if the function returns FALSE */ + + for (idx = user_prio + 1; idx < V4L2_CHN_PRIO_COUNT; idx++) { + if (dev->user_prios[idx] > 0) { + up(p_lock); + return 0; + } + } + + post_chn_event(dev, ev); + + return 1; +} + /* ------------------------------------------------------------------ */ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) @@ -1170,6 +1316,10 @@ fh->width = 768; fh->height = 576; + fh->chn_ev_poll = 0; + fh->user_prio = V4L2_CHN_PRIO_COUNT; + check_alloc_prio(fh, V4L2_CHN_PRIO_DEFAULT); + videobuf_queue_init(&fh->cap, &video_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, @@ -1205,6 +1355,9 @@ { struct saa7134_fh *fh = file->private_data; + if (fh->chn_ev_poll) + return -EBUSY; + switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (res_locked(fh->dev,RESOURCE_VIDEO)) @@ -1229,6 +1382,14 @@ struct saa7134_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; + if (fh->chn_ev_poll) { + poll_wait(file, &fh->dev->chn_ev_q, wait); + if (memcmp(fh->dev->chn_ev_idx, fh->chn_ev_idx, + sizeof(fh->chn_ev_idx)) != 0) + return POLLIN|POLLRDNORM; + return 0; + } + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) return videobuf_poll_stream(file, &fh->vbi, wait); @@ -1297,6 +1458,9 @@ res_free(dev,fh,RESOURCE_VBI); } + /* release priority */ + release_prio(dev, fh); + saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi); @@ -1550,6 +1714,8 @@ return -EINVAL; down(&dev->lock); + if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_STD)) + return -EBUSY; if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); @@ -1610,6 +1776,8 @@ if (NULL == card_in(dev,*i).name) return -EINVAL; down(&dev->lock); + if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_INP)) + return -EBUSY; video_mux(dev,*i); up(&dev->lock); return 0; @@ -1674,6 +1842,8 @@ if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; down(&dev->lock); + if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_INP)) + return -EBUSY; dev->ctl_freq = f->frequency; saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&dev->ctl_freq); up(&dev->lock); @@ -1795,6 +1965,8 @@ if (*on) { if (!res_get(dev,fh,RESOURCE_OVERLAY)) return -EBUSY; + if (fh->chn_ev_poll) + return -EBUSY; spin_lock_irqsave(&dev->slock,flags); start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); @@ -1866,6 +2038,8 @@ { int res = saa7134_resource(fh); + if (fh->chn_ev_poll) + return -EBUSY; if (!res_get(dev,fh,res)) return -EBUSY; return videobuf_streamon(file,saa7134_queue(fh)); @@ -1879,6 +2053,51 @@ return err; res_free(dev,fh,res); return 0; + } + + /* --- preliminary ------------------------------------------- */ + case VIDIOC_G_CHNPRIO: + { + enum v4l2_chn_prio *chn_prio = arg; + *chn_prio = get_max_user_prio(dev); + return 0; + } + case VIDIOC_S_CHNPRIO: + { + enum v4l2_chn_prio *chn_prio = arg; + + if ((*chn_prio >= V4L2_CHN_PRIO_COUNT) || (*chn_prio < 0)) + return -EINVAL; + if (!check_alloc_prio(fh, *chn_prio)) + return -EBUSY; + return 0; + } + case VIDIOC_S_CHNEVENT: + { + v4l2_chn_event_mask *ev_poll = arg; + + if ((*ev_poll & ~V4L2_CHN_EV_MASK_ALL) != 0) + return -EINVAL; + if (res_check(fh, RESOURCE_OVERLAY | RESOURCE_VIDEO | RESOURCE_VBI)) + return -EBUSY; + + fh->chn_ev_poll = (*ev_poll ? 1 : 0); + if (fh->chn_ev_poll) + get_chn_events(dev, fh); + return 0; + } + case VIDIOC_G_CHNEVENT: + { + v4l2_chn_event_mask *ev_mask = arg; + int retval = 0; + + if (fh->chn_ev_poll == 0) + return -EINVAL; + + retval = wait_chn_event(dev, fh, file->f_flags & O_NONBLOCK); + if (retval == 0) + *ev_mask = get_chn_events(dev, fh); + return retval; } default: diff -u orig/saa7134.h ./saa7134.h --- orig/saa7134.h Mon Jun 2 12:40:46 2003 +++ ./saa7134.h Sun Jun 15 12:53:43 2003 @@ -84,6 +84,25 @@ CCIR656 = 1, }; +/* XXX note: the following should go into videodev2.h */ +enum v4l2_chn_prio { + V4L2_CHN_PRIO_BACKGROUND, + V4L2_CHN_PRIO_INTERACTIVE, + V4L2_CHN_PRIO_DEFAULT = V4L2_CHN_PRIO_INTERACTIVE, + V4L2_CHN_PRIO_RADIO = V4L2_CHN_PRIO_INTERACTIVE, + V4L2_CHN_PRIO_RECORD, + V4L2_CHN_PRIO_EXCL, + V4L2_CHN_PRIO_COUNT +}; +enum v4l2_chn_event_bit { + V4L2_CHN_EV_STD, + V4L2_CHN_EV_INP, + V4L2_CHN_EV_PRIO, + V4L2_CHN_EV_COUNT +}; +typedef __u32 v4l2_chn_event_mask; +#define V4L2_CHN_EV_MASK_ALL ((1 << V4L2_CHN_EV_COUNT) - 1) + /* ----------------------------------------------------------- */ /* static data */ @@ -257,6 +276,9 @@ struct v4l2_clip clips[8]; unsigned int nclips; unsigned int resources; + int user_prio; + unsigned char chn_ev_idx[V4L2_CHN_EV_COUNT]; + int chn_ev_poll; /* video capture */ struct saa7134_format *fmt; @@ -315,6 +337,9 @@ /* various device info */ unsigned int resources; + int user_prios[V4L2_CHN_PRIO_COUNT]; + wait_queue_head_t chn_ev_q; + unsigned char chn_ev_idx[V4L2_CHN_EV_COUNT]; struct video_device video_dev; struct video_device ts_dev; struct video_device radio_dev; @@ -375,6 +400,15 @@ unsigned int hw_mute; int last_carrier; }; + +/* ----------------------------------------------------------- */ +/* private ioctls */ + +/* XXX note: the following should go into videodev2.h */ +#define VIDIOC_G_CHNPRIO _IOR('V' , BASE_VIDIOCPRIVATE+10, enum v4l2_chn_prio) +#define VIDIOC_S_CHNPRIO _IOW('V' , BASE_VIDIOCPRIVATE+11, enum v4l2_chn_prio) +#define VIDIOC_G_CHNEVENT _IOR('V' , BASE_VIDIOCPRIVATE+12, v4l2_chn_event_mask) +#define VIDIOC_S_CHNEVENT _IOW('V' , BASE_VIDIOCPRIVATE+13, v4l2_chn_event_mask) /* ----------------------------------------------------------- */