/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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->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<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 fw_ver) { int err; if (fw_ver > 0x15) { switch (qc->id) { case V4L2_CID_BRIGHTNESS: return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); case V4L2_CID_CONTRAST: return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); case V4L2_CID_SATURATION: return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); case V4L2_CID_HUE: return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf); case V4L2_CID_SHARPNESS: return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); } } else { 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); } } switch (qc->id) { 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, dev->fw_ver); 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; }