324b04ba5d
A number of things going on here, but the end result is that the IR part on the hdpvr gets enabled, and can be used with ir-kbd-i2c and/or lirc_zilog. First up, there are some conditional build fixes that come into play whether i2c is built-in or modular. Second, we're swapping out i2c_new_probed_device() for i2c_new_device(), as in my testing, probing always fails, but we *know* that all hdpvr devices have a z8 chip at 0x70 and 0x71. Third, we're poking at an i2c address directly without a client, and writing some magic bits to actually turn on this IR part (this could use some improvement in the future). Fourth, some of the i2c_adapter storage has been reworked, as the existing implementation used to lead to an oops following i2c changes c. 2.6.31. Earlier editions of this patch have been floating around the 'net for a while, including being patched into Fedora kernels, and they *do* work. This specific version isn't yet tested, beyond loading ir-kbd-i2c and confirming that it does bind to the RX address of the hdpvr. [mchehab@redhat.com: I2C_CLASS_TV_ANALOG is not defined. Fix compilation bug] Signed-off-by: Jarod Wilson <jarod@redhat.com> Acked-by: Andy Walls <awalls@md.metrocast.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
1271 lines
30 KiB
C
1271 lines
30 KiB
C
/*
|
|
* Hauppauge HD PVR USB driver - video 4 linux 2 interface
|
|
*
|
|
* Copyright (C) 2008 Janne Grunau (j@jannau.net)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation, version 2.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/version.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include "hdpvr.h"
|
|
|
|
#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
|
|
|
|
#define print_buffer_status() { \
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
|
|
"%s:%d buffer stat: %d free, %d proc\n", \
|
|
__func__, __LINE__, \
|
|
list_size(&dev->free_buff_list), \
|
|
list_size(&dev->rec_buff_list)); }
|
|
|
|
struct hdpvr_fh {
|
|
struct hdpvr_device *dev;
|
|
};
|
|
|
|
static uint list_size(struct list_head *list)
|
|
{
|
|
struct list_head *tmp;
|
|
uint count = 0;
|
|
|
|
list_for_each(tmp, list) {
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*=========================================================================*/
|
|
/* urb callback */
|
|
static void hdpvr_read_bulk_callback(struct urb *urb)
|
|
{
|
|
struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
|
|
struct hdpvr_device *dev = buf->dev;
|
|
|
|
/* marking buffer as received and wake waiting */
|
|
buf->status = BUFSTAT_READY;
|
|
wake_up_interruptible(&dev->wait_data);
|
|
}
|
|
|
|
/*=========================================================================*/
|
|
/* bufffer bits */
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
int hdpvr_cancel_queue(struct hdpvr_device *dev)
|
|
{
|
|
struct hdpvr_buffer *buf;
|
|
|
|
list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
|
|
usb_kill_urb(buf->urb);
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
}
|
|
|
|
list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdpvr_free_queue(struct list_head *q)
|
|
{
|
|
struct list_head *tmp;
|
|
struct list_head *p;
|
|
struct hdpvr_buffer *buf;
|
|
struct urb *urb;
|
|
|
|
for (p = q->next; p != q;) {
|
|
buf = list_entry(p, struct hdpvr_buffer, buff_list);
|
|
|
|
urb = buf->urb;
|
|
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
|
urb->transfer_buffer, urb->transfer_dma);
|
|
usb_free_urb(urb);
|
|
tmp = p->next;
|
|
list_del(p);
|
|
kfree(buf);
|
|
p = tmp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
int hdpvr_free_buffers(struct hdpvr_device *dev)
|
|
{
|
|
hdpvr_cancel_queue(dev);
|
|
|
|
hdpvr_free_queue(&dev->free_buff_list);
|
|
hdpvr_free_queue(&dev->rec_buff_list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
|
|
{
|
|
uint i;
|
|
int retval = -ENOMEM;
|
|
u8 *mem;
|
|
struct hdpvr_buffer *buf;
|
|
struct urb *urb;
|
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"allocating %u buffers\n", count);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
|
|
if (!buf) {
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
|
|
goto exit;
|
|
}
|
|
buf->dev = dev;
|
|
|
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (!urb) {
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
|
|
goto exit_urb;
|
|
}
|
|
buf->urb = urb;
|
|
|
|
mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
|
|
&urb->transfer_dma);
|
|
if (!mem) {
|
|
v4l2_err(&dev->v4l2_dev,
|
|
"cannot allocate usb transfer buffer\n");
|
|
goto exit_urb_buffer;
|
|
}
|
|
|
|
usb_fill_bulk_urb(buf->urb, dev->udev,
|
|
usb_rcvbulkpipe(dev->udev,
|
|
dev->bulk_in_endpointAddr),
|
|
mem, dev->bulk_in_size,
|
|
hdpvr_read_bulk_callback, buf);
|
|
|
|
buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
list_add_tail(&buf->buff_list, &dev->free_buff_list);
|
|
}
|
|
return 0;
|
|
exit_urb_buffer:
|
|
usb_free_urb(urb);
|
|
exit_urb:
|
|
kfree(buf);
|
|
exit:
|
|
hdpvr_free_buffers(dev);
|
|
return retval;
|
|
}
|
|
|
|
static int hdpvr_submit_buffers(struct hdpvr_device *dev)
|
|
{
|
|
struct hdpvr_buffer *buf;
|
|
struct urb *urb;
|
|
int ret = 0, err_count = 0;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
while (dev->status == STATUS_STREAMING &&
|
|
!list_empty(&dev->free_buff_list)) {
|
|
|
|
buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
|
|
buff_list);
|
|
if (buf->status != BUFSTAT_AVAILABLE) {
|
|
v4l2_err(&dev->v4l2_dev,
|
|
"buffer not marked as available\n");
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
|
|
urb = buf->urb;
|
|
urb->status = 0;
|
|
urb->actual_length = 0;
|
|
ret = usb_submit_urb(urb, GFP_KERNEL);
|
|
if (ret) {
|
|
v4l2_err(&dev->v4l2_dev,
|
|
"usb_submit_urb in %s returned %d\n",
|
|
__func__, ret);
|
|
if (++err_count > 2)
|
|
break;
|
|
continue;
|
|
}
|
|
buf->status = BUFSTAT_INPROGRESS;
|
|
list_move_tail(&buf->buff_list, &dev->rec_buff_list);
|
|
}
|
|
err:
|
|
print_buffer_status();
|
|
mutex_unlock(&dev->io_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
|
|
{
|
|
struct hdpvr_buffer *buf;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
if (list_empty(&dev->rec_buff_list)) {
|
|
mutex_unlock(&dev->io_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
|
|
buff_list);
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void hdpvr_transmit_buffers(struct work_struct *work)
|
|
{
|
|
struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
|
|
worker);
|
|
|
|
while (dev->status == STATUS_STREAMING) {
|
|
|
|
if (hdpvr_submit_buffers(dev)) {
|
|
v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
|
|
goto error;
|
|
}
|
|
if (wait_event_interruptible(dev->wait_buffer,
|
|
!list_empty(&dev->free_buff_list) ||
|
|
dev->status != STATUS_STREAMING))
|
|
goto error;
|
|
}
|
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"transmit worker exited\n");
|
|
return;
|
|
error:
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"transmit buffers errored\n");
|
|
dev->status = STATUS_ERROR;
|
|
}
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
static int hdpvr_start_streaming(struct hdpvr_device *dev)
|
|
{
|
|
int ret;
|
|
struct hdpvr_video_info *vidinf;
|
|
|
|
if (dev->status == STATUS_STREAMING)
|
|
return 0;
|
|
else if (dev->status != STATUS_IDLE)
|
|
return -EAGAIN;
|
|
|
|
vidinf = get_video_info(dev);
|
|
|
|
if (vidinf) {
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"video signal: %dx%d@%dhz\n", vidinf->width,
|
|
vidinf->height, vidinf->fps);
|
|
kfree(vidinf);
|
|
|
|
/* start streaming 2 request */
|
|
ret = usb_control_msg(dev->udev,
|
|
usb_sndctrlpipe(dev->udev, 0),
|
|
0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"encoder start control request returned %d\n", ret);
|
|
|
|
hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
|
|
|
|
INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
|
|
queue_work(dev->workqueue, &dev->worker);
|
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"streaming started\n");
|
|
dev->status = STATUS_STREAMING;
|
|
|
|
return 0;
|
|
}
|
|
msleep(250);
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"no video signal at input %d\n", dev->options.video_input);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
|
|
/* function expects dev->io_mutex to be hold by caller */
|
|
static int hdpvr_stop_streaming(struct hdpvr_device *dev)
|
|
{
|
|
int actual_length;
|
|
uint c = 0;
|
|
u8 *buf;
|
|
|
|
if (dev->status == STATUS_IDLE)
|
|
return 0;
|
|
else if (dev->status != STATUS_STREAMING)
|
|
return -EAGAIN;
|
|
|
|
buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
|
|
if (!buf)
|
|
v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
|
|
"for emptying the internal device buffer. "
|
|
"Next capture start will be slow\n");
|
|
|
|
dev->status = STATUS_SHUTTING_DOWN;
|
|
hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
wake_up_interruptible(&dev->wait_buffer);
|
|
msleep(50);
|
|
|
|
flush_workqueue(dev->workqueue);
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
/* kill the still outstanding urbs */
|
|
hdpvr_cancel_queue(dev);
|
|
|
|
/* emptying the device buffer beforeshutting it down */
|
|
while (buf && ++c < 500 &&
|
|
!usb_bulk_msg(dev->udev,
|
|
usb_rcvbulkpipe(dev->udev,
|
|
dev->bulk_in_endpointAddr),
|
|
buf, dev->bulk_in_size, &actual_length,
|
|
BULK_URB_TIMEOUT)) {
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"%2d: got %d bytes\n", c, actual_length);
|
|
}
|
|
kfree(buf);
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"used %d urbs to empty device buffers\n", c-1);
|
|
msleep(10);
|
|
|
|
dev->status = STATUS_IDLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*=======================================================================*/
|
|
/*
|
|
* video 4 linux 2 file operations
|
|
*/
|
|
|
|
static int hdpvr_open(struct file *file)
|
|
{
|
|
struct hdpvr_device *dev;
|
|
struct hdpvr_fh *fh;
|
|
int retval = -ENOMEM;
|
|
|
|
dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
|
|
if (!dev) {
|
|
pr_err("open failing with with ENODEV\n");
|
|
retval = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
|
|
if (!fh) {
|
|
v4l2_err(&dev->v4l2_dev, "Out of memory\n");
|
|
goto err;
|
|
}
|
|
/* lock the device to allow correctly handling errors
|
|
* in resumption */
|
|
mutex_lock(&dev->io_mutex);
|
|
dev->open_count++;
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
fh->dev = dev;
|
|
|
|
/* save our object in the file's private structure */
|
|
file->private_data = fh;
|
|
|
|
retval = 0;
|
|
err:
|
|
return retval;
|
|
}
|
|
|
|
static int hdpvr_release(struct file *file)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
|
|
hdpvr_stop_streaming(dev);
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* hdpvr_v4l2_read()
|
|
* will allocate buffers when called for the first time
|
|
*/
|
|
static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
|
|
loff_t *pos)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
struct hdpvr_buffer *buf = NULL;
|
|
struct urb *urb;
|
|
unsigned int ret = 0;
|
|
int rem, cnt;
|
|
|
|
if (*pos)
|
|
return -ESPIPE;
|
|
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
if (dev->status == STATUS_IDLE) {
|
|
if (hdpvr_start_streaming(dev)) {
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"start_streaming failed\n");
|
|
ret = -EIO;
|
|
msleep(200);
|
|
dev->status = STATUS_IDLE;
|
|
mutex_unlock(&dev->io_mutex);
|
|
goto err;
|
|
}
|
|
print_buffer_status();
|
|
}
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
/* wait for the first buffer */
|
|
if (!(file->f_flags & O_NONBLOCK)) {
|
|
if (wait_event_interruptible(dev->wait_data,
|
|
hdpvr_get_next_buffer(dev)))
|
|
return -ERESTARTSYS;
|
|
}
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
|
|
while (count > 0 && buf) {
|
|
|
|
if (buf->status != BUFSTAT_READY &&
|
|
dev->status != STATUS_DISCONNECTED) {
|
|
/* return nonblocking */
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
if (!ret)
|
|
ret = -EAGAIN;
|
|
goto err;
|
|
}
|
|
|
|
if (wait_event_interruptible(dev->wait_data,
|
|
buf->status == BUFSTAT_READY)) {
|
|
ret = -ERESTARTSYS;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (buf->status != BUFSTAT_READY)
|
|
break;
|
|
|
|
/* set remaining bytes to copy */
|
|
urb = buf->urb;
|
|
rem = urb->actual_length - buf->pos;
|
|
cnt = rem > count ? count : rem;
|
|
|
|
if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
|
|
cnt)) {
|
|
v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
|
|
if (!ret)
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
|
|
buf->pos += cnt;
|
|
count -= cnt;
|
|
buffer += cnt;
|
|
ret += cnt;
|
|
|
|
/* finished, take next buffer */
|
|
if (buf->pos == urb->actual_length) {
|
|
mutex_lock(&dev->io_mutex);
|
|
buf->pos = 0;
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
|
|
list_move_tail(&buf->buff_list, &dev->free_buff_list);
|
|
|
|
print_buffer_status();
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
wake_up_interruptible(&dev->wait_buffer);
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
}
|
|
}
|
|
err:
|
|
if (!ret && !buf)
|
|
ret = -EAGAIN;
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
|
|
{
|
|
struct hdpvr_buffer *buf = NULL;
|
|
struct hdpvr_fh *fh = filp->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
unsigned int mask = 0;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
if (!video_is_registered(dev->video_dev)) {
|
|
mutex_unlock(&dev->io_mutex);
|
|
return -EIO;
|
|
}
|
|
|
|
if (dev->status == STATUS_IDLE) {
|
|
if (hdpvr_start_streaming(dev)) {
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
"start_streaming failed\n");
|
|
dev->status = STATUS_IDLE;
|
|
}
|
|
|
|
print_buffer_status();
|
|
}
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
/* only wait if no data is available */
|
|
if (!buf || buf->status != BUFSTAT_READY) {
|
|
poll_wait(filp, &dev->wait_data, wait);
|
|
buf = hdpvr_get_next_buffer(dev);
|
|
}
|
|
if (buf && buf->status == BUFSTAT_READY)
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
return mask;
|
|
}
|
|
|
|
|
|
static const struct v4l2_file_operations hdpvr_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = hdpvr_open,
|
|
.release = hdpvr_release,
|
|
.read = hdpvr_read,
|
|
.poll = hdpvr_poll,
|
|
.unlocked_ioctl = video_ioctl2,
|
|
};
|
|
|
|
/*=======================================================================*/
|
|
/*
|
|
* V4L2 ioctl handling
|
|
*/
|
|
|
|
static int vidioc_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
{
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
strcpy(cap->driver, "hdpvr");
|
|
strcpy(cap->card, "Hauppauge HD PVR");
|
|
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
|
cap->version = HDPVR_VERSION;
|
|
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
|
|
V4L2_CAP_AUDIO |
|
|
V4L2_CAP_READWRITE;
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_std(struct file *file, void *private_data,
|
|
v4l2_std_id *std)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
u8 std_type = 1;
|
|
|
|
if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
|
|
std_type = 0;
|
|
|
|
return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
|
|
}
|
|
|
|
static const char *iname[] = {
|
|
[HDPVR_COMPONENT] = "Component",
|
|
[HDPVR_SVIDEO] = "S-Video",
|
|
[HDPVR_COMPOSITE] = "Composite",
|
|
};
|
|
|
|
static int vidioc_enum_input(struct file *file, void *priv,
|
|
struct v4l2_input *i)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
unsigned int n;
|
|
|
|
n = i->index;
|
|
if (n >= HDPVR_VIDEO_INPUTS)
|
|
return -EINVAL;
|
|
|
|
i->type = V4L2_INPUT_TYPE_CAMERA;
|
|
|
|
strncpy(i->name, iname[n], sizeof(i->name) - 1);
|
|
i->name[sizeof(i->name) - 1] = '\0';
|
|
|
|
i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
|
|
|
|
i->std = dev->video_dev->tvnorms;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_input(struct file *file, void *private_data,
|
|
unsigned int index)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int retval;
|
|
|
|
if (index >= HDPVR_VIDEO_INPUTS)
|
|
return -EINVAL;
|
|
|
|
if (dev->status != STATUS_IDLE)
|
|
return -EAGAIN;
|
|
|
|
retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
|
|
if (!retval)
|
|
dev->options.video_input = index;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int vidioc_g_input(struct file *file, void *private_data,
|
|
unsigned int *index)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
|
|
*index = dev->options.video_input;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const char *audio_iname[] = {
|
|
[HDPVR_RCA_FRONT] = "RCA front",
|
|
[HDPVR_RCA_BACK] = "RCA back",
|
|
[HDPVR_SPDIF] = "SPDIF",
|
|
};
|
|
|
|
static int vidioc_enumaudio(struct file *file, void *priv,
|
|
struct v4l2_audio *audio)
|
|
{
|
|
unsigned int n;
|
|
|
|
n = audio->index;
|
|
if (n >= HDPVR_AUDIO_INPUTS)
|
|
return -EINVAL;
|
|
|
|
audio->capability = V4L2_AUDCAP_STEREO;
|
|
|
|
strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
|
|
audio->name[sizeof(audio->name) - 1] = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_audio(struct file *file, void *private_data,
|
|
struct v4l2_audio *audio)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int retval;
|
|
|
|
if (audio->index >= HDPVR_AUDIO_INPUTS)
|
|
return -EINVAL;
|
|
|
|
if (dev->status != STATUS_IDLE)
|
|
return -EAGAIN;
|
|
|
|
retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
|
|
if (!retval)
|
|
dev->options.audio_input = audio->index;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int vidioc_g_audio(struct file *file, void *private_data,
|
|
struct v4l2_audio *audio)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
|
|
audio->index = dev->options.audio_input;
|
|
audio->capability = V4L2_AUDCAP_STEREO;
|
|
strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
|
|
audio->name[sizeof(audio->name) - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static const s32 supported_v4l2_ctrls[] = {
|
|
V4L2_CID_BRIGHTNESS,
|
|
V4L2_CID_CONTRAST,
|
|
V4L2_CID_SATURATION,
|
|
V4L2_CID_HUE,
|
|
V4L2_CID_SHARPNESS,
|
|
V4L2_CID_MPEG_AUDIO_ENCODING,
|
|
V4L2_CID_MPEG_VIDEO_ENCODING,
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
|
|
V4L2_CID_MPEG_VIDEO_BITRATE,
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
|
|
};
|
|
|
|
static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
|
|
int ac3)
|
|
{
|
|
int err;
|
|
|
|
switch (qc->id) {
|
|
case V4L2_CID_BRIGHTNESS:
|
|
return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
|
|
case V4L2_CID_CONTRAST:
|
|
return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
|
|
case V4L2_CID_SATURATION:
|
|
return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
|
|
case V4L2_CID_HUE:
|
|
return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
|
|
case V4L2_CID_SHARPNESS:
|
|
return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
return v4l2_ctrl_query_fill(
|
|
qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
|
|
ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
|
|
: V4L2_MPEG_AUDIO_ENCODING_AAC,
|
|
1, V4L2_MPEG_AUDIO_ENCODING_AAC);
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
return v4l2_ctrl_query_fill(
|
|
qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
|
|
|
|
/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
|
|
/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
return v4l2_ctrl_query_fill(
|
|
qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
|
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE:
|
|
return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
|
|
6500000);
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
|
|
err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
|
|
9000000);
|
|
if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
|
|
qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
|
|
return err;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int vidioc_queryctrl(struct file *file, void *private_data,
|
|
struct v4l2_queryctrl *qc)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int i, next;
|
|
u32 id = qc->id;
|
|
|
|
memset(qc, 0, sizeof(*qc));
|
|
|
|
next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
|
|
qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
|
|
if (next) {
|
|
if (qc->id < supported_v4l2_ctrls[i])
|
|
qc->id = supported_v4l2_ctrls[i];
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (qc->id == supported_v4l2_ctrls[i])
|
|
return fill_queryctrl(&dev->options, qc,
|
|
dev->flags & HDPVR_FLAG_AC3_CAP);
|
|
|
|
if (qc->id < supported_v4l2_ctrls[i])
|
|
break;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int vidioc_g_ctrl(struct file *file, void *private_data,
|
|
struct v4l2_control *ctrl)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_BRIGHTNESS:
|
|
ctrl->value = dev->options.brightness;
|
|
break;
|
|
case V4L2_CID_CONTRAST:
|
|
ctrl->value = dev->options.contrast;
|
|
break;
|
|
case V4L2_CID_SATURATION:
|
|
ctrl->value = dev->options.saturation;
|
|
break;
|
|
case V4L2_CID_HUE:
|
|
ctrl->value = dev->options.hue;
|
|
break;
|
|
case V4L2_CID_SHARPNESS:
|
|
ctrl->value = dev->options.sharpness;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_ctrl(struct file *file, void *private_data,
|
|
struct v4l2_control *ctrl)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int retval;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_BRIGHTNESS:
|
|
retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
|
|
if (!retval)
|
|
dev->options.brightness = ctrl->value;
|
|
break;
|
|
case V4L2_CID_CONTRAST:
|
|
retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
|
|
if (!retval)
|
|
dev->options.contrast = ctrl->value;
|
|
break;
|
|
case V4L2_CID_SATURATION:
|
|
retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
|
|
if (!retval)
|
|
dev->options.saturation = ctrl->value;
|
|
break;
|
|
case V4L2_CID_HUE:
|
|
retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
|
|
if (!retval)
|
|
dev->options.hue = ctrl->value;
|
|
break;
|
|
case V4L2_CID_SHARPNESS:
|
|
retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
|
|
if (!retval)
|
|
dev->options.sharpness = ctrl->value;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
static int hdpvr_get_ctrl(struct hdpvr_options *opt,
|
|
struct v4l2_ext_control *ctrl)
|
|
{
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
ctrl->value = opt->audio_codec;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
|
|
break;
|
|
/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
|
|
/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
|
|
/* break; */
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
|
|
? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
|
|
: V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE:
|
|
ctrl->value = opt->bitrate * 100000;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
|
|
ctrl->value = opt->peak_bitrate * 100000;
|
|
break;
|
|
case V4L2_CID_MPEG_STREAM_TYPE:
|
|
ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_g_ext_ctrls(struct file *file, void *priv,
|
|
struct v4l2_ext_controls *ctrls)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int i, err = 0;
|
|
|
|
if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
|
|
for (i = 0; i < ctrls->count; i++) {
|
|
struct v4l2_ext_control *ctrl = ctrls->controls + i;
|
|
|
|
err = hdpvr_get_ctrl(&dev->options, ctrl);
|
|
if (err) {
|
|
ctrls->error_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
|
|
(ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
|
|
ret = 0;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
|
|
ret = 0;
|
|
break;
|
|
/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
|
|
/* if (ctrl->value == 0 || ctrl->value == 128) */
|
|
/* ret = 0; */
|
|
/* break; */
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
|
|
ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
|
|
ret = 0;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE:
|
|
{
|
|
uint bitrate = ctrl->value / 100000;
|
|
if (bitrate >= 10 && bitrate <= 135)
|
|
ret = 0;
|
|
break;
|
|
}
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
|
|
{
|
|
uint peak_bitrate = ctrl->value / 100000;
|
|
if (peak_bitrate >= 10 && peak_bitrate <= 202)
|
|
ret = 0;
|
|
break;
|
|
}
|
|
case V4L2_CID_MPEG_STREAM_TYPE:
|
|
if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_try_ext_ctrls(struct file *file, void *priv,
|
|
struct v4l2_ext_controls *ctrls)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int i, err = 0;
|
|
|
|
if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
|
|
for (i = 0; i < ctrls->count; i++) {
|
|
struct v4l2_ext_control *ctrl = ctrls->controls + i;
|
|
|
|
err = hdpvr_try_ctrl(ctrl,
|
|
dev->flags & HDPVR_FLAG_AC3_CAP);
|
|
if (err) {
|
|
ctrls->error_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static int hdpvr_set_ctrl(struct hdpvr_device *dev,
|
|
struct v4l2_ext_control *ctrl)
|
|
{
|
|
struct hdpvr_options *opt = &dev->options;
|
|
int ret = 0;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
if (dev->flags & HDPVR_FLAG_AC3_CAP) {
|
|
opt->audio_codec = ctrl->value;
|
|
ret = hdpvr_set_audio(dev, opt->audio_input,
|
|
opt->audio_codec);
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
break;
|
|
/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
|
|
/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
|
|
/* opt->gop_mode |= 0x2; */
|
|
/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
|
|
/* opt->gop_mode); */
|
|
/* } */
|
|
/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
|
|
/* opt->gop_mode &= ~0x2; */
|
|
/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
|
|
/* opt->gop_mode); */
|
|
/* } */
|
|
/* break; */
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
|
|
opt->bitrate_mode != HDPVR_CONSTANT) {
|
|
opt->bitrate_mode = HDPVR_CONSTANT;
|
|
hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
|
|
opt->bitrate_mode);
|
|
}
|
|
if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
|
|
opt->bitrate_mode == HDPVR_CONSTANT) {
|
|
opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
|
|
hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
|
|
opt->bitrate_mode);
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE: {
|
|
uint bitrate = ctrl->value / 100000;
|
|
|
|
opt->bitrate = bitrate;
|
|
if (bitrate >= opt->peak_bitrate)
|
|
opt->peak_bitrate = bitrate+1;
|
|
|
|
hdpvr_set_bitrate(dev);
|
|
break;
|
|
}
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
|
|
uint peak_bitrate = ctrl->value / 100000;
|
|
|
|
if (opt->bitrate_mode == HDPVR_CONSTANT)
|
|
break;
|
|
|
|
if (opt->bitrate < peak_bitrate) {
|
|
opt->peak_bitrate = peak_bitrate;
|
|
hdpvr_set_bitrate(dev);
|
|
} else
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
case V4L2_CID_MPEG_STREAM_TYPE:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int vidioc_s_ext_ctrls(struct file *file, void *priv,
|
|
struct v4l2_ext_controls *ctrls)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int i, err = 0;
|
|
|
|
if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
|
|
for (i = 0; i < ctrls->count; i++) {
|
|
struct v4l2_ext_control *ctrl = ctrls->controls + i;
|
|
|
|
err = hdpvr_try_ctrl(ctrl,
|
|
dev->flags & HDPVR_FLAG_AC3_CAP);
|
|
if (err) {
|
|
ctrls->error_idx = i;
|
|
break;
|
|
}
|
|
err = hdpvr_set_ctrl(dev, ctrl);
|
|
if (err) {
|
|
ctrls->error_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
|
|
if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
return -EINVAL;
|
|
|
|
f->flags = V4L2_FMT_FLAG_COMPRESSED;
|
|
strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
|
|
f->pixelformat = V4L2_PIX_FMT_MPEG;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct hdpvr_fh *fh = file->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
struct hdpvr_video_info *vid_info;
|
|
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
vid_info = get_video_info(dev);
|
|
if (!vid_info)
|
|
return -EFAULT;
|
|
|
|
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
|
|
f->fmt.pix.width = vid_info->width;
|
|
f->fmt.pix.height = vid_info->height;
|
|
f->fmt.pix.sizeimage = dev->bulk_in_size;
|
|
f->fmt.pix.colorspace = 0;
|
|
f->fmt.pix.bytesperline = 0;
|
|
f->fmt.pix.field = V4L2_FIELD_ANY;
|
|
|
|
kfree(vid_info);
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_encoder_cmd(struct file *filp, void *priv,
|
|
struct v4l2_encoder_cmd *a)
|
|
{
|
|
struct hdpvr_fh *fh = filp->private_data;
|
|
struct hdpvr_device *dev = fh->dev;
|
|
int res;
|
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
memset(&a->raw, 0, sizeof(a->raw));
|
|
switch (a->cmd) {
|
|
case V4L2_ENC_CMD_START:
|
|
a->flags = 0;
|
|
res = hdpvr_start_streaming(dev);
|
|
break;
|
|
case V4L2_ENC_CMD_STOP:
|
|
res = hdpvr_stop_streaming(dev);
|
|
break;
|
|
default:
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"Unsupported encoder cmd %d\n", a->cmd);
|
|
res = -EINVAL;
|
|
}
|
|
mutex_unlock(&dev->io_mutex);
|
|
return res;
|
|
}
|
|
|
|
static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
|
|
struct v4l2_encoder_cmd *a)
|
|
{
|
|
switch (a->cmd) {
|
|
case V4L2_ENC_CMD_START:
|
|
case V4L2_ENC_CMD_STOP:
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
|
|
.vidioc_querycap = vidioc_querycap,
|
|
.vidioc_s_std = vidioc_s_std,
|
|
.vidioc_enum_input = vidioc_enum_input,
|
|
.vidioc_g_input = vidioc_g_input,
|
|
.vidioc_s_input = vidioc_s_input,
|
|
.vidioc_enumaudio = vidioc_enumaudio,
|
|
.vidioc_g_audio = vidioc_g_audio,
|
|
.vidioc_s_audio = vidioc_s_audio,
|
|
.vidioc_queryctrl = vidioc_queryctrl,
|
|
.vidioc_g_ctrl = vidioc_g_ctrl,
|
|
.vidioc_s_ctrl = vidioc_s_ctrl,
|
|
.vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
|
|
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
|
|
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
|
|
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
.vidioc_encoder_cmd = vidioc_encoder_cmd,
|
|
.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
|
|
};
|
|
|
|
static void hdpvr_device_release(struct video_device *vdev)
|
|
{
|
|
struct hdpvr_device *dev = video_get_drvdata(vdev);
|
|
|
|
hdpvr_delete(dev);
|
|
mutex_lock(&dev->io_mutex);
|
|
destroy_workqueue(dev->workqueue);
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
|
|
|
/* deregister I2C adapter */
|
|
#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE)
|
|
mutex_lock(&dev->i2c_mutex);
|
|
i2c_del_adapter(&dev->i2c_adapter);
|
|
mutex_unlock(&dev->i2c_mutex);
|
|
#endif /* CONFIG_I2C */
|
|
|
|
kfree(dev->usbc_buf);
|
|
kfree(dev);
|
|
}
|
|
|
|
static const struct video_device hdpvr_video_template = {
|
|
/* .type = VFL_TYPE_GRABBER, */
|
|
/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
|
|
.fops = &hdpvr_fops,
|
|
.release = hdpvr_device_release,
|
|
.ioctl_ops = &hdpvr_ioctl_ops,
|
|
.tvnorms =
|
|
V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B |
|
|
V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
|
|
V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
|
|
V4L2_STD_PAL_60,
|
|
.current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
|
|
V4L2_STD_PAL_60,
|
|
};
|
|
|
|
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
|
|
int devnum)
|
|
{
|
|
/* setup and register video device */
|
|
dev->video_dev = video_device_alloc();
|
|
if (!dev->video_dev) {
|
|
v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
|
|
goto error;
|
|
}
|
|
|
|
*(dev->video_dev) = hdpvr_video_template;
|
|
strcpy(dev->video_dev->name, "Hauppauge HD PVR");
|
|
dev->video_dev->parent = parent;
|
|
video_set_drvdata(dev->video_dev, dev);
|
|
|
|
if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
|
|
v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
return -ENOMEM;
|
|
}
|