1
0
Files
kernel-49/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
Greg Kroah-Hartman 06a33ede12 Merge 4.9.208 into android-4.9-q
Changes in 4.9.208
	btrfs: skip log replay on orphaned roots
	btrfs: do not leak reloc root if we fail to read the fs root
	btrfs: handle ENOENT in btrfs_uuid_tree_iterate
	ALSA: pcm: Avoid possible info leaks from PCM stream buffers
	ALSA: hda/ca0132 - Keep power on during processing DSP response
	ALSA: hda/ca0132 - Avoid endless loop
	drm: mst: Fix query_payload ack reply struct
	drm/bridge: analogix-anx78xx: silence -EPROBE_DEFER warnings
	iio: light: bh1750: Resolve compiler warning and make code more readable
	spi: Add call to spi_slave_abort() function when spidev driver is released
	staging: rtl8192u: fix multiple memory leaks on error path
	staging: rtl8188eu: fix possible null dereference
	rtlwifi: prevent memory leak in rtl_usb_probe
	libertas: fix a potential NULL pointer dereference
	IB/iser: bound protection_sg size by data_sg size
	media: am437x-vpfe: Setting STD to current value is not an error
	media: i2c: ov2659: fix s_stream return value
	media: i2c: ov2659: Fix missing 720p register config
	media: ov6650: Fix stored frame format not in sync with hardware
	tools/power/cpupower: Fix initializer override in hsw_ext_cstates
	usb: renesas_usbhs: add suspend event support in gadget mode
	hwrng: omap3-rom - Call clk_disable_unprepare() on exit only if not idled
	regulator: max8907: Fix the usage of uninitialized variable in max8907_regulator_probe()
	media: flexcop-usb: fix NULL-ptr deref in flexcop_usb_transfer_init()
	media: cec-funcs.h: add status_req checks
	samples: pktgen: fix proc_cmd command result check logic
	mwifiex: pcie: Fix memory leak in mwifiex_pcie_init_evt_ring
	media: ti-vpe: vpe: fix a v4l2-compliance warning about invalid pixel format
	media: ti-vpe: vpe: fix a v4l2-compliance failure about frame sequence number
	media: ti-vpe: vpe: Make sure YUYV is set as default format
	extcon: sm5502: Reset registers during initialization
	x86/mm: Use the correct function type for native_set_fixmap()
	perf test: Report failure for mmap events
	perf report: Add warning when libunwind not compiled in
	usb: usbfs: Suppress problematic bind and unbind uevents.
	iio: adc: max1027: Reset the device at probe time
	Bluetooth: hci_core: fix init for HCI_USER_CHANNEL
	x86/mce: Lower throttling MCE messages' priority to warning
	drm/gma500: fix memory disclosures due to uninitialized bytes
	rtl8xxxu: fix RTL8723BU connection failure issue after warm reboot
	x86/ioapic: Prevent inconsistent state when moving an interrupt
	arm64: psci: Reduce the waiting time for cpu_psci_cpu_kill()
	libata: Ensure ata_port probe has completed before detach
	pinctrl: sh-pfc: sh7734: Fix duplicate TCLK1_B
	Bluetooth: Fix advertising duplicated flags
	bnx2x: Fix PF-VF communication over multi-cos queues.
	spi: img-spfi: fix potential double release
	ALSA: timer: Limit max amount of slave instances
	rtlwifi: fix memory leak in rtl92c_set_fw_rsvdpagepkt()
	perf probe: Fix to find range-only function instance
	perf probe: Fix to list probe event with correct line number
	perf probe: Walk function lines in lexical blocks
	perf probe: Fix to probe an inline function which has no entry pc
	perf probe: Fix to show ranges of variables in functions without entry_pc
	perf probe: Fix to show inlined function callsite without entry_pc
	perf probe: Fix to probe a function which has no entry pc
	perf probe: Skip overlapped location on searching variables
	perf probe: Return a better scope DIE if there is no best scope
	perf probe: Fix to show calling lines of inlined functions
	perf probe: Skip end-of-sequence and non statement lines
	perf probe: Filter out instances except for inlined subroutine and subprogram
	ath10k: fix get invalid tx rate for Mesh metric
	media: pvrusb2: Fix oops on tear-down when radio support is not present
	media: si470x-i2c: add missed operations in remove
	EDAC/ghes: Fix grain calculation
	spi: pxa2xx: Add missed security checks
	ASoC: rt5677: Mark reg RT5677_PWR_ANLG2 as volatile
	s390/disassembler: don't hide instruction addresses
	parport: load lowlevel driver if ports not found
	cpufreq: Register drivers only after CPU devices have been registered
	x86/crash: Add a forward declaration of struct kimage
	iwlwifi: mvm: fix unaligned read of rx_pkt_status
	spi: tegra20-slink: add missed clk_unprepare
	mmc: tmio: Add MMC_CAP_ERASE to allow erase/discard/trim requests
	btrfs: don't prematurely free work in end_workqueue_fn()
	btrfs: don't prematurely free work in run_ordered_work()
	spi: st-ssc4: add missed pm_runtime_disable
	x86/insn: Add some Intel instructions to the opcode map
	iwlwifi: check kasprintf() return value
	fbtft: Make sure string is NULL terminated
	crypto: sun4i-ss - Fix 64-bit size_t warnings on sun4i-ss-hash.c
	crypto: vmx - Avoid weird build failures
	libtraceevent: Fix memory leakage in copy_filter_type
	net: phy: initialise phydev speed and duplex sanely
	btrfs: don't prematurely free work in reada_start_machine_worker()
	Revert "mmc: sdhci: Fix incorrect switch to HS mode"
	usb: xhci: Fix build warning seen with CONFIG_PM=n
	btrfs: don't double lock the subvol_sem for rename exchange
	btrfs: do not call synchronize_srcu() in inode_tree_del
	btrfs: return error pointer from alloc_test_extent_buffer
	btrfs: abort transaction after failed inode updates in create_subvol
	Btrfs: fix removal logic of the tree mod log that leads to use-after-free issues
	af_packet: set defaule value for tmo
	fjes: fix missed check in fjes_acpi_add
	mod_devicetable: fix PHY module format
	net: hisilicon: Fix a BUG trigered by wrong bytes_compl
	net: nfc: nci: fix a possible sleep-in-atomic-context bug in nci_uart_tty_receive()
	net: qlogic: Fix error paths in ql_alloc_large_buffers()
	net: usb: lan78xx: Fix suspend/resume PHY register access error
	sctp: fully initialize v4 addr in some functions
	net: dst: Force 4-byte alignment of dst_metrics
	usbip: Fix error path of vhci_recv_ret_submit()
	USB: EHCI: Do not return -EPIPE when hub is disconnected
	platform/x86: hp-wmi: Make buffer for HPWMI_FEATURE2_QUERY 128 bytes
	staging: comedi: gsc_hpdi: check dma_alloc_coherent() return value
	ext4: fix ext4_empty_dir() for directories with holes
	ext4: check for directory entries too close to block end
	powerpc/irq: fix stack overflow verification
	mmc: sdhci-of-esdhc: fix P2020 errata handling
	perf probe: Fix to show function entry line as probe-able
	scsi: mpt3sas: Fix clear pending bit in ioctl status
	scsi: lpfc: Fix locking on mailbox command completion
	Input: atmel_mxt_ts - disable IRQ across suspend
	iommu/tegra-smmu: Fix page tables in > 4 GiB memory
	scsi: target: compare full CHAP_A Algorithm strings
	scsi: lpfc: Fix SLI3 hba in loop mode not discovering devices
	scsi: csiostor: Don't enable IRQs too early
	powerpc/pseries: Mark accumulate_stolen_time() as notrace
	powerpc/pseries: Don't fail hash page table insert for bolted mapping
	dma-debug: add a schedule point in debug_dma_dump_mappings()
	clocksource/drivers/asm9260: Add a check for of_clk_get
	powerpc/security/book3s64: Report L1TF status in sysfs
	powerpc/book3s64/hash: Add cond_resched to avoid soft lockup warning
	jbd2: Fix statistics for the number of logged blocks
	scsi: tracing: Fix handling of TRANSFER LENGTH == 0 for READ(6) and WRITE(6)
	scsi: lpfc: Fix duplicate unreg_rpi error in port offline flow
	clk: qcom: Allow constant ratio freq tables for rcg
	irqchip/irq-bcm7038-l1: Enable parent IRQ if necessary
	irqchip: ingenic: Error out if IRQ domain creation failed
	fs/quota: handle overflows of sysctl fs.quota.* and report as unsigned long
	scsi: lpfc: fix: Coverity: lpfc_cmpl_els_rsp(): Null pointer dereferences
	scsi: ufs: fix potential bug which ends in system hang
	powerpc/pseries/cmm: Implement release() function for sysfs device
	powerpc/security: Fix wrong message when RFI Flush is disable
	scsi: atari_scsi: sun3_scsi: Set sg_tablesize to 1 instead of SG_NONE
	clk: pxa: fix one of the pxa RTC clocks
	bcache: at least try to shrink 1 node in bch_mca_scan()
	HID: Improve Windows Precision Touchpad detection.
	ext4: work around deleting a file with i_nlink == 0 safely
	scsi: pm80xx: Fix for SATA device discovery
	scsi: scsi_debug: num_tgts must be >= 0
	scsi: target: iscsi: Wait for all commands to finish before freeing a session
	gpio: mpc8xxx: Don't overwrite default irq_set_type callback
	scripts/kallsyms: fix definitely-lost memory leak
	cdrom: respect device capabilities during opening action
	perf regs: Make perf_reg_name() return "unknown" instead of NULL
	libfdt: define INT32_MAX and UINT32_MAX in libfdt_env.h
	s390/cpum_sf: Check for SDBT and SDB consistency
	ocfs2: fix passing zero to 'PTR_ERR' warning
	kernel: sysctl: make drop_caches write-only
	x86/mce: Fix possibly incorrect severity calculation on AMD
	net, sysctl: Fix compiler warning when only cBPF is present
	ALSA: hda - Downgrade error message for single-cmd fallback
	perf strbuf: Remove redundant va_end() in strbuf_addv()
	Make filldir[64]() verify the directory entry filename is valid
	filldir[64]: remove WARN_ON_ONCE() for bad directory entries
	netfilter: ebtables: compat: reject all padding in matches/watchers
	6pack,mkiss: fix possible deadlock
	netfilter: bridge: make sure to pull arp header in br_nf_forward_arp()
	net: icmp: fix data-race in cmp_global_allow()
	hrtimer: Annotate lockless access to timer->state
	tty/serial: atmel: fix out of range clock divider handling
	pinctrl: baytrail: Really serialize all register accesses
	mmc: sdhci: Update the tuning failed messages to pr_debug level
	net: ena: fix napi handler misbehavior when the napi budget is zero
	vhost/vsock: accept only packets with the right dst_cid
	tcp/dccp: fix possible race __inet_lookup_established()
	tcp: do not send empty skb from tcp_write_xmit()
	gtp: fix wrong condition in gtp_genl_dump_pdp()
	gtp: avoid zero size hashtable
	Linux 4.9.208

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2020-01-05 23:14:44 +03:00

1347 lines
34 KiB
C

/*
*
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
*
* 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; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include "pvrusb2-context.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-v4l2.h"
#include "pvrusb2-ioread.h"
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
struct pvr2_v4l2_dev;
struct pvr2_v4l2_fh;
struct pvr2_v4l2;
struct pvr2_v4l2_dev {
struct video_device devbase; /* MUST be first! */
struct pvr2_v4l2 *v4lp;
struct pvr2_context_stream *stream;
/* Information about this device: */
enum pvr2_config config; /* Expected stream format */
int v4l_type; /* V4L defined type for this device node */
enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
};
struct pvr2_v4l2_fh {
struct v4l2_fh fh;
struct pvr2_channel channel;
struct pvr2_v4l2_dev *pdi;
struct pvr2_ioread *rhp;
struct file *file;
wait_queue_head_t wait_data;
int fw_mode_flag;
/* Map contiguous ordinal value to input id */
unsigned char *input_map;
unsigned int input_cnt;
};
struct pvr2_v4l2 {
struct pvr2_channel channel;
/* streams - Note that these must be separately, individually,
* allocated pointers. This is because the v4l core is going to
* manage their deletion - separately, individually... */
struct pvr2_v4l2_dev *dev_video;
struct pvr2_v4l2_dev *dev_radio;
};
static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
module_param_array(video_nr, int, NULL, 0444);
MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
module_param_array(radio_nr, int, NULL, 0444);
MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
module_param_array(vbi_nr, int, NULL, 0444);
MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
static struct v4l2_fmtdesc pvr_fmtdesc [] = {
{
.index = 0,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = V4L2_FMT_FLAG_COMPRESSED,
.description = "MPEG1/2",
// This should really be V4L2_PIX_FMT_MPEG, but xawtv
// breaks when I do that.
.pixelformat = 0, // V4L2_PIX_FMT_MPEG,
}
};
#define PVR_FORMAT_PIX 0
#define PVR_FORMAT_VBI 1
static struct v4l2_format pvr_format [] = {
[PVR_FORMAT_PIX] = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.fmt = {
.pix = {
.width = 720,
.height = 576,
// This should really be V4L2_PIX_FMT_MPEG,
// but xawtv breaks when I do that.
.pixelformat = 0, // V4L2_PIX_FMT_MPEG,
.field = V4L2_FIELD_INTERLACED,
.bytesperline = 0, // doesn't make sense
// here
//FIXME : Don't know what to put here...
.sizeimage = (32*1024),
.colorspace = 0, // doesn't make sense here
.priv = 0
}
}
},
[PVR_FORMAT_VBI] = {
.type = V4L2_BUF_TYPE_VBI_CAPTURE,
.fmt = {
.vbi = {
.sampling_rate = 27000000,
.offset = 248,
.samples_per_line = 1443,
.sample_format = V4L2_PIX_FMT_GREY,
.start = { 0, 0 },
.count = { 0, 0 },
.flags = 0,
}
}
}
};
/*
* This is part of Video 4 Linux API. These procedures handle ioctl() calls.
*/
static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
strlcpy(cap->driver, "pvrusb2", sizeof(cap->driver));
strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
sizeof(cap->bus_info));
strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS;
switch (fh->pdi->devbase.vfl_type) {
case VFL_TYPE_GRABBER:
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO;
break;
case VFL_TYPE_RADIO:
cap->device_caps = V4L2_CAP_RADIO;
break;
}
cap->device_caps |= V4L2_CAP_TUNER | V4L2_CAP_READWRITE;
return 0;
}
static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int val = 0;
int ret;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val);
*std = val;
return ret;
}
static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
return pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std);
}
static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int val = 0;
int ret;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val);
*std = val;
return ret;
}
static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct pvr2_ctrl *cptr;
struct v4l2_input tmp;
unsigned int cnt;
int val;
cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
memset(&tmp, 0, sizeof(tmp));
tmp.index = vi->index;
if (vi->index >= fh->input_cnt)
return -EINVAL;
val = fh->input_map[vi->index];
switch (val) {
case PVR2_CVAL_INPUT_TV:
case PVR2_CVAL_INPUT_DTV:
case PVR2_CVAL_INPUT_RADIO:
tmp.type = V4L2_INPUT_TYPE_TUNER;
break;
case PVR2_CVAL_INPUT_SVIDEO:
case PVR2_CVAL_INPUT_COMPOSITE:
tmp.type = V4L2_INPUT_TYPE_CAMERA;
break;
default:
return -EINVAL;
}
cnt = 0;
pvr2_ctrl_get_valname(cptr, val,
tmp.name, sizeof(tmp.name) - 1, &cnt);
tmp.name[cnt] = 0;
/* Don't bother with audioset, since this driver currently
always switches the audio whenever the video is
switched. */
/* Handling std is a tougher problem. It doesn't make
sense in cases where a device might be multi-standard.
We could just copy out the current value for the
standard, but it can change over time. For now just
leave it zero. */
*vi = tmp;
return 0;
}
static int pvr2_g_input(struct file *file, void *priv, unsigned int *i)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
unsigned int idx;
struct pvr2_ctrl *cptr;
int val;
int ret;
cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
val = 0;
ret = pvr2_ctrl_get_value(cptr, &val);
*i = 0;
for (idx = 0; idx < fh->input_cnt; idx++) {
if (fh->input_map[idx] == val) {
*i = idx;
break;
}
}
return ret;
}
static int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
if (inp >= fh->input_cnt)
return -EINVAL;
return pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
fh->input_map[inp]);
}
static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
{
/* pkt: FIXME: We are returning one "fake" input here
which could very well be called "whatever_we_like".
This is for apps that want to see an audio input
just to feel comfortable, as well as to test if
it can do stereo or sth. There is actually no guarantee
that the actual audio input cannot change behind the app's
back, but most applications should not mind that either.
Hopefully, mplayer people will work with us on this (this
whole mess is to support mplayer pvr://), or Hans will come
up with a more standard way to say "we have inputs but we
don 't want you to change them independent of video" which
will sort this mess.
*/
if (vin->index > 0)
return -EINVAL;
strncpy(vin->name, "PVRUSB2 Audio", 14);
vin->capability = V4L2_AUDCAP_STEREO;
return 0;
}
static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
{
/* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
vin->index = 0;
strncpy(vin->name, "PVRUSB2 Audio", 14);
vin->capability = V4L2_AUDCAP_STEREO;
return 0;
}
static int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout)
{
if (vout->index)
return -EINVAL;
return 0;
}
static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
if (vt->index != 0)
return -EINVAL; /* Only answer for the 1st tuner */
pvr2_hdw_execute_tuner_poll(hdw);
return pvr2_hdw_get_tuner_status(hdw, vt);
}
static int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
if (vt->index != 0)
return -EINVAL;
return pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
vt->audmode);
}
static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
unsigned long fv;
struct v4l2_tuner vt;
int cur_input;
struct pvr2_ctrl *ctrlp;
int ret;
ret = pvr2_hdw_get_tuner_status(hdw, &vt);
if (ret != 0)
return ret;
ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
ret = pvr2_ctrl_get_value(ctrlp, &cur_input);
if (ret != 0)
return ret;
if (vf->type == V4L2_TUNER_RADIO) {
if (cur_input != PVR2_CVAL_INPUT_RADIO)
pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO);
} else {
if (cur_input == PVR2_CVAL_INPUT_RADIO)
pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV);
}
fv = vf->frequency;
if (vt.capability & V4L2_TUNER_CAP_LOW)
fv = (fv * 125) / 2;
else
fv = fv * 62500;
return pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
}
static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int val = 0;
int cur_input;
struct v4l2_tuner vt;
int ret;
ret = pvr2_hdw_get_tuner_status(hdw, &vt);
if (ret != 0)
return ret;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY),
&val);
if (ret != 0)
return ret;
pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
&cur_input);
if (cur_input == PVR2_CVAL_INPUT_RADIO)
vf->type = V4L2_TUNER_RADIO;
else
vf->type = V4L2_TUNER_ANALOG_TV;
if (vt.capability & V4L2_TUNER_CAP_LOW)
val = (val * 2) / 125;
else
val /= 62500;
vf->frequency = val;
return 0;
}
static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd)
{
/* Only one format is supported : mpeg.*/
if (fd->index != 0)
return -EINVAL;
memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
return 0;
}
static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int val;
memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format));
val = 0;
pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES),
&val);
vf->fmt.pix.width = val;
val = 0;
pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES),
&val);
vf->fmt.pix.height = val;
return 0;
}
static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int lmin, lmax, ldef;
struct pvr2_ctrl *hcp, *vcp;
int h = vf->fmt.pix.height;
int w = vf->fmt.pix.width;
hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
lmin = pvr2_ctrl_get_min(hcp);
lmax = pvr2_ctrl_get_max(hcp);
pvr2_ctrl_get_def(hcp, &ldef);
if (w == -1)
w = ldef;
else if (w < lmin)
w = lmin;
else if (w > lmax)
w = lmax;
lmin = pvr2_ctrl_get_min(vcp);
lmax = pvr2_ctrl_get_max(vcp);
pvr2_ctrl_get_def(vcp, &ldef);
if (h == -1)
h = ldef;
else if (h < lmin)
h = lmin;
else if (h > lmax)
h = lmax;
memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
sizeof(struct v4l2_format));
vf->fmt.pix.width = w;
vf->fmt.pix.height = h;
return 0;
}
static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct pvr2_ctrl *hcp, *vcp;
int ret = pvr2_try_fmt_vid_cap(file, fh, vf);
if (ret)
return ret;
hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
return 0;
}
static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct pvr2_v4l2_dev *pdi = fh->pdi;
int ret;
if (!fh->pdi->stream) {
/* No stream defined for this node. This means
that we're not currently allowed to stream from
this node. */
return -EPERM;
}
ret = pvr2_hdw_set_stream_type(hdw, pdi->config);
if (ret < 0)
return ret;
return pvr2_hdw_set_streaming(hdw, !0);
}
static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
if (!fh->pdi->stream) {
/* No stream defined for this node. This means
that we're not currently allowed to stream from
this node. */
return -EPERM;
}
return pvr2_hdw_set_streaming(hdw, 0);
}
static int pvr2_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *vc)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct pvr2_ctrl *cptr;
int val;
if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
cptr = pvr2_hdw_get_ctrl_nextv4l(
hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
if (cptr)
vc->id = pvr2_ctrl_get_v4lid(cptr);
} else {
cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id);
}
if (!cptr) {
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"QUERYCTRL id=0x%x not implemented here",
vc->id);
return -EINVAL;
}
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"QUERYCTRL id=0x%x mapping name=%s (%s)",
vc->id, pvr2_ctrl_get_name(cptr),
pvr2_ctrl_get_desc(cptr));
strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name));
vc->flags = pvr2_ctrl_get_v4lflags(cptr);
pvr2_ctrl_get_def(cptr, &val);
vc->default_value = val;
switch (pvr2_ctrl_get_type(cptr)) {
case pvr2_ctl_enum:
vc->type = V4L2_CTRL_TYPE_MENU;
vc->minimum = 0;
vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
vc->step = 1;
break;
case pvr2_ctl_bool:
vc->type = V4L2_CTRL_TYPE_BOOLEAN;
vc->minimum = 0;
vc->maximum = 1;
vc->step = 1;
break;
case pvr2_ctl_int:
vc->type = V4L2_CTRL_TYPE_INTEGER;
vc->minimum = pvr2_ctrl_get_min(cptr);
vc->maximum = pvr2_ctrl_get_max(cptr);
vc->step = 1;
break;
default:
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"QUERYCTRL id=0x%x name=%s not mappable",
vc->id, pvr2_ctrl_get_name(cptr));
return -EINVAL;
}
return 0;
}
static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
unsigned int cnt = 0;
int ret;
ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id),
vm->index,
vm->name, sizeof(vm->name) - 1,
&cnt);
vm->name[cnt] = 0;
return ret;
}
static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int val = 0;
int ret;
ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
&val);
vc->value = val;
return ret;
}
static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
vc->value);
}
static int pvr2_g_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctls)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct v4l2_ext_control *ctrl;
struct pvr2_ctrl *cptr;
unsigned int idx;
int val;
int ret;
ret = 0;
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
cptr = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
if (cptr) {
if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
pvr2_ctrl_get_def(cptr, &val);
else
ret = pvr2_ctrl_get_value(cptr, &val);
} else
ret = -EINVAL;
if (ret) {
ctls->error_idx = idx;
return ret;
}
/* Ensure that if read as a 64 bit value, the user
will still get a hopefully sane value */
ctrl->value64 = 0;
ctrl->value = val;
}
return 0;
}
static int pvr2_s_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctls)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct v4l2_ext_control *ctrl;
unsigned int idx;
int ret;
/* Default value cannot be changed */
if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
return -EINVAL;
ret = 0;
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id),
ctrl->value);
if (ret) {
ctls->error_idx = idx;
return ret;
}
}
return 0;
}
static int pvr2_try_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctls)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct v4l2_ext_control *ctrl;
struct pvr2_ctrl *pctl;
unsigned int idx;
/* For the moment just validate that the requested control
actually exists. */
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
if (!pctl) {
ctls->error_idx = idx;
return -EINVAL;
}
}
return 0;
}
static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int ret;
if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
ret = pvr2_hdw_get_cropcap(hdw, cap);
cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
return ret;
}
static int pvr2_g_selection(struct file *file, void *priv,
struct v4l2_selection *sel)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct v4l2_cropcap cap;
int val = 0;
int ret;
if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
if (ret != 0)
return -EINVAL;
sel->r.left = val;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
if (ret != 0)
return -EINVAL;
sel->r.top = val;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
if (ret != 0)
return -EINVAL;
sel->r.width = val;
ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
if (ret != 0)
return -EINVAL;
sel->r.height = val;
break;
case V4L2_SEL_TGT_CROP_DEFAULT:
ret = pvr2_hdw_get_cropcap(hdw, &cap);
sel->r = cap.defrect;
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
ret = pvr2_hdw_get_cropcap(hdw, &cap);
sel->r = cap.bounds;
break;
default:
return -EINVAL;
}
return ret;
}
static int pvr2_s_selection(struct file *file, void *priv,
struct v4l2_selection *sel)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
int ret;
if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
sel->r.left);
if (ret != 0)
return -EINVAL;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
sel->r.top);
if (ret != 0)
return -EINVAL;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
sel->r.width);
if (ret != 0)
return -EINVAL;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
sel->r.height);
if (ret != 0)
return -EINVAL;
return 0;
}
static int pvr2_log_status(struct file *file, void *priv)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
pvr2_hdw_trigger_module_log(hdw);
return 0;
}
static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
.vidioc_querycap = pvr2_querycap,
.vidioc_s_audio = pvr2_s_audio,
.vidioc_g_audio = pvr2_g_audio,
.vidioc_enumaudio = pvr2_enumaudio,
.vidioc_enum_input = pvr2_enum_input,
.vidioc_cropcap = pvr2_cropcap,
.vidioc_s_selection = pvr2_s_selection,
.vidioc_g_selection = pvr2_g_selection,
.vidioc_g_input = pvr2_g_input,
.vidioc_s_input = pvr2_s_input,
.vidioc_g_frequency = pvr2_g_frequency,
.vidioc_s_frequency = pvr2_s_frequency,
.vidioc_s_tuner = pvr2_s_tuner,
.vidioc_g_tuner = pvr2_g_tuner,
.vidioc_g_std = pvr2_g_std,
.vidioc_s_std = pvr2_s_std,
.vidioc_querystd = pvr2_querystd,
.vidioc_log_status = pvr2_log_status,
.vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap,
.vidioc_streamon = pvr2_streamon,
.vidioc_streamoff = pvr2_streamoff,
.vidioc_queryctrl = pvr2_queryctrl,
.vidioc_querymenu = pvr2_querymenu,
.vidioc_g_ctrl = pvr2_g_ctrl,
.vidioc_s_ctrl = pvr2_s_ctrl,
.vidioc_g_ext_ctrls = pvr2_g_ext_ctrls,
.vidioc_s_ext_ctrls = pvr2_s_ext_ctrls,
.vidioc_try_ext_ctrls = pvr2_try_ext_ctrls,
};
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
{
struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
enum pvr2_config cfg = dip->config;
char msg[80];
unsigned int mcnt;
/* Construct the unregistration message *before* we actually
perform the unregistration step. By doing it this way we don't
have to worry about potentially touching deleted resources. */
mcnt = scnprintf(msg, sizeof(msg) - 1,
"pvrusb2: unregistered device %s [%s]",
video_device_node_name(&dip->devbase),
pvr2_config_get_name(cfg));
msg[mcnt] = 0;
pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
/* Paranoia */
dip->v4lp = NULL;
dip->stream = NULL;
/* Actual deallocation happens later when all internal references
are gone. */
video_unregister_device(&dip->devbase);
printk(KERN_INFO "%s\n", msg);
}
static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
{
if (!dip) return;
if (!dip->devbase.v4l2_dev->dev) return;
dip->devbase.v4l2_dev->dev = NULL;
device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
}
static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
{
if (vp->dev_video) {
pvr2_v4l2_dev_destroy(vp->dev_video);
vp->dev_video = NULL;
}
if (vp->dev_radio) {
pvr2_v4l2_dev_destroy(vp->dev_radio);
vp->dev_radio = NULL;
}
pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
pvr2_channel_done(&vp->channel);
kfree(vp);
}
static void pvr2_video_device_release(struct video_device *vdev)
{
struct pvr2_v4l2_dev *dev;
dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
kfree(dev);
}
static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
{
struct pvr2_v4l2 *vp;
vp = container_of(chp,struct pvr2_v4l2,channel);
if (!vp->channel.mc_head->disconnect_flag) return;
pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
if (!list_empty(&vp->dev_video->devbase.fh_list) ||
(vp->dev_radio &&
!list_empty(&vp->dev_radio->devbase.fh_list))) {
pvr2_trace(PVR2_TRACE_STRUCT,
"pvr2_v4l2 internal_check exit-empty id=%p", vp);
return;
}
pvr2_v4l2_destroy_no_lock(vp);
}
static long pvr2_v4l2_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
long ret = -EINVAL;
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
if (!pvr2_hdw_dev_ok(hdw)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"ioctl failed - bad or no context");
return -EFAULT;
}
ret = video_ioctl2(file, cmd, arg);
pvr2_hdw_commit_ctl(hdw);
if (ret < 0) {
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"pvr2_v4l2_do_ioctl failure, ret=%ld"
" command was:", ret);
v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
}
} else {
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
ret, ret);
}
return ret;
}
static int pvr2_v4l2_release(struct file *file)
{
struct pvr2_v4l2_fh *fhp = file->private_data;
struct pvr2_v4l2 *vp = fhp->pdi->v4lp;
struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
if (fhp->rhp) {
struct pvr2_stream *sp;
pvr2_hdw_set_streaming(hdw,0);
sp = pvr2_ioread_get_stream(fhp->rhp);
if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
pvr2_ioread_destroy(fhp->rhp);
fhp->rhp = NULL;
}
v4l2_fh_del(&fhp->fh);
v4l2_fh_exit(&fhp->fh);
file->private_data = NULL;
pvr2_channel_done(&fhp->channel);
pvr2_trace(PVR2_TRACE_STRUCT,
"Destroying pvr_v4l2_fh id=%p",fhp);
if (fhp->input_map) {
kfree(fhp->input_map);
fhp->input_map = NULL;
}
kfree(fhp);
if (vp->channel.mc_head->disconnect_flag &&
list_empty(&vp->dev_video->devbase.fh_list) &&
(!vp->dev_radio ||
list_empty(&vp->dev_radio->devbase.fh_list))) {
pvr2_v4l2_destroy_no_lock(vp);
}
return 0;
}
static int pvr2_v4l2_open(struct file *file)
{
struct pvr2_v4l2_dev *dip; /* Our own context pointer */
struct pvr2_v4l2_fh *fhp;
struct pvr2_v4l2 *vp;
struct pvr2_hdw *hdw;
unsigned int input_mask = 0;
unsigned int input_cnt,idx;
int ret = 0;
dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
vp = dip->v4lp;
hdw = vp->channel.hdw;
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
if (!pvr2_hdw_dev_ok(hdw)) {
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
"pvr2_v4l2_open: hardware not ready");
return -EIO;
}
fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
if (!fhp) {
return -ENOMEM;
}
v4l2_fh_init(&fhp->fh, &dip->devbase);
init_waitqueue_head(&fhp->wait_data);
fhp->pdi = dip;
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
if (dip->v4l_type == VFL_TYPE_RADIO) {
/* Opening device as a radio, legal input selection subset
is just the radio. */
input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
} else {
/* Opening the main V4L device, legal input selection
subset includes all analog inputs. */
input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
(1 << PVR2_CVAL_INPUT_TV) |
(1 << PVR2_CVAL_INPUT_COMPOSITE) |
(1 << PVR2_CVAL_INPUT_SVIDEO));
}
ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
if (ret) {
pvr2_channel_done(&fhp->channel);
pvr2_trace(PVR2_TRACE_STRUCT,
"Destroying pvr_v4l2_fh id=%p (input mask error)",
fhp);
kfree(fhp);
return ret;
}
input_mask &= pvr2_hdw_get_input_available(hdw);
input_cnt = 0;
for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
if (input_mask & (1 << idx)) input_cnt++;
}
fhp->input_cnt = input_cnt;
fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
if (!fhp->input_map) {
pvr2_channel_done(&fhp->channel);
pvr2_trace(PVR2_TRACE_STRUCT,
"Destroying pvr_v4l2_fh id=%p (input map failure)",
fhp);
kfree(fhp);
return -ENOMEM;
}
input_cnt = 0;
for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
if (!(input_mask & (1 << idx))) continue;
fhp->input_map[input_cnt++] = idx;
}
fhp->file = file;
file->private_data = fhp;
fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
v4l2_fh_add(&fhp->fh);
return 0;
}
static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
{
wake_up(&fhp->wait_data);
}
static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
{
int ret;
struct pvr2_stream *sp;
struct pvr2_hdw *hdw;
if (fh->rhp) return 0;
if (!fh->pdi->stream) {
/* No stream defined for this node. This means that we're
not currently allowed to stream from this node. */
return -EPERM;
}
/* First read() attempt. Try to claim the stream and start
it... */
if ((ret = pvr2_channel_claim_stream(&fh->channel,
fh->pdi->stream)) != 0) {
/* Someone else must already have it */
return ret;
}
fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
if (!fh->rhp) {
pvr2_channel_claim_stream(&fh->channel,NULL);
return -ENOMEM;
}
hdw = fh->channel.mc_head->hdw;
sp = fh->pdi->stream->stream;
pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
return pvr2_ioread_set_enabled(fh->rhp,!0);
}
static ssize_t pvr2_v4l2_read(struct file *file,
char __user *buff, size_t count, loff_t *ppos)
{
struct pvr2_v4l2_fh *fh = file->private_data;
int ret;
if (fh->fw_mode_flag) {
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
char *tbuf;
int c1,c2;
int tcnt = 0;
unsigned int offs = *ppos;
tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
if (!tbuf) return -ENOMEM;
while (count) {
c1 = count;
if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
if (c2 < 0) {
tcnt = c2;
break;
}
if (!c2) break;
if (copy_to_user(buff,tbuf,c2)) {
tcnt = -EFAULT;
break;
}
offs += c2;
tcnt += c2;
buff += c2;
count -= c2;
*ppos += c2;
}
kfree(tbuf);
return tcnt;
}
if (!fh->rhp) {
ret = pvr2_v4l2_iosetup(fh);
if (ret) {
return ret;
}
}
for (;;) {
ret = pvr2_ioread_read(fh->rhp,buff,count);
if (ret >= 0) break;
if (ret != -EAGAIN) break;
if (file->f_flags & O_NONBLOCK) break;
/* Doing blocking I/O. Wait here. */
ret = wait_event_interruptible(
fh->wait_data,
pvr2_ioread_avail(fh->rhp) >= 0);
if (ret < 0) break;
}
return ret;
}
static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
struct pvr2_v4l2_fh *fh = file->private_data;
int ret;
if (fh->fw_mode_flag) {
mask |= POLLIN | POLLRDNORM;
return mask;
}
if (!fh->rhp) {
ret = pvr2_v4l2_iosetup(fh);
if (ret) return POLLERR;
}
poll_wait(file,&fh->wait_data,wait);
if (pvr2_ioread_avail(fh->rhp) >= 0) {
mask |= POLLIN | POLLRDNORM;
}
return mask;
}
static const struct v4l2_file_operations vdev_fops = {
.owner = THIS_MODULE,
.open = pvr2_v4l2_open,
.release = pvr2_v4l2_release,
.read = pvr2_v4l2_read,
.unlocked_ioctl = pvr2_v4l2_ioctl,
.poll = pvr2_v4l2_poll,
};
static struct video_device vdev_template = {
.fops = &vdev_fops,
};
static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
struct pvr2_v4l2 *vp,
int v4l_type)
{
int mindevnum;
int unit_number;
struct pvr2_hdw *hdw;
int *nr_ptr = NULL;
dip->v4lp = vp;
hdw = vp->channel.mc_head->hdw;
dip->v4l_type = v4l_type;
switch (v4l_type) {
case VFL_TYPE_GRABBER:
dip->stream = &vp->channel.mc_head->video_stream;
dip->config = pvr2_config_mpeg;
dip->minor_type = pvr2_v4l_type_video;
nr_ptr = video_nr;
if (!dip->stream) {
pr_err(KBUILD_MODNAME
": Failed to set up pvrusb2 v4l video dev"
" due to missing stream instance\n");
return;
}
break;
case VFL_TYPE_VBI:
dip->config = pvr2_config_vbi;
dip->minor_type = pvr2_v4l_type_vbi;
nr_ptr = vbi_nr;
break;
case VFL_TYPE_RADIO:
dip->stream = &vp->channel.mc_head->video_stream;
dip->config = pvr2_config_mpeg;
dip->minor_type = pvr2_v4l_type_radio;
nr_ptr = radio_nr;
break;
default:
/* Bail out (this should be impossible) */
pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
" due to unrecognized config\n");
return;
}
dip->devbase = vdev_template;
dip->devbase.release = pvr2_video_device_release;
dip->devbase.ioctl_ops = &pvr2_ioctl_ops;
{
int val;
pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw,
PVR2_CID_STDAVAIL), &val);
dip->devbase.tvnorms = (v4l2_std_id)val;
}
mindevnum = -1;
unit_number = pvr2_hdw_get_unit_number(hdw);
if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
mindevnum = nr_ptr[unit_number];
}
pvr2_hdw_set_v4l2_dev(hdw, &dip->devbase);
if ((video_register_device(&dip->devbase,
dip->v4l_type, mindevnum) < 0) &&
(video_register_device(&dip->devbase,
dip->v4l_type, -1) < 0)) {
pr_err(KBUILD_MODNAME
": Failed to register pvrusb2 v4l device\n");
}
printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
video_device_node_name(&dip->devbase),
pvr2_config_get_name(dip->config));
pvr2_hdw_v4l_store_minor_number(hdw,
dip->minor_type,dip->devbase.minor);
}
struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
{
struct pvr2_v4l2 *vp;
vp = kzalloc(sizeof(*vp),GFP_KERNEL);
if (!vp) return vp;
pvr2_channel_init(&vp->channel,mnp);
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
vp->channel.check_func = pvr2_v4l2_internal_check;
/* register streams */
vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
if (!vp->dev_video) goto fail;
pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
(1 << PVR2_CVAL_INPUT_RADIO)) {
vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
if (!vp->dev_radio) goto fail;
pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
}
return vp;
fail:
pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
pvr2_v4l2_destroy_no_lock(vp);
return NULL;
}