2009-03-19 05:10:04 +08:00
|
|
|
/*
|
2009-03-20 06:00:35 +08:00
|
|
|
* Hauppauge HD PVR USB driver - video 4 linux 2 interface
|
2009-03-19 05:10:04 +08:00
|
|
|
*
|
|
|
|
* 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>
|
2013-03-22 02:46:18 +08:00
|
|
|
#include <linux/kconfig.h>
|
2009-03-19 05:10:04 +08:00
|
|
|
#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/workqueue.h>
|
|
|
|
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
#include <media/v4l2-dev.h>
|
|
|
|
#include <media/v4l2-common.h>
|
|
|
|
#include <media/v4l2-ioctl.h>
|
2013-02-12 19:26:59 +08:00
|
|
|
#include <media/v4l2-event.h>
|
2009-03-19 05:10:04 +08:00
|
|
|
#include "hdpvr.h"
|
|
|
|
|
2010-07-26 19:50:32 +08:00
|
|
|
#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
#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)); }
|
2009-03-27 07:29:39 +08:00
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
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;
|
2010-04-12 19:17:25 +08:00
|
|
|
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
|
|
|
urb->transfer_buffer, urb->transfer_dma);
|
2009-03-19 05:10:04 +08:00
|
|
|
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;
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"allocating %u buffers\n", count);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
|
|
|
buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
|
|
|
|
if (!buf) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
buf->dev = dev;
|
|
|
|
|
|
|
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
|
if (!urb) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
|
2009-11-21 19:49:41 +08:00
|
|
|
goto exit_urb;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
buf->urb = urb;
|
|
|
|
|
2010-04-12 19:17:25 +08:00
|
|
|
mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
|
|
|
|
&urb->transfer_dma);
|
2009-03-19 05:10:04 +08:00
|
|
|
if (!mem) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
|
|
|
"cannot allocate usb transfer buffer\n");
|
2009-11-21 19:49:41 +08:00
|
|
|
goto exit_urb_buffer;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2010-10-04 06:09:18 +08:00
|
|
|
buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2009-03-19 05:10:04 +08:00
|
|
|
buf->status = BUFSTAT_AVAILABLE;
|
|
|
|
list_add_tail(&buf->buff_list, &dev->free_buff_list);
|
|
|
|
}
|
|
|
|
return 0;
|
2009-11-21 19:49:41 +08:00
|
|
|
exit_urb_buffer:
|
|
|
|
usb_free_urb(urb);
|
|
|
|
exit_urb:
|
|
|
|
kfree(buf);
|
2009-03-19 05:10:04 +08:00
|
|
|
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) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
2009-04-15 10:14:10 +08:00
|
|
|
"buffer not marked as available\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
urb = buf->urb;
|
|
|
|
urb->status = 0;
|
|
|
|
urb->actual_length = 0;
|
|
|
|
ret = usb_submit_urb(urb, GFP_KERNEL);
|
|
|
|
if (ret) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev,
|
|
|
|
"usb_submit_urb in %s returned %d\n",
|
|
|
|
__func__, ret);
|
2009-03-19 05:10:04 +08:00
|
|
|
if (++err_count > 2)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
buf->status = BUFSTAT_INPROGRESS;
|
|
|
|
list_move_tail(&buf->buff_list, &dev->rec_buff_list);
|
|
|
|
}
|
|
|
|
err:
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
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)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (wait_event_interruptible(dev->wait_buffer,
|
|
|
|
!list_empty(&dev->free_buff_list) ||
|
|
|
|
dev->status != STATUS_STREAMING))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"transmit worker exited\n");
|
|
|
|
return;
|
|
|
|
error:
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"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) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"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);
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"encoder start control request returned %d\n", ret);
|
|
|
|
|
|
|
|
hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
|
|
|
|
|
2012-02-03 00:35:21 +08:00
|
|
|
dev->status = STATUS_STREAMING;
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
|
|
|
|
queue_work(dev->workqueue, &dev->worker);
|
|
|
|
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"streaming started\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
msleep(250);
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 05:10:04 +08:00
|
|
|
"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)
|
|
|
|
{
|
2010-01-23 21:44:34 +08:00
|
|
|
int actual_length;
|
|
|
|
uint c = 0;
|
2009-03-28 07:21:17 +08:00
|
|
|
u8 *buf;
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
if (dev->status == STATUS_IDLE)
|
|
|
|
return 0;
|
|
|
|
else if (dev->status != STATUS_STREAMING)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
2009-03-28 07:21:17 +08:00
|
|
|
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");
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_SHUTTING_DOWN;
|
|
|
|
hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
|
2009-03-27 07:56:06 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
wake_up_interruptible(&dev->wait_buffer);
|
|
|
|
msleep(50);
|
|
|
|
|
|
|
|
flush_workqueue(dev->workqueue);
|
|
|
|
|
2009-03-27 07:56:06 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
/* kill the still outstanding urbs */
|
|
|
|
hdpvr_cancel_queue(dev);
|
|
|
|
|
2009-03-28 07:21:17 +08:00
|
|
|
/* 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);
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_IDLE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*=======================================================================*/
|
|
|
|
/*
|
|
|
|
* video 4 linux 2 file operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int hdpvr_release(struct file *file)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (file->private_data == dev->owner) {
|
2009-03-19 05:10:04 +08:00
|
|
|
hdpvr_stop_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->owner = NULL;
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
2013-04-06 17:00:17 +08:00
|
|
|
return v4l2_fh_release(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
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)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"start_streaming failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
ret = -EIO;
|
|
|
|
msleep(200);
|
|
|
|
dev->status = STATUS_IDLE;
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
goto err;
|
|
|
|
}
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->owner = file->private_data;
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
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)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
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);
|
|
|
|
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2013-02-12 19:26:59 +08:00
|
|
|
unsigned long req_events = poll_requested_events(wait);
|
2009-03-27 01:32:54 +08:00
|
|
|
struct hdpvr_buffer *buf = NULL;
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(filp);
|
2013-02-12 19:26:59 +08:00
|
|
|
unsigned int mask = v4l2_ctrl_poll(filp, wait);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-02-12 19:26:59 +08:00
|
|
|
if (!(req_events & (POLLIN | POLLRDNORM)))
|
|
|
|
return mask;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-02-12 19:26:59 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
if (dev->status == STATUS_IDLE) {
|
|
|
|
if (hdpvr_start_streaming(dev)) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
|
|
|
|
"start_streaming failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->status = STATUS_IDLE;
|
2013-04-06 17:00:17 +08:00
|
|
|
} else {
|
|
|
|
dev->owner = filp->private_data;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
2009-03-27 07:29:39 +08:00
|
|
|
print_buffer_status();
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
2009-03-27 01:32:54 +08:00
|
|
|
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);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
2009-03-27 01:32:54 +08:00
|
|
|
if (buf && buf->status == BUFSTAT_READY)
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations hdpvr_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
2013-04-06 17:00:17 +08:00
|
|
|
.open = v4l2_fh_open,
|
2009-03-19 05:10:04 +08:00
|
|
|
.release = hdpvr_release,
|
|
|
|
.read = hdpvr_read,
|
|
|
|
.poll = hdpvr_poll,
|
2009-03-19 07:57:04 +08:00
|
|
|
.unlocked_ioctl = video_ioctl2,
|
2009-03-19 05:10:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*=======================================================================*/
|
|
|
|
/*
|
|
|
|
* 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");
|
2010-02-14 19:57:39 +08:00
|
|
|
strcpy(cap->card, "Hauppauge HD PVR");
|
2009-03-19 05:10:04 +08:00
|
|
|
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
2013-02-12 20:21:36 +08:00
|
|
|
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO |
|
|
|
|
V4L2_CAP_READWRITE;
|
|
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
2009-03-19 05:10:04 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_s_std(struct file *file, void *private_data,
|
2013-03-15 17:10:40 +08:00
|
|
|
v4l2_std_id std)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
u8 std_type = 1;
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
if (dev->options.video_input == HDPVR_COMPONENT)
|
|
|
|
return -ENODATA;
|
|
|
|
if (dev->status != STATUS_IDLE)
|
|
|
|
return -EBUSY;
|
|
|
|
if (std & V4L2_STD_525_60)
|
2009-03-19 05:10:04 +08:00
|
|
|
std_type = 0;
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->cur_std = std;
|
|
|
|
dev->width = 720;
|
|
|
|
dev->height = std_type ? 576 : 480;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
|
|
|
|
}
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
static int vidioc_g_std(struct file *file, void *private_data,
|
|
|
|
v4l2_std_id *std)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
|
|
|
|
if (dev->options.video_input == HDPVR_COMPONENT)
|
|
|
|
return -ENODATA;
|
|
|
|
*std = dev->cur_std;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *a)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
|
|
|
struct hdpvr_video_info *vid_info;
|
|
|
|
|
|
|
|
if (dev->options.video_input == HDPVR_COMPONENT)
|
|
|
|
return -ENODATA;
|
|
|
|
*a = V4L2_STD_ALL;
|
|
|
|
vid_info = get_video_info(dev);
|
|
|
|
if (vid_info == NULL)
|
|
|
|
return 0;
|
|
|
|
if (vid_info->width == 720 &&
|
|
|
|
(vid_info->height == 480 || vid_info->height == 576)) {
|
|
|
|
*a = (vid_info->height == 480) ?
|
|
|
|
V4L2_STD_525_60 : V4L2_STD_625_50;
|
|
|
|
}
|
|
|
|
kfree(vid_info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
i->capabilities = n ? V4L2_IN_CAP_STD : V4L2_IN_CAP_DV_TIMINGS;
|
|
|
|
i->std = n ? V4L2_STD_ALL : 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_s_input(struct file *file, void *private_data,
|
|
|
|
unsigned int index)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (index >= HDPVR_VIDEO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dev->status != STATUS_IDLE)
|
2013-02-12 20:26:30 +08:00
|
|
|
return -EBUSY;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
|
2013-03-19 20:53:29 +08:00
|
|
|
if (!retval) {
|
2009-03-19 05:10:04 +08:00
|
|
|
dev->options.video_input = index;
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->video_dev->tvnorms =
|
|
|
|
index != HDPVR_COMPONENT ? V4L2_STD_ALL : 0;
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_g_input(struct file *file, void *private_data,
|
|
|
|
unsigned int *index)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
*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,
|
2012-09-04 22:59:31 +08:00
|
|
|
const struct v4l2_audio *audio)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (audio->index >= HDPVR_AUDIO_INPUTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dev->status != STATUS_IDLE)
|
2013-02-12 20:26:30 +08:00
|
|
|
return -EBUSY;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static int hdpvr_try_ctrl(struct v4l2_ctrl *ctrl)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct hdpvr_device *dev =
|
|
|
|
container_of(ctrl->handler, struct hdpvr_device, hdl);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
switch (ctrl->id) {
|
2013-03-19 20:30:50 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
|
|
if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
|
|
|
|
dev->video_bitrate->val >= dev->video_bitrate_peak->val)
|
|
|
|
dev->video_bitrate_peak->val =
|
|
|
|
dev->video_bitrate->val + 100000;
|
2009-03-19 05:10:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static int hdpvr_s_ctrl(struct v4l2_ctrl *ctrl)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct hdpvr_device *dev =
|
|
|
|
container_of(ctrl->handler, struct hdpvr_device, hdl);
|
|
|
|
struct hdpvr_options *opt = &dev->options;
|
|
|
|
int ret = -EINVAL;
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_BRIGHTNESS:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.brightness = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_CONTRAST:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.contrast = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_SATURATION:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.saturation = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_HUE:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_HUE, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.hue = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_SHARPNESS:
|
2013-03-19 20:30:50 +08:00
|
|
|
ret = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
dev->options.sharpness = ctrl->val;
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_MPEG_AUDIO_ENCODING:
|
|
|
|
if (dev->flags & HDPVR_FLAG_AC3_CAP) {
|
2013-03-19 20:30:50 +08:00
|
|
|
opt->audio_codec = ctrl->val;
|
|
|
|
return hdpvr_set_audio(dev, opt->audio_input,
|
2009-03-19 05:10:04 +08:00
|
|
|
opt->audio_codec);
|
|
|
|
}
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
/* 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; */
|
2013-03-19 20:30:50 +08:00
|
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: {
|
|
|
|
uint peak_bitrate = dev->video_bitrate_peak->val / 100000;
|
|
|
|
uint bitrate = dev->video_bitrate->val / 100000;
|
|
|
|
|
|
|
|
if (ctrl->is_new) {
|
|
|
|
if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
|
|
|
|
opt->bitrate_mode = HDPVR_CONSTANT;
|
|
|
|
else
|
|
|
|
opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
|
2009-03-19 05:10:04 +08:00
|
|
|
hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
|
|
|
|
opt->bitrate_mode);
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_activate(dev->video_bitrate_peak,
|
|
|
|
ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
if (dev->video_bitrate_peak->is_new ||
|
|
|
|
dev->video_bitrate->is_new) {
|
|
|
|
opt->bitrate = bitrate;
|
2009-03-19 05:10:04 +08:00
|
|
|
opt->peak_bitrate = peak_bitrate;
|
|
|
|
hdpvr_set_bitrate(dev);
|
2013-03-19 20:30:50 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
case V4L2_CID_MPEG_STREAM_TYPE:
|
2013-03-19 20:30:50 +08:00
|
|
|
return 0;
|
2009-03-19 05:10:04 +08:00
|
|
|
default:
|
2013-03-19 20:30:50 +08:00
|
|
|
break;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
|
|
|
|
struct v4l2_fmtdesc *f)
|
|
|
|
{
|
2013-02-12 20:26:30 +08:00
|
|
|
if (f->index != 0)
|
2009-03-19 05:10:04 +08:00
|
|
|
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)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(file);
|
2009-03-19 05:10:04 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-03-19 07:57:04 +08:00
|
|
|
static int vidioc_encoder_cmd(struct file *filp, void *priv,
|
|
|
|
struct v4l2_encoder_cmd *a)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
struct hdpvr_device *dev = video_drvdata(filp);
|
|
|
|
int res = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
|
|
|
|
mutex_lock(&dev->io_mutex);
|
2013-04-06 17:00:17 +08:00
|
|
|
a->flags = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
|
|
|
|
switch (a->cmd) {
|
|
|
|
case V4L2_ENC_CMD_START:
|
2013-04-06 17:00:17 +08:00
|
|
|
if (dev->owner && filp->private_data != dev->owner) {
|
|
|
|
res = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dev->status == STATUS_STREAMING)
|
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
res = hdpvr_start_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (!res)
|
|
|
|
dev->owner = filp->private_data;
|
|
|
|
else
|
|
|
|
dev->status = STATUS_IDLE;
|
2009-03-19 07:57:04 +08:00
|
|
|
break;
|
|
|
|
case V4L2_ENC_CMD_STOP:
|
2013-04-06 17:00:17 +08:00
|
|
|
if (dev->owner && filp->private_data != dev->owner) {
|
|
|
|
res = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dev->status == STATUS_IDLE)
|
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
res = hdpvr_stop_streaming(dev);
|
2013-04-06 17:00:17 +08:00
|
|
|
if (!res)
|
|
|
|
dev->owner = NULL;
|
2009-03-19 07:57:04 +08:00
|
|
|
break;
|
|
|
|
default:
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
2009-03-19 07:57:04 +08:00
|
|
|
"Unsupported encoder cmd %d\n", a->cmd);
|
2009-03-27 07:56:06 +08:00
|
|
|
res = -EINVAL;
|
2013-04-06 17:00:17 +08:00
|
|
|
break;
|
2009-03-19 07:57:04 +08:00
|
|
|
}
|
2013-04-06 17:00:17 +08:00
|
|
|
|
2009-03-19 07:57:04 +08:00
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
|
|
|
|
struct v4l2_encoder_cmd *a)
|
|
|
|
{
|
2013-04-06 17:00:17 +08:00
|
|
|
a->flags = 0;
|
2009-03-19 07:57:04 +08:00
|
|
|
switch (a->cmd) {
|
|
|
|
case V4L2_ENC_CMD_START:
|
|
|
|
case V4L2_ENC_CMD_STOP:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2009-03-19 05:10:04 +08:00
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
|
|
|
|
.vidioc_querycap = vidioc_querycap,
|
|
|
|
.vidioc_s_std = vidioc_s_std,
|
2013-03-19 20:53:29 +08:00
|
|
|
.vidioc_g_std = vidioc_g_std,
|
|
|
|
.vidioc_querystd = vidioc_querystd,
|
2009-03-19 05:10:04 +08:00
|
|
|
.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,
|
2013-02-12 19:26:59 +08:00
|
|
|
.vidioc_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap,
|
|
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
2009-03-19 07:57:04 +08:00
|
|
|
.vidioc_encoder_cmd = vidioc_encoder_cmd,
|
|
|
|
.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
|
2013-02-12 19:26:59 +08:00
|
|
|
.vidioc_log_status = v4l2_ctrl_log_status,
|
|
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
2009-03-19 05:10:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static void hdpvr_device_release(struct video_device *vdev)
|
|
|
|
{
|
|
|
|
struct hdpvr_device *dev = video_get_drvdata(vdev);
|
|
|
|
|
|
|
|
hdpvr_delete(dev);
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_lock(&dev->io_mutex);
|
|
|
|
destroy_workqueue(dev->workqueue);
|
|
|
|
mutex_unlock(&dev->io_mutex);
|
|
|
|
|
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_free(&dev->hdl);
|
2010-05-02 19:01:04 +08:00
|
|
|
|
|
|
|
/* deregister I2C adapter */
|
2013-03-22 02:46:18 +08:00
|
|
|
#if IS_ENABLED(CONFIG_I2C)
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_lock(&dev->i2c_mutex);
|
[media] hdpvr: enable IR part
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>
2011-01-15 03:25:21 +08:00
|
|
|
i2c_del_adapter(&dev->i2c_adapter);
|
2010-05-02 19:01:04 +08:00
|
|
|
mutex_unlock(&dev->i2c_mutex);
|
|
|
|
#endif /* CONFIG_I2C */
|
|
|
|
|
|
|
|
kfree(dev->usbc_buf);
|
|
|
|
kfree(dev);
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct video_device hdpvr_video_template = {
|
|
|
|
.fops = &hdpvr_fops,
|
|
|
|
.release = hdpvr_device_release,
|
|
|
|
.ioctl_ops = &hdpvr_ioctl_ops,
|
|
|
|
};
|
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = {
|
|
|
|
.try_ctrl = hdpvr_try_ctrl,
|
|
|
|
.s_ctrl = hdpvr_s_ctrl,
|
|
|
|
};
|
|
|
|
|
2009-03-27 01:40:55 +08:00
|
|
|
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
|
|
|
|
int devnum)
|
2009-03-19 05:10:04 +08:00
|
|
|
{
|
2013-03-19 20:30:50 +08:00
|
|
|
struct v4l2_ctrl_handler *hdl = &dev->hdl;
|
|
|
|
bool ac3 = dev->flags & HDPVR_FLAG_AC3_CAP;
|
|
|
|
int res;
|
|
|
|
|
2013-03-19 20:53:29 +08:00
|
|
|
dev->cur_std = V4L2_STD_525_60;
|
|
|
|
dev->width = 720;
|
|
|
|
dev->height = 480;
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_init(hdl, 11);
|
|
|
|
if (dev->fw_ver > 0x15) {
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x40);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x40);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_HUE, 0x0, 0x1e, 1, 0xf);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
} else {
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x86);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_HUE, 0x0, 0xff, 1, 0x80);
|
|
|
|
v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_STREAM_TYPE,
|
|
|
|
V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
|
|
|
|
0x1, V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_AUDIO_ENCODING,
|
|
|
|
ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : V4L2_MPEG_AUDIO_ENCODING_AAC,
|
|
|
|
0x7, V4L2_MPEG_AUDIO_ENCODING_AAC);
|
|
|
|
v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_ENCODING,
|
|
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 0x3,
|
|
|
|
V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
|
|
|
|
|
|
|
|
dev->video_mode = v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
|
|
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
|
|
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
|
|
|
|
|
|
|
|
dev->video_bitrate = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE,
|
|
|
|
1000000, 13500000, 100000, 6500000);
|
|
|
|
dev->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
|
|
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
|
|
|
|
1100000, 20200000, 100000, 9000000);
|
|
|
|
dev->v4l2_dev.ctrl_handler = hdl;
|
|
|
|
if (hdl->error) {
|
|
|
|
res = hdl->error;
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Could not register controls\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
v4l2_ctrl_cluster(3, &dev->video_mode);
|
|
|
|
res = v4l2_ctrl_handler_setup(hdl);
|
|
|
|
if (res < 0) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Could not setup controls\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2009-03-19 05:10:04 +08:00
|
|
|
/* setup and register video device */
|
|
|
|
dev->video_dev = video_device_alloc();
|
|
|
|
if (!dev->video_dev) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
|
2013-03-19 20:30:50 +08:00
|
|
|
res = -ENOMEM;
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2013-04-06 17:00:17 +08:00
|
|
|
*dev->video_dev = hdpvr_video_template;
|
2009-03-19 05:10:04 +08:00
|
|
|
strcpy(dev->video_dev->name, "Hauppauge HD PVR");
|
2013-04-06 17:00:17 +08:00
|
|
|
dev->video_dev->v4l2_dev = &dev->v4l2_dev;
|
2009-03-19 05:10:04 +08:00
|
|
|
video_set_drvdata(dev->video_dev, dev);
|
2013-02-12 19:26:59 +08:00
|
|
|
set_bit(V4L2_FL_USE_FH_PRIO, &dev->video_dev->flags);
|
2009-03-19 05:10:04 +08:00
|
|
|
|
2013-03-19 20:30:50 +08:00
|
|
|
res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum);
|
|
|
|
if (res < 0) {
|
2009-03-28 07:09:40 +08:00
|
|
|
v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
|
2009-03-19 05:10:04 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
2013-03-19 20:30:50 +08:00
|
|
|
v4l2_ctrl_handler_free(hdl);
|
|
|
|
return res;
|
2009-03-19 05:10:04 +08:00
|
|
|
}
|