summaryrefslogtreecommitdiff
path: root/v4l_experimental
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <devnull@localhost>2005-11-14 13:31:23 +0000
committerMauro Carvalho Chehab <devnull@localhost>2005-11-14 13:31:23 +0000
commitfcc4a84bda737ed0f6b3511d3206b927d0d8f115 (patch)
tree449c045aadbf0b43f402b6bfd6e05982e81aab0c /v4l_experimental
parentaf56f460fb2b3169685752f928993355a42573e3 (diff)
downloadmediapointer-dvb-s2-fcc4a84bda737ed0f6b3511d3206b927d0d8f115.tar.gz
mediapointer-dvb-s2-fcc4a84bda737ed0f6b3511d3206b927d0d8f115.tar.bz2
Added experimental branch for pvrusb2
From: Mike Isely at pobox <isely@pobox.com> - Added experimental branch for pvrusb2 Signed-off-by: Mike Isely at pobox <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
Diffstat (limited to 'v4l_experimental')
-rw-r--r--v4l_experimental/pvrusb2/Kbuild29
-rw-r--r--v4l_experimental/pvrusb2/Makefile57
-rw-r--r--v4l_experimental/pvrusb2/README191
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-audio.c230
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-audio.h52
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-context.c195
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-context.h89
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-debug.h63
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-debugifc.c480
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-debugifc.h53
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-eeprom.c329
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-eeprom.h41
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-encoder.c570
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-encoder.h42
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h159
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-hdw.c2293
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-hdw.h415
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-i2c.c441
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-i2c.h44
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-io.c682
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-io.h101
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-ioread.c357
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-ioread.h47
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-main.c177
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-sysfs.c800
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-sysfs.h47
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-tuner.c154
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-tuner.h41
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-util.h63
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-v4l2.c1302
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-v4l2.h40
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-version.h4
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-video.c216
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-video.h55
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2.h43
35 files changed, 9902 insertions, 0 deletions
diff --git a/v4l_experimental/pvrusb2/Kbuild b/v4l_experimental/pvrusb2/Kbuild
new file mode 100644
index 000000000..3e1c3d72e
--- /dev/null
+++ b/v4l_experimental/pvrusb2/Kbuild
@@ -0,0 +1,29 @@
+
+pvrusb2-objs := \
+ pvrusb2-audio.o \
+ pvrusb2-encoder.o \
+ pvrusb2-video.o \
+ pvrusb2-eeprom.o \
+ pvrusb2-tuner.o \
+ pvrusb2-i2c.o \
+ pvrusb2-main.o \
+ pvrusb2-hdw.o \
+ pvrusb2-v4l2.o \
+ pvrusb2-sysfs.o \
+ pvrusb2-context.o \
+ pvrusb2-io.o \
+ pvrusb2-ioread.o \
+ pvrusb2-debugifc.o \
+ $(END)
+
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
+
+# Stuff for Emacs to see, in order to encourage consistent editing style:
+# *** Local Variables: ***
+# *** mode: Makefile ***
+# *** fill-column: 75 ***
+# *** indent-tabs-mode: nil ***
+# *** End: ***
+
+# Similarly for vim to see:
+# vim:expandtab:textwidth=75
diff --git a/v4l_experimental/pvrusb2/Makefile b/v4l_experimental/pvrusb2/Makefile
new file mode 100644
index 000000000..720bf695c
--- /dev/null
+++ b/v4l_experimental/pvrusb2/Makefile
@@ -0,0 +1,57 @@
+
+# Mike Isely <isely@pobox.com>
+
+# This is the module build file for pvrusb2. It requires the kbuild
+# system from any 2.6.x kernel. It requires the kbuild system from
+# any 2.6.x kernel (but it's only been tried against kernels 2.6.10
+# and later). This WILL NOT BUILD for 2.4.x kernels. Don't even
+# bother trying. Even if you were to fix this build for 2.4.x, you
+# would still have to port the driver as well. Everything here
+# assumes 2.6.x.
+
+# To build, you can just run this Makefile. There are several
+# variables you may want to override however:
+
+# KDIR - Path to kernel source tree
+# KREL - Version of kernel, i.e. 'uname -r' output
+# INSTALL_MOD_DIR - where within the module tree to put the driver
+
+# If you do not override anything, then KREL is set to the result of
+# 'uname -r', KDIR is set to '/lib/modules/$(KREL)/build', and
+# INSTALL_MOD_DIR is set to 'pvrusb2'. If you choose to override
+# KDIR, then you do _NOT_ need to worry about KREL, as KREL is only
+# used here when calculating KDIR. If the default path for KDIR is
+# only wrong in terms of version element, then you can just override
+# KREL with the corrected value.
+
+# Sensible build targets include 'modules' (same as no target),
+# 'install', and 'clean'
+
+ifeq ($(KERNELRELEASE),)
+
+ # Override any of these if you'd like
+ ifeq ($(strip $(KREL)),)
+ KREL := $(shell uname -r)
+ endif
+ ifeq ($(strip $(KDIR)),)
+ KDIR := /lib/modules/$(KREL)/build
+ endif
+ INSTALL_MOD_DIR := pvrusb2
+
+ .PHONY: all default install clean modules
+ default: all
+ all: modules
+
+ modules modules_install clean:
+ $(MAKE) INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) -C $(KDIR) M=$(shell pwd) CONFIG_VIDEO_PVRUSB2=m $@
+
+ install:
+ $(MAKE) INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) -C $(KDIR) M=$(shell pwd) CONFIG_VIDEO_PVRUSB2=m modules_install
+
+else
+
+ # Backwards compatibility in case kbuild can't find Kbuild on its own.
+ include Kbuild
+
+endif
+
diff --git a/v4l_experimental/pvrusb2/README b/v4l_experimental/pvrusb2/README
new file mode 100644
index 000000000..dc93b7750
--- /dev/null
+++ b/v4l_experimental/pvrusb2/README
@@ -0,0 +1,191 @@
+
+$Id: README,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+Mike Isely <isely@pobox.com>
+
+ pvrusb2 driver
+
+Background:
+
+ This driver is intended for the "Hauppauge WinTV PVR USB 2.0", which
+ is a USB 2.0 hosted TV Tuner. This driver is a work in progress.
+ Its history started with the reverse-engineering effort by Björn
+ Danielsson <pvrusb2@dax.nu> whose web page can be found here:
+
+ http://pvrusb2.dax.nu/
+
+ From there Aurelien Alleaume <slts@free.fr> began an effort to
+ create a video4linux compatible driver. I began with Aurelien's
+ last known snapshot and evolved the driver to the state it is in
+ here.
+
+ More information on this driver can be found at:
+
+ http://www.isely.net/pvrusb2.html
+
+
+ This driver has a strong separation of layers. They are very
+ roughly:
+
+ 1a. Low level wire-protocol implementation with the device.
+
+ 1b. I2C adaptor implementation and corresponding I2C client drivers
+ implemented elsewhere in V4L.
+
+ 1c. High level hardware driver implementation which coordinates all
+ activities that ensure correct operation of the device.
+
+ 2. A "context" layer which manages instancing of driver, setup,
+ tear-down, arbitration, and interaction with high level
+ interfaces appropriately as devices are hotplugged in the
+ system.
+
+ 3. High level interfaces which glue the driver to various published
+ Linux APIs (V4L, sysfs, maybe DVB in the future).
+
+ The most important shearing layer is between the top 2 layers. A
+ lot of work went into the driver to ensure that any kind of
+ conceivable API can be laid on top of the core driver. (Yes, the
+ driver internally leverages V4L to do its work but that really has
+ nothing to do with the API published by the driver to the outside
+ world.) The architecture allows for different APIs to
+ simultaneously access the driver. I have a strong sense of fairness
+ about APIs and also feel that it is a good design principle to keep
+ implementation and interface isolated from each other. Thus while
+ right now the V4L high level interface is the most complete, the
+ sysfs high level interface will work equally well for similar
+ functions, and there's no reason I see right now why it shouldn't be
+ possible to produce a DVB high level interface that can sit right
+ alongside V4L.
+
+ NOTE: Complete documentation on the pvrusb2 driver is contained in
+ the html files within the doc directory; these are exactly the same
+ as what is on the web site at the time. Browse those files
+ (especially the FAQ) before asking questions.
+
+
+Building
+
+ To build these modules essentially amounts to just running "Make",
+ but you need the kernel source tree nearby and you will likely also
+ want to set a few controlling environment variables first in order
+ to link things up with that source tree. Please see the Makefile
+ here for comments that explain how to do that.
+
+
+Source file list / functional overview:
+
+ (Note: The term "module" used below generally refers to loosely
+ defined functional units within the pvrusb2 driver and bears no
+ relation to the Linux kernel's concept of a loadable module.)
+
+ pvrusb2-audio.[ch] - This is glue logic that resides between this
+ driver and the msp3400.ko I2C client driver (which is found
+ elsewhere in V4L).
+
+ pvrusb2-context.[ch] - This module implements the context for an
+ instance of the driver. Everything else eventually ties back to
+ or is otherwise instanced within the data structures implemented
+ here. Hotplugging is ultimately coordinated here. All high level
+ interfaces tie into the driver through this module. This module
+ helps arbitrate each interface's access to the actual driver core,
+ and is designed to allow concurrent access through multiple
+ instances of multiple interfaces (thus you can for example change
+ the tuner's frequency through sysfs while simultaneously streaming
+ video through V4L out to an instance of mplayer).
+
+ pvrusb2-debug.h - This header defines a printk() wrapper and a mask
+ of debugging bit definitions for the various kinds of debug
+ messages that can be enabled within the driver.
+
+ pvrusb2-debugifc.[ch] - This module implements a crude command line
+ oriented debug interface into the driver. Aside from being part
+ of the process for implementing manual firmware extraction (see
+ the pvrusb2 web site mentioned earlier), probably I'm the only one
+ who has ever used this. It is mainly a debugging aid.
+
+ pvrusb2-eeprom.[ch] - This is glue logic that resides between this
+ driver the tveeprom.ko module, which is itself implemented
+ elsewhere in V4L.
+
+ pvrusb2-encoder.[ch] - This module implements all protocol needed to
+ interact with the Conexant mpeg2 encoder chip within the pvrusb2
+ device. It is a crude echo of corresponding logic in ivtv,
+ however the design goals (strict isolation) and physical layer
+ (proxy through USB instead of PCI) are enough different that this
+ implementation had to be completely different.
+
+ pvrusb2-hdw-internal.h - This header defines the core data structure
+ in the driver used to track ALL internal state related to control
+ of the hardware. Nobody outside of the core hardware-handling
+ modules should have any business using this header. All external
+ access to the driver should be through one of the high level
+ interfaces (e.g. V4L, sysfs, etc), and in fact even those high
+ level interfaces are restricted to the API defined in
+ pvrusb2-hdw.h and NOT this header.
+
+ pvrusb2-hdw.h - This header defines the full internal API for
+ controlling the hardware. High level interfaces (e.g. V4L, sysfs)
+ will work through here.
+
+ pvrusb2-hdw.c - This module implements all the various bits of logic
+ that handle overall control of a specific pvrusb2 device.
+ (Policy, instantiation, and arbitration of pvrusb2 devices fall
+ within the jurisdiction of pvrusb-context not here).
+
+ pvrusb2-i2c.[ch] - This module provides an implementation of a
+ kernel-friendly I2C adaptor driver, through which other external
+ I2C client drivers (e.g. msp3400, tuner, lirc) may connect and
+ operate corresponding chips within the the pvrusb2 device. It is
+ through here that other V4L modules can reach into this driver to
+ operate specific pieces (and those modules are in turn driven by
+ glue logic here which is coordinated by pvrusb2-hdw, doled out by
+ pvrusb2-context, and then ultimately made available to users
+ through one of the high level interfaces).
+
+ pvrusb2-io.[ch] - This module implements a very low level ring of
+ transfer buffers, required in order to stream data from the
+ device. This module is *very* low level. It only operates the
+ buffers and makes no attempt to define any policy or mechanism for
+ how such buffers might be used.
+
+ pvrusb2-ioread.[ch] - This module layers on top of pvrusb2-io.[ch]
+ to provide a streaming API usable by a read() system call style of
+ I/O. Right now this is the only layer on top of pvrusb2-io.[ch],
+ however the underlying architecture here was intended to allow for
+ other styles of I/O to be implemented with additonal modules, like
+ mmap()'ed buffers or something even more exotic.
+
+ pvrusb2-main.c - This is the top level of the driver. Module level
+ and USB core entry points are here. This is our "main".
+
+ pvrusb2-sysfs.[ch] - This is the high level interface which ties the
+ pvrusb2 driver into sysfs. Through this interface you can do
+ everything with the driver except actually stream data.
+
+ pvrusb2-tuner.[ch] - This is glue logic that resides between this
+ driver and the tuner.ko I2C client driver (which is found
+ elsewhere in V4L).
+
+ pvrusb2-util.h - This header defines some common macros used
+ throughout the driver. These macros are not really specific to
+ the driver, but they had to go somewhere.
+
+ pvrusb2-v4l2.[ch] - This is the high level interface which ties the
+ pvrusb2 driver into video4linux. It is through here that V4L
+ applications can open and operate the driver in the usual V4L
+ ways. Note that **ALL** V4L functionality is published only
+ through here and nowhere else.
+
+ pvrusb2-video.[ch] - This is glue logic that resides between this
+ driver and the saa711x.ko I2C client driver (which is found
+ elsewhere in V4L). Note that saa711x.ko used to be known as
+ saa7115.ko in ivtv.
+
+ pvrusb2.h - This header contains compile time tunable parameters
+ (and at the moment the driver has very little that needs to be
+ tuned).
+
+
+ -Mike Isely
+ isely@pobox.com
+
diff --git a/v4l_experimental/pvrusb2/pvrusb2-audio.c b/v4l_experimental/pvrusb2/pvrusb2-audio.c
new file mode 100644
index 000000000..ebe1f38f1
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-audio.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * $Id: pvrusb2-audio.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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 "pvrusb2-i2c.h"
+#include "pvrusb2-audio.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "msp3400.h"
+#include <linux/videodev.h>
+#include <media/audiochip.h>
+
+/*
+
+ MCI <isely@pobox.com> 10-Mar-2005 - Rather than operate the msp34xx
+ directly, we rely on the msp3400.ko module to do it for us. We
+ really have to do this because that $##@!! module is going to attach
+ itself to us anyway, so we really can't operate the chip ourselves.
+ Unfortunately msp3400.ko is a real train wreck of a piece of code.
+ Most of the code below tries to tickle that module in just the right
+ way to get the results we need. Yuck. msp3400.c should be taken
+ out back and shot. Based on my reading of the actual chip datasheet
+ it should in theory be possible to write a far cleaner and simpler
+ driver than what is currently there right now.
+
+*/
+
+static int xlat_audiomode_to_v4l2(int id)
+{
+ switch (id) {
+ case PVR2_CVAL_AUDIOMODE_MONO:
+ return V4L2_TUNER_MODE_MONO;
+ case PVR2_CVAL_AUDIOMODE_STEREO:
+ return V4L2_TUNER_MODE_STEREO;
+ case PVR2_CVAL_AUDIOMODE_SAP:
+ return V4L2_TUNER_MODE_SAP;
+ case PVR2_CVAL_AUDIOMODE_LANG1:
+ return V4L2_TUNER_MODE_LANG1;
+ case PVR2_CVAL_AUDIOMODE_LANG2:
+ return V4L2_TUNER_MODE_LANG2;
+ }
+ return V4L2_TUNER_MODE_STEREO;
+}
+
+/* Relay the video standard into the msp3400 module so that it can use
+ that information as additional clue(s) for correctly detecting
+ audio. */
+int pvr2_audio_set_standard(struct pvr2_hdw *hdw)
+{
+ struct video_channel vc;
+ memset(&vc,0,sizeof(vc));
+ switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) {
+ default:
+ case PVR2_CVAL_VIDEOSTANDARD_NTSC_M:
+ vc.norm = VIDEO_MODE_NTSC;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_SECAM_L:
+ vc.norm = VIDEO_MODE_SECAM;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_BG:
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_I:
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_DK:
+ vc.norm = VIDEO_MODE_PAL;
+ break;
+ }
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_STD;
+ return pvr2_i2c_msp3400_cmd(hdw,VIDIOCSCHAN,&vc);
+}
+
+/* This function selects the correct audio input source */
+int pvr2_audio_set_stereo(struct pvr2_hdw *hdw)
+{
+ int stat;
+ unsigned short sarg = 0;
+ struct msp_matrix mspm;
+
+ pvr2_trace(PVR2_TRACE_AUDIO,"pvr_audio_set_stereo");
+
+ if (hdw->controls[PVR2_CID_INPUT].value == PVR2_CVAL_INPUT_TV) {
+ struct v4l2_tuner vt;
+ memset(&vt,0,sizeof(vt));
+ vt.audmode = xlat_audiomode_to_v4l2(
+ hdw->controls[PVR2_CID_AUDIOMODE].value);
+ stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOC_S_TUNER,&vt);
+ if (stat < 0) return stat;
+ }
+
+ sarg = AUDIO_TUNER;
+ switch (hdw->controls[PVR2_CID_INPUT].value) {
+ case PVR2_CVAL_INPUT_TV:
+ sarg = AUDIO_TUNER;
+ break;
+ case PVR2_CVAL_INPUT_RADIO:
+ /* Assume that msp34xx also handle FM decoding, in which case
+ we're still using the tuner. */
+ sarg = AUDIO_TUNER;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ sarg = AUDIO_EXTERN;
+ break;
+ }
+ stat = pvr2_i2c_msp3400_cmd(hdw,AUDC_SET_INPUT,&sarg);
+ if (stat < 0) return stat;
+
+ /* The above should have been enough to do the job, however
+ msp3400.ko does an incomplete job of handling the scart
+ routing. Really. It doesn't even bother to initialize the
+ SC1 output at all. So we have to help it here...
+ Unfortunately this also means that we have include
+ msp3400.c's header file and it currently isn't in a public
+ place. Damnit! */
+
+ mspm.input = SCART_IN1_DA;
+ switch (hdw->controls[PVR2_CID_INPUT].value) {
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ /* Bypass the DSP and just use IN1. In theory we
+ should be able to permanent just use IN1_DA, but to
+ do that msp3400.ko should be adjusting the DSP
+ input SCART routing correctly when doing video in.
+ Unfortunately that appears not to be the case. I
+ really hate that module. */
+ mspm.input = SCART_IN1;
+ break;
+ }
+ mspm.output = 1;
+ stat = pvr2_i2c_msp3400_cmd(hdw,MSP_SET_MATRIX,&mspm);
+ if (stat < 0) return stat;
+
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_MODE;
+
+ return 0;
+}
+
+/* This sets the audio volume parameters */
+int pvr2_audio_setvolume(struct pvr2_hdw *hdw)
+{
+ struct video_audio vt;
+ memset(&vt,0,sizeof(vt));
+ vt.flags = (VIDEO_AUDIO_VOLUME |
+ VIDEO_AUDIO_BALANCE |
+ VIDEO_AUDIO_BASS |
+ VIDEO_AUDIO_TREBLE |
+ VIDEO_AUDIO_MUTABLE);
+ if (hdw->controls[PVR2_CID_MUTE].value) vt.flags |= VIDEO_AUDIO_MUTE;
+ vt.volume = hdw->controls[PVR2_CID_VOLUME].value;
+ vt.balance = hdw->controls[PVR2_CID_BALANCE].value;
+ vt.bass = hdw->controls[PVR2_CID_BASS].value;
+ vt.treble = hdw->controls[PVR2_CID_TREBLE].value;
+ pvr2_trace(PVR2_TRACE_AUDIO,
+ "pvr_audio_setvolume(vol=%d bal=%d bas=%d treb=%d mute=%d)",
+ vt.volume,vt.balance,vt.bass,vt.treble,
+ (vt.flags & VIDEO_AUDIO_MUTE) != 0);
+
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_VBBTM;
+
+ return pvr2_i2c_msp3400_cmd(hdw,VIDIOCSAUDIO,&vt);
+}
+
+/* This reads back the current volume parameters and signal type */
+int pvr2_audio_update_status(struct pvr2_hdw *hdw)
+{
+ struct video_audio vt;
+ int stat;
+
+ stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOCGAUDIO,&vt);
+ if (stat < 0) return stat;
+
+#ifdef notdef
+ if (vt.flags & VIDEO_AUDIO_MUTABLE) {
+ hdw->controls[PVR2_CID_MUTE].value =
+ (vt.flags & VIDEO_AUDIO_MUTE) ? 1 : 0;
+ }
+ if (vt.flags & VIDEO_AUDIO_VOLUME) {
+ pvr2_trace(PVR2_TRACE_AUDIO,
+ "pvr_audio_update_status: got volume");
+ hdw->controls[PVR2_CID_VOLUME].value = vt.volume;
+ }
+ if (vt.flags & VIDEO_AUDIO_BASS) {
+ pvr2_trace(PVR2_TRACE_AUDIO,
+ "pvr_audio_update_status: got bass");
+ hdw->controls[PVR2_CID_BASS].value = vt.bass;
+ }
+ if (vt.flags & VIDEO_AUDIO_TREBLE) {
+ pvr2_trace(PVR2_TRACE_AUDIO,
+ "pvr_audio_update_status: got treble");
+ hdw->controls[PVR2_CID_TREBLE].value = vt.treble;
+ }
+ if (vt.flags & (VIDEO_AUDIO_BALANCE|VIDEO_AUDIO_VOLUME)) {
+ pvr2_trace(PVR2_TRACE_AUDIO,
+ "pvr_audio_update_status: got balance");
+ hdw->controls[PVR2_CID_BALANCE].value = vt.balance;
+ }
+#endif
+
+ hdw->flag_stereo = (vt.mode & VIDEO_SOUND_STEREO) != 0;
+ hdw->flag_bilingual = (vt.mode &
+ (VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2)) != 0;
+ return 0;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-audio.h b/v4l_experimental/pvrusb2/pvrusb2-audio.h
new file mode 100644
index 000000000..3800438f7
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-audio.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * $Id: pvrusb2-audio.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_AUDIO_H
+#define __PVRUSB2_AUDIO_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device audio processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+*/
+
+struct pvr2_hdw;
+
+int pvr2_audio_setvolume(struct pvr2_hdw *);
+int pvr2_audio_set_stereo(struct pvr2_hdw *);
+int pvr2_audio_set_standard(struct pvr2_hdw *);
+int pvr2_audio_update_status(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_AUDIO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-context.c b/v4l_experimental/pvrusb2/pvrusb2-context.c
new file mode 100644
index 000000000..b0faa9a44
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-context.c
@@ -0,0 +1,195 @@
+/*
+ * $Id: pvrusb2-context.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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 "pvrusb2-context.h"
+#include "pvrusb2-io.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+
+
+static void pvr2_context_destroy(struct pvr2_context *mp)
+{
+ if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
+ flush_workqueue(mp->workqueue);
+ destroy_workqueue(mp->workqueue);
+ kfree(mp);
+}
+
+
+static void pvr2_context_setup(struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp); do {
+ (mp->kthread_ref_count)--;
+ if (!pvr2_hdw_dev_ok(mp->hdw)) break;
+ pvr2_hdw_setup(mp->hdw);
+ if (!pvr2_hdw_dev_ok(mp->hdw)) break;
+ if (!pvr2_hdw_init_ok(mp->hdw)) break;
+ mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
+ if (mp->setup_func) {
+ mp->setup_func(mp);
+ }
+ } while (0); pvr2_context_exit(mp);
+}
+
+
+struct pvr2_context *pvr2_context_create(
+ struct usb_interface *intf,void (*setup_func)(struct pvr2_context *))
+{
+ struct pvr2_context *mp = 0;
+ mp = kmalloc(sizeof(*mp),GFP_KERNEL);
+ if (!mp) goto done;
+ memset(mp,0,sizeof(*mp));
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp);
+ mp->setup_func = setup_func;
+ init_MUTEX(&mp->sem);
+ mp->hdw = pvr2_hdw_create(intf);
+ if (!mp->hdw) {
+ pvr2_context_destroy(mp);
+ mp = 0;
+ goto done;
+ }
+
+ mp->workqueue = create_singlethread_workqueue("pvrusb2");
+ (mp->kthread_ref_count)++;
+ INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp);
+ queue_work(mp->workqueue,&mp->workinit);
+ done:
+ return mp;
+}
+
+
+void pvr2_context_enter(struct pvr2_context *mp)
+{
+ down(&mp->sem);
+ pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp);
+}
+
+
+void pvr2_context_exit(struct pvr2_context *mp)
+{
+ int destroy_flag = 0;
+ if (!(mp->mc_first ||
+ (!mp->disconnect_flag) ||
+ mp->kthread_ref_count)) {
+ destroy_flag = !0;
+ }
+ pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
+ up(&mp->sem);
+ if (destroy_flag) pvr2_context_destroy(mp);
+}
+
+
+static void pvr2_context_run_checks(struct pvr2_context *mp)
+{
+ struct pvr2_channel *ch1,*ch2;
+ for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
+ ch2 = ch1->mc_next;
+ if (ch1->check_func) {
+ ch1->check_func(ch1);
+ }
+ }
+}
+
+
+void pvr2_context_disconnect(struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp); do {
+ pvr2_hdw_disconnect(mp->hdw);
+ mp->disconnect_flag = !0;
+ pvr2_context_run_checks(mp);
+ } while (0); pvr2_context_exit(mp);
+}
+
+
+void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
+{
+ cp->hdw = mp->hdw;
+ cp->mc_head = mp;
+ cp->mc_next = 0;
+ cp->mc_prev = mp->mc_last;
+ if (mp->mc_last) {
+ mp->mc_last->mc_next = cp;
+ } else {
+ mp->mc_first = cp;
+ }
+ mp->mc_last = cp;
+}
+
+
+static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
+{
+ if (!cp->stream) return;
+ pvr2_stream_kill(cp->stream->stream);
+ cp->stream->user = 0;
+ cp->stream = 0;
+}
+
+
+void pvr2_channel_done(struct pvr2_channel *cp)
+{
+ struct pvr2_context *mp = cp->mc_head;
+ pvr2_channel_disclaim_stream(cp);
+ if (cp->mc_next) {
+ cp->mc_next->mc_prev = cp->mc_prev;
+ } else {
+ mp->mc_last = cp->mc_prev;
+ }
+ if (cp->mc_prev) {
+ cp->mc_prev->mc_next = cp->mc_next;
+ } else {
+ mp->mc_first = cp->mc_next;
+ }
+ cp->hdw = 0;
+}
+
+
+int pvr2_channel_claim_stream(struct pvr2_channel *cp,
+ struct pvr2_context_stream *sp)
+{
+ int code = 0;
+ pvr2_context_enter(cp->mc_head); do {
+ if (sp == cp->stream) break;
+ if (sp->user) {
+ code = -EBUSY;
+ break;
+ }
+ pvr2_channel_disclaim_stream(cp);
+ if (!sp) break;
+ sp->user = cp;
+ cp->stream = sp;
+ } while (0); pvr2_context_exit(cp->mc_head);
+ return code;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-context.h b/v4l_experimental/pvrusb2/pvrusb2-context.h
new file mode 100644
index 000000000..230c2828f
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-context.h
@@ -0,0 +1,89 @@
+/*
+ * $Id: pvrusb2-context.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_BASE_H
+#define __PVRUSB2_BASE_H
+
+#include <asm/semaphore.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+struct pvr2_hdw; /* hardware interface - defined elsewhere */
+struct pvr2_stream; /* stream interface - defined elsewhere */
+
+struct pvr2_context; /* All central state */
+struct pvr2_channel; /* One I/O pathway to a user */
+struct pvr2_context_stream; /* Wrapper for a stream */
+struct pvr2_crit_reg; /* Critical region pointer */
+
+struct pvr2_context_stream {
+ struct pvr2_channel *user;
+ struct pvr2_stream *stream;
+};
+
+struct pvr2_context {
+ struct pvr2_channel *mc_first;
+ struct pvr2_channel *mc_last;
+ struct pvr2_hdw *hdw;
+ struct pvr2_context_stream video_stream;
+ struct semaphore sem;
+ int disconnect_flag;
+ int kthread_ref_count;
+
+ /* Called after pvr2_context initialization is complete */
+ void (*setup_func)(struct pvr2_context *);
+
+ /* Work queue overhead for out-of-line processing */
+ struct workqueue_struct *workqueue;
+ struct work_struct workinit;
+
+};
+
+struct pvr2_channel {
+ struct pvr2_context *mc_head;
+ struct pvr2_channel *mc_next;
+ struct pvr2_channel *mc_prev;
+ struct pvr2_context_stream *stream;
+ struct pvr2_hdw *hdw;
+ void (*check_func)(struct pvr2_channel *);
+};
+
+void pvr2_context_enter(struct pvr2_context *);
+void pvr2_context_exit(struct pvr2_context *);
+
+struct pvr2_context *pvr2_context_create(struct usb_interface *intf,
+ void (*setup_func)(struct pvr2_context *));
+void pvr2_context_disconnect(struct pvr2_context *);
+
+void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *);
+void pvr2_channel_done(struct pvr2_channel *);
+int pvr2_channel_claim_stream(struct pvr2_channel *,
+ struct pvr2_context_stream *);
+
+
+#endif /* __PVRUSB2_CONTEXT_H */
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-debug.h b/v4l_experimental/pvrusb2/pvrusb2-debug.h
new file mode 100644
index 000000000..f970be2c2
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-debug.h
@@ -0,0 +1,63 @@
+/*
+ * $Id: pvrusb2-debug.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEBUG_H
+#define __PVRUSB2_DEBUG_H
+
+extern int debug;
+
+#define pvr2_trace(msk, fmt, arg...) do {if(msk & debug) printk(KERN_INFO "pvrusb2 " fmt "\n", ##arg); } while (0)
+
+/* These are listed in *rough* order of decreasing usefulness and
+ increasing noise level. */
+#define PVR2_TRACE_INFO 0x00000001 // Normal messages
+#define PVR2_TRACE_ERROR_LEGS 0x00000002 // error messages
+#define PVR2_TRACE_TRAP 0x00000004 // Trap & report misbehavior from app
+#define PVR2_TRACE_INIT 0x00000008 // misc initialization steps
+#define PVR2_TRACE_START_STOP 0x00000010 // Streaming start / stop
+#define PVR2_TRACE_CTL 0x00000020 // commit of control changes
+#define PVR2_TRACE_DEBUG 0x00000040 // Temporary debug code
+#define PVR2_TRACE_EEPROM 0x00000080 // eeprom parsing / report
+#define PVR2_TRACE_STRUCT 0x00000100 // internal struct creation
+#define PVR2_TRACE_OPEN_CLOSE 0x00000200 // application open / close
+#define PVR2_TRACE_CREG 0x00000400 // Main critical region entry / exit
+#define PVR2_TRACE_SYSFS 0x00000800 // Sysfs driven I/O
+#define PVR2_TRACE_FIRMWARE 0x00001000 // firmware upload actions
+#define PVR2_TRACE_TUNER 0x00002000 // tuner operation
+#define PVR2_TRACE_I2C 0x00004000 // I2C related stuff
+#define PVR2_TRACE_V4LIOCTL 0x00008000 // v4l ioctl details
+#define PVR2_TRACE_AUDIO 0x00010000 // audio operation
+#define PVR2_TRACE_DECODER 0x00020000 // video capture operation
+#define PVR2_TRACE_ENCODER 0x00040000 // mpeg2 encoder operation
+#define PVR2_TRACE_BUF_POOL 0x00080000 // Track buffer pool management
+#define PVR2_TRACE_BUF_FLOW 0x00100000 // Track buffer flow in system
+#define PVR2_TRACE_DATA_FLOW 0x00200000 // Track data flow
+#define PVR2_TRACE_DEBUGIFC 0x00400000 // Debug interface actions
+#define PVR2_TRACE_GPIO 0x00800000 // GPIO state bit changes
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-debugifc.c b/v4l_experimental/pvrusb2/pvrusb2-debugifc.c
new file mode 100644
index 000000000..a29bafb85
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-debugifc.c
@@ -0,0 +1,480 @@
+/*
+ *
+ * $Id: pvrusb2-debugifc.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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/string.h>
+#include <linux/slab.h>
+#include "pvrusb2-debugifc.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+
+struct debugifc_mask_item {
+ const char *name;
+ unsigned long msk;
+};
+
+static struct debugifc_mask_item mask_items[] = {
+ {"ENC_FIRMWARE",PVR2_SUBSYS_ENC_FIRMWARE},
+ {"TUN_STD",PVR2_SUBSYS_TUNER_CFG_STD},
+ {"TUN_FREQ",PVR2_SUBSYS_TUNER_CFG_FREQ},
+ {"AUD_VBBTM",PVR2_SUBSYS_AUDIO_CFG_VBBTM},
+ {"AUD_STD",PVR2_SUBSYS_AUDIO_CFG_STD},
+ {"AUD_MODE",PVR2_SUBSYS_AUDIO_CFG_MODE},
+ {"DIG_NORM",PVR2_SUBSYS_DIGITIZER_CFG_NORM},
+ {"DIG_INPUT",PVR2_SUBSYS_DIGITIZER_CFG_INPUT},
+ {"DIG_SIZE",PVR2_SUBSYS_DIGITIZER_CFG_SIZE},
+ {"DIG_AUDIO",PVR2_SUBSYS_DIGITIZER_CFG_AUDIO},
+ {"DIG_BCSH",PVR2_SUBSYS_DIGITIZER_CFG_BCSH},
+ {"ENC_CFG",PVR2_SUBSYS_ENC_CFG},
+ {"DIG_RUN",PVR2_SUBSYS_DIGITIZER_RUN},
+ {"USB_RUN",PVR2_SUBSYS_USBSTREAM_RUN},
+ {"ENC_RUN",PVR2_SUBSYS_ENC_RUN},
+};
+
+
+static unsigned int debugifc_count_whitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') continue;
+ if (ch == '\t') continue;
+ if (ch == '\n') continue;
+ break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_count_nonwhitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') break;
+ if (ch == '\t') break;
+ if (ch == '\n') break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_isolate_word(const char *buf,unsigned int count,
+ const char **wstrPtr,
+ unsigned int *wlenPtr)
+{
+ const char *wptr;
+ unsigned int consume_cnt = 0;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ wptr = 0;
+ wlen = 0;
+ scnt = debugifc_count_whitespace(buf,count);
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+ if (!count) goto done;
+
+ scnt = debugifc_count_nonwhitespace(buf,count);
+ if (!scnt) goto done;
+ wptr = buf;
+ wlen = scnt;
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+
+ done:
+ *wstrPtr = wptr;
+ *wlenPtr = wlen;
+ return consume_cnt;
+}
+
+
+static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
+ u32 *num_ptr)
+{
+ u32 result = 0;
+ u32 val;
+ int ch;
+ int radix = 10;
+ if ((count >= 2) && (buf[0] == '0') &&
+ ((buf[1] == 'x') || (buf[1] == 'X'))) {
+ radix = 16;
+ count -= 2;
+ buf += 2;
+ } else if ((count >= 1) && (buf[0] == '0')) {
+ radix = 8;
+ }
+
+ while (count--) {
+ ch = *buf++;
+ if ((ch >= '0') && (ch <= '9')) {
+ val = ch - '0';
+ } else if ((ch >= 'a') && (ch <= 'f')) {
+ val = ch - 'a' + 10;
+ } else if ((ch >= 'A') && (ch <= 'F')) {
+ val = ch - 'A' + 10;
+ } else {
+ return -EINVAL;
+ }
+ if (val >= radix) return -EINVAL;
+ result *= radix;
+ result += val;
+ }
+ *num_ptr = result;
+ return 0;
+}
+
+
+static int debugifc_match_keyword(const char *buf,unsigned int count,
+ const char *keyword)
+{
+ unsigned int kl;
+ if (!keyword) return 0;
+ kl = strlen(keyword);
+ if (kl != count) return 0;
+ return !memcmp(buf,keyword,kl);
+}
+
+
+static unsigned long debugifc_find_mask(const char *buf,unsigned int count)
+{
+ struct debugifc_mask_item *mip;
+ unsigned int idx;
+ for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) {
+ mip = mask_items + idx;
+ if (debugifc_match_keyword(buf,count,mip->name)) {
+ return mip->msk;
+ }
+ }
+ return 0;
+}
+
+
+static int debugifc_print_mask(char *buf,unsigned int sz,
+ unsigned long msk,unsigned long val)
+{
+ struct debugifc_mask_item *mip;
+ unsigned int idx;
+ int bcnt = 0;
+ int ccnt;
+ for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) {
+ mip = mask_items + idx;
+ if (!(mip->msk & msk)) continue;
+ ccnt = scnprintf(buf,sz,"%s%c%s",
+ (bcnt ? " " : ""),
+ ((mip->msk & val) ? '+' : '-'),
+ mip->name);
+ sz -= ccnt;
+ buf += ccnt;
+ bcnt += ccnt;
+ }
+ return bcnt;
+}
+
+static unsigned int debugifc_parse_subsys_mask(const char *buf,
+ unsigned int count,
+ unsigned long *mskPtr,
+ unsigned long *valPtr)
+{
+ const char *wptr;
+ unsigned int consume_cnt = 0;
+ unsigned int scnt;
+ unsigned int wlen;
+ int mode;
+ unsigned long m1,msk,val;
+
+ msk = 0;
+ val = 0;
+
+ while (count) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) break;
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+ if (!wptr) break;
+
+ mode = 0;
+ if (wlen) switch (wptr[0]) {
+ case '+':
+ wptr++;
+ wlen--;
+ break;
+ case '-':
+ mode = 1;
+ wptr++;
+ wlen--;
+ break;
+ }
+ if (!wlen) continue;
+ m1 = debugifc_find_mask(wptr,wlen);
+ if (!m1) break;
+ msk |= m1;
+ if (!mode) val |= m1;
+ }
+ *mskPtr = msk;
+ *valPtr = val;
+ return consume_cnt;
+}
+
+
+int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ struct pvr2_hdw_debug_info dbg;
+
+ pvr2_hdw_get_debug_info(hdw,&dbg);
+
+ ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s",
+ (dbg.big_lock_held ? "held" : "free"),
+ (dbg.ctl_lock_held ? "held" : "free"));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ if (dbg.ctl_lock_held) {
+ ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d"
+ " cmd_wlen=%d cmd_rlen=%d"
+ " wpend=%d rpend=%d tmout=%d rstatus=%d"
+ " wstatus=%d",
+ dbg.cmd_debug_state,dbg.cmd_code,
+ dbg.cmd_debug_write_len,
+ dbg.cmd_debug_read_len,
+ dbg.cmd_debug_write_pend,
+ dbg.cmd_debug_read_pend,
+ dbg.cmd_debug_timeout,
+ dbg.cmd_debug_rstatus,
+ dbg.cmd_debug_wstatus);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(
+ buf,acnt,"driver flags: %s %s %s\n",
+ (dbg.flag_init_ok ? "initialized" : "uninitialized"),
+ (dbg.flag_ok ? "ok" : "fail"),
+ (dbg.flag_disconnected ? "disconnected" : "connected"));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+}
+
+
+int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ unsigned long msk;
+ int ret;
+ u32 gpio_dir,gpio_in,gpio_out;
+
+ ret = pvr2_hdw_is_hsm(hdw);
+ ccnt = scnprintf(buf,acnt,"USB link speed: %s\n",
+ (ret < 0 ? "FAIL" : (ret ? "high" : "full")));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ gpio_dir = 0; gpio_in = 0; gpio_out = 0;
+ pvr2_hdw_gpio_get_dir(hdw,&gpio_dir);
+ pvr2_hdw_gpio_get_out(hdw,&gpio_out);
+ pvr2_hdw_gpio_get_in(hdw,&gpio_in);
+ ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n",
+ gpio_dir,gpio_in,gpio_out);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ ccnt = scnprintf(buf,acnt,"Streaming is %s\n",
+ pvr2_hdw_get_streaming(hdw) ? "on" : "off");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ msk = pvr2_hdw_subsys_get(hdw);
+ ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,~msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ msk = pvr2_hdw_subsys_stream_get(hdw);
+ ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+}
+
+
+int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ const char *wptr;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return 0;
+ count -= scnt; buf += scnt;
+ if (!wptr) return 0;
+
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr);
+ if (debugifc_match_keyword(wptr,wlen,"reset")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"cpu")) {
+ pvr2_hdw_cpureset_assert(hdw,!0);
+ pvr2_hdw_cpureset_assert(hdw,0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"bus")) {
+ pvr2_hdw_device_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"soft")) {
+ return pvr2_hdw_cmd_soft_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"deep")) {
+ return pvr2_hdw_cmd_deep_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"firmware")) {
+ return pvr2_upload_firmware2(hdw);
+ }
+ return -EINVAL;
+ } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) {
+ unsigned long msk = 0;
+ unsigned long val = 0;
+ if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc parse error on subsys mask");
+ return -EINVAL;
+ }
+ pvr2_hdw_subsys_bit_chg(hdw,msk,val);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) {
+ unsigned long msk = 0;
+ unsigned long val = 0;
+ if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc parse error on stream mask");
+ return -EINVAL;
+ }
+ pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"fetch")) {
+ pvr2_hdw_cpufw_set_enabled(hdw,!0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"done")) {
+ pvr2_hdw_cpufw_set_enabled(hdw,0);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+ } else if (debugifc_match_keyword(wptr,wlen,"gpio")) {
+ int dir_fl = 0;
+ int ret;
+ u32 msk,val;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"dir")) {
+ dir_fl = !0;
+ } else if (!debugifc_match_keyword(wptr,wlen,"out")) {
+ return -EINVAL;
+ }
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&msk);
+ if (ret) return ret;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (wptr) {
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&val);
+ if (ret) return ret;
+ } else {
+ val = msk;
+ msk = 0xffffffff;
+ }
+ if (dir_fl) {
+ ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val);
+ } else {
+ ret = pvr2_hdw_gpio_chg_out(hdw,msk,val);
+ }
+ return ret;
+ }
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr);
+ return -EINVAL;
+}
+
+
+int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ unsigned int bcnt = 0;
+ int ret;
+
+ while (count) {
+ for (bcnt = 0; bcnt < count; bcnt++) {
+ if (buf[bcnt] == '\n') break;
+ }
+
+ ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt);
+ if (ret < 0) return ret;
+ if (bcnt < count) bcnt++;
+ buf += bcnt;
+ count -= bcnt;
+ }
+
+ return 0;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-debugifc.h b/v4l_experimental/pvrusb2/pvrusb2-debugifc.h
new file mode 100644
index 000000000..59f4373d6
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-debugifc.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * $Id: pvrusb2-debugifc.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEBUGIFC_H
+#define __PVRUSB2_DEBUGIFC_H
+
+struct pvr2_hdw;
+
+/* Non-intrusively print some useful debugging info from inside the
+ driver. This should work even if the driver appears to be
+ wedged. */
+int pvr2_debugifc_print_info(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Print general status of driver. This will also trigger a probe of
+ the USB link. Unlike print_info(), this one synchronizes with the
+ driver so the information should be self-consistent (but it will
+ hang if the driver is wedged). */
+int pvr2_debugifc_print_status(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Parse a string command into a driver action. */
+int pvr2_debugifc_docmd(struct pvr2_hdw *,
+ const char *buf_ptr,unsigned int buf_size);
+
+#endif /* __PVRUSB2_DEBUGIFC_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-eeprom.c b/v4l_experimental/pvrusb2/pvrusb2-eeprom.c
new file mode 100644
index 000000000..e83ef0db7
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-eeprom.c
@@ -0,0 +1,329 @@
+/*
+ *
+ * $Id: pvrusb2-eeprom.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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 "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "compat.h"
+
+#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
+
+/*
+
+ isely@pobox.com 16-Oct-2005 - There are two method by which we can
+ theoretically retrieve information from the device's eeprom:
+
+ Method #1: We expect tveeprom to attach to our I2C adapter as a
+ client, in which case we send it a command to tell us what it
+ knows about the device.
+
+ Method #2: We retrieve the eeprom contents ourselves and call into
+ tveeprom_hauppauge_analog() to parse the data and tell us what
+ it knows about the device.
+
+ Unfortunately it isn't perfectly clear which method is the best.
+ They each have pros and cons:
+
+ #1 is simpler & more portable and has an API which is more stable.
+
+ #1 doesn't provide as much information as #2 does. For example, we
+ can't retrieve the device's serial number with method #1.
+
+ #1 requires that tveeprom.ko autonomously detect the eeprom chip on
+ its own; we can't help it out here. Worse still, it seems that
+ the eeprom in some PVR USB2 devices (like mine) can't be detected
+ correctly (I don't see an ack on a zero length write which is
+ what the I2C core attempts).
+
+ #2 uses an unstable API. Current the ivtv implementation of #2 uses
+ a completely different tveeprom struct than the v4l
+ implementation of #2. This causes a usability nightmare.
+
+ Since I can't decide, both methods are implemented below. Method #2
+ (direct call) is the default choice, but if you want to try method
+ #1, comment out the PVRUSB2_EEPROM_DIRECT_CALL macro and cross your
+ fingers...
+
+ If you use method #1, please be aware that you won't have a serial
+ number for the device and thus the sysfs interface may be a little
+ different. In addition, if tveeprom.ko fails to detect the eeprom
+ you may have to force it using standard i2c module options (try
+ force=-1,80). FINALLY (and this may foreclose this option for you
+ completely), the PVR USB2 eeprom seems to have valid data only in
+ the upper 128 bytes - the lower 128 bytes causes tveeprom.ko to
+ abort. In method #2 we only read the upper 128 bytes...
+
+ */
+
+#define PVRUSB2_EEPROM_DIRECT_CALL
+
+
+#ifndef PVRUSB2_EEPROM_DIRECT_CALL
+
+#include "pvrusb2-i2c.h"
+
+/* Use I2C client interface to retrieve usable information from within
+ * tveeprom. This tends to be more stable than directly calling. */
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u32 props[5];
+ int stat;
+
+ stat = pvr2_i2c_tveeprom_cmd(hdw,0,props);
+ if (stat < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to retrieve eeprom data stat=%d",stat);
+ return stat;
+ }
+
+ trace_eeprom("eeprom client query results:");
+ trace_eeprom("tuner_type=%d",props[0]);
+ trace_eeprom("tuner_formats=0x%x",props[1]);
+ trace_eeprom("model=%d",props[2]);
+ trace_eeprom("revision=%d",props[3]);
+ trace_eeprom("has_radio=%d",props[4]);
+
+ hdw->tuner_type = props[0];
+ hdw->video_standards = props[1];
+
+ return 0;
+}
+
+#endif
+
+#ifdef PVRUSB2_EEPROM_DIRECT_CALL
+
+/* Directly call eeprom analysis function within tveeprom. This has
+ portability issues because the internal API has been changing. We
+ have to do something positively gross here. The two variants of
+ tveeprom that we deal with (ivtv and v4l) use completely different
+ definitions for the tveeprom structure. To accomodate this, we'll
+ allocate enough storage for the larger of the two, initialize it to
+ bad but predictable data, and then call the analysis function.
+ Upon return, we'll check how much data was changed and use that as
+ a hint to determine exactly which tveeprom structure had been
+ used. Did I say this was ugly? It's disgusting. */
+
+#define PVR_EEPROM_I2C_ADDR 0x50
+
+#include <media/tveeprom.h>
+
+/*
+ * Read and analyze data in the eeprom. Use tveeprom to figure out
+ * the packet structure, since this is another Hauppauge device and
+ * internally it has a family resemblence to ivtv-type devices
+ */
+
+/* We seem to only be interested in the back half of the EEPROM */
+#define EEPROM_SIZE 128
+#define EEPROM_OFFS 128
+
+/* This has to be an EXACT(!!) match with the tveeprom structure
+ defined in our local copy of tveeprom.c. */
+struct tveeprom_ivtv {
+ u32 has_radio;
+
+ u32 tuner_type;
+ u32 tuner_formats;
+
+ u32 digitizer;
+ u32 digitizer_formats;
+
+ u32 audio_processor;
+ u32 decoder_processor;
+ /* a_p_fmts? */
+
+ u32 model;
+ u32 revision;
+ u32 serial_number;
+ char rev_str[5];
+};
+
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u8 *eeprom;
+ u8 offs;
+ unsigned pcnt,tcnt;
+ int ret;
+ int tp;
+ struct i2c_msg msg[2];
+ union {
+ struct tveeprom v4l;
+ struct tveeprom_ivtv ivtv;
+ } tvdata;
+
+ memset(&tvdata,0x93,sizeof(tvdata));
+
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ return -ENOMEM;
+ }
+
+ msg[0].addr = PVR_EEPROM_I2C_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &offs;
+ msg[1].addr = PVR_EEPROM_I2C_ADDR;
+ msg[1].flags = I2C_M_RD;
+
+ /* We have to do the actual eeprom data fetch ourselves, because
+ (1) we're only fetching part of the eeprom, and (2) if we were
+ getting the whole thing our I2C driver can't grab it in one
+ pass - which is what tveeprom is otherwise going to attempt */
+ memset(eeprom,0,EEPROM_SIZE);
+ for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
+ pcnt = 16;
+ if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
+ offs = tcnt + EEPROM_OFFS;
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(
+ &hdw->i2c_adap,
+ msg,sizeof(msg)/sizeof(msg[0]))) != 2) {
+ trace_eeprom("eeprom fetch set offs err=%d",ret);
+ if (ret > 0) ret = -EIO;
+ kfree(eeprom);
+ return ret;
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+ {
+ struct i2c_client fake_client;
+ /* Newer version expects a useless client interface */
+ fake_client.addr = PVR_EEPROM_I2C_ADDR;
+ fake_client.adapter = &hdw->i2c_adap;
+ tveeprom_hauppauge_analog(&fake_client,
+ (struct tveeprom *)&tvdata,eeprom);
+ }
+#else
+ tveeprom_hauppauge_analog((struct tveeprom *)&tvdata,eeprom);
+#endif
+
+ /* Now figure out which structure was used */
+ for (tcnt = 0; tcnt < sizeof(tvdata); tcnt++) {
+ if (((unsigned char *)(&tvdata))[sizeof(tvdata) - (tcnt+1)] !=
+ 0x93) {
+ break;
+ }
+ }
+ tcnt = sizeof(tvdata) - tcnt;
+
+ if (sizeof(tvdata.ivtv) < sizeof(tvdata.v4l)) {
+ tp = 0;
+ if (tcnt > sizeof(tvdata.ivtv)) tp = 1;
+ } else {
+ tp = 1;
+ if (tcnt > sizeof(tvdata.v4l)) tp = 0;
+ }
+
+ if (tp) {
+ /* v4l */
+ trace_eeprom("eeprom detected v4l tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.v4l.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.v4l.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.v4l.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.v4l.audio_processor);
+ trace_eeprom("model=%d",tvdata.v4l.model);
+ trace_eeprom("revision=%d",tvdata.v4l.revision);
+ trace_eeprom("serial_number=%d",tvdata.v4l.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.v4l.rev_str);
+ hdw->tuner_type = tvdata.v4l.tuner_type;
+ hdw->serial_number = tvdata.v4l.serial_number;
+ hdw->video_standards = tvdata.v4l.tuner_formats;
+ } else {
+ /* ivtv */
+ trace_eeprom("eeprom detected ivtv tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.ivtv.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.ivtv.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.ivtv.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.ivtv.audio_processor);
+ trace_eeprom("model=%d",tvdata.ivtv.model);
+ trace_eeprom("revision=%d",tvdata.ivtv.revision);
+ trace_eeprom("serial_number=%d",tvdata.ivtv.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.ivtv.rev_str);
+ hdw->tuner_type = tvdata.ivtv.tuner_type;
+ hdw->serial_number = tvdata.ivtv.serial_number;
+ hdw->video_standards = tvdata.ivtv.tuner_formats;
+ }
+
+ kfree(eeprom);
+
+ return 0;
+}
+#endif
+
+
+static v4l2_std_id std_choices[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = V4L2_STD_NTSC,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = V4L2_STD_PAL_BG,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = V4L2_STD_PAL_I,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = V4L2_STD_PAL_DK,
+ [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = V4L2_STD_SECAM_L,
+};
+
+void pvr2_eeprom_set_default_standard(struct pvr2_hdw *hdw)
+{
+ int vstd_value = 0;
+ int vstd_found = 0;
+ unsigned int idx;
+ v4l2_std_id vs = (v4l2_std_id)hdw->video_standards;
+
+ for (idx = 0; idx < sizeof(std_choices)/sizeof(std_choices[0]);
+ idx++) {
+ if (!(vs & std_choices[idx])) continue;
+ trace_eeprom("Detected video standard %s (from eeprom)",
+ pvr2_hdw_get_ctl_value_name(
+ hdw,PVR2_CID_VIDEOSTANDARD,idx));
+ if (vstd_found) continue;
+ vstd_value = idx;
+ vstd_found = !0;
+ }
+
+ if (!vstd_found) {
+ trace_eeprom("eeprom unable to recognize"
+ " a known video standard");
+ return;
+ }
+
+ trace_eeprom("Setting initial video standard to %s"
+ " (detected from eeprom)",
+ pvr2_hdw_get_ctl_value_name(hdw,
+ PVR2_CID_VIDEOSTANDARD,
+ vstd_value));
+ pvr2_hdw_set_ctl_value_internal(hdw,PVR2_CID_VIDEOSTANDARD,vstd_value);
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-eeprom.h b/v4l_experimental/pvrusb2/pvrusb2-eeprom.h
new file mode 100644
index 000000000..edc80b62e
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-eeprom.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * $Id: pvrusb2-eeprom.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_EEPROM_H
+#define __PVRUSB2_EEPROM_H
+
+struct pvr2_hdw;
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *);
+void pvr2_eeprom_set_default_standard(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_EEPROM_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-encoder.c b/v4l_experimental/pvrusb2/pvrusb2-encoder.c
new file mode 100644
index 000000000..450d14209
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-encoder.c
@@ -0,0 +1,570 @@
+/*
+ *
+ * $Id: pvrusb2-encoder.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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/device.h> // for linux/firmware.h
+#include <linux/firmware.h>
+#include "pvrusb2-util.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-i2c.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+
+static u32 pvr_tbl_emphasis [] = {
+ [PVR2_CVAL_AUDIOEMPHASIS_NONE] = 0x0 << 12,
+ [PVR2_CVAL_AUDIOEMPHASIS_50_15] = 0x1 << 12,
+ [PVR2_CVAL_AUDIOEMPHASIS_CCITT] = 0x3 << 12,
+};
+
+static u32 pvr_tbl_srate[] = {
+ [PVR2_CVAL_SRATE_48] = 0x01,
+ [PVR2_CVAL_SRATE_44_1] = 0x00,
+};
+
+static u32 pvr_tbl_audiobitrate[] = {
+ [PVR2_CVAL_AUDIOBITRATE_384] = 0xe << 4,
+ [PVR2_CVAL_AUDIOBITRATE_320] = 0xd << 4,
+ [PVR2_CVAL_AUDIOBITRATE_256] = 0xc << 4,
+ [PVR2_CVAL_AUDIOBITRATE_224] = 0xb << 4,
+ [PVR2_CVAL_AUDIOBITRATE_192] = 0xa << 4,
+ [PVR2_CVAL_AUDIOBITRATE_160] = 0x9 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_128] = 0x8 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_112] = 0x7 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_96] = 0x6 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_80] = 0x5 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_64] = 0x4 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_56] = 0x3 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_48] = 0x2 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_32] = 0x1 << 4,
+ [PVR2_CVAL_AUDIOBITRATE_VBR] = 0x0 << 4,
+};
+
+/* Firmware API commands - definitions found from ivtv */
+#ifdef notdef
+#define IVTV_API_DEC_PING_FW 0x00
+#define IVTV_API_DEC_START_PLAYBACK 0x01
+#define IVTV_API_DEC_STOP_PLAYBACK 0x02
+#define IVTV_API_DEC_PLAYBACK_SPEED 0x03
+#define IVTV_API_DEC_STEP_VIDEO 0x05
+#define IVTV_API_DEC_DMA_BLOCKSIZE 0x08
+#define IVTV_API_DEC_XFER_INFO 0x09
+#define IVTV_API_DEC_DMA_STATUS 0x0a
+#define IVTV_API_DEC_DMA_FROM_HOST 0x0b
+#define IVTV_API_DEC_PAUSE_PLAYBACK 0x0d
+#define IVTV_API_DEC_HALT_FW 0x0f
+
+#define IVTV_API_DEC_DISP_STANDARD 0x10
+#define IVTV_API_DEC_GETVER 0x11
+#define IVTV_API_DEC_STREAM_INPUT 0x14
+#define IVTV_API_DEC_TIMING_INFO 0x15
+#define IVTV_API_DEC_SELECT_AUDIO 0x16
+#define IVTV_API_DEC_EVENT_NOTIFICATION 0x17
+#define IVTV_API_DEC_DISPLAY_BUFFERS 0x18
+#define IVTV_API_DEC_EXTRACT_VBI 0x19
+#define IVTV_API_DEC_DECODE_SOURCE 0x1a
+#define IVTV_API_DEC_AUDIO_OUTPUT 0x1b
+#define IVTV_API_DEC_SET_AV_DELAY 0x1c
+#define IVTV_API_DEC_BUFFER 0x1e
+
+#define IVTV_API_FB_GET_FRAMEBUFFER 0x41
+#define IVTV_API_FB_GET_PIXEL_FORMAT 0x42
+#define IVTV_API_FB_SET_PIXEL_FORMAT 0x43
+#define IVTV_API_FB_GET_STATE 0x44
+#define IVTV_API_FB_SET_STATE 0x45
+#define IVTV_API_FB_GET_OSD_COORDS 0x46
+#define IVTV_API_FB_SET_OSD_COORDS 0x47
+#define IVTV_API_FB_GET_SCREEN_COORDS 0x48
+#define IVTV_API_FB_SET_SCREEN_COORDS 0x49
+#define IVTV_API_FB_GET_GLOBAL_ALPHA 0x4a
+#define IVTV_API_FB_SET_GLOBAL_ALPHA 0x4b
+#define IVTV_API_FB_SET_BLEND_COORDS 0x4c
+#define IVTV_API_FB_GET_FLICKER_STATE 0x4f
+
+#define IVTV_API_FB_SET_FLICKER_STATE 0x50
+#define IVTV_API_FB_BLT_COPY 0x52
+#define IVTV_API_FB_BLT_FILL 0x53
+#define IVTV_API_FB_BLT_TEXT 0x54
+#define IVTV_API_FB_SET_FRAMEBUFFER_WINDOW 0x56
+
+#define IVTV_API_FB_SET_CHROMA_KEY 0x60
+#define IVTV_API_FB_GET_ALPHA_CONTENT_INDEX 0x61
+#define IVTV_API_FB_SET_ALPHA_CONTENT_INDEX 0x62
+#endif
+
+#define IVTV_API_ENC_PING_FW 0x80
+#define IVTV_API_BEGIN_CAPTURE 0x81
+#define IVTV_API_END_CAPTURE 0x82
+#define IVTV_API_ASSIGN_FRAMERATE 0x8f
+
+#define IVTV_API_ASSIGN_FRAME_SIZE 0x91
+#define IVTV_API_ASSIGN_BITRATES 0x95
+#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x97
+#define IVTV_API_ASSIGN_ASPECT_RATIO 0x99
+#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x9b
+#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x9d
+#define IVTV_API_ASSIGN_CORING_LEVELS 0x9f
+
+#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0xa1
+
+#define IVTV_API_ASSIGN_3_2_PULLDOWN 0xb1
+#define IVTV_API_SELECT_VBI_LINE 0xb7
+#define IVTV_API_ASSIGN_STREAM_TYPE 0xb9
+#define IVTV_API_ASSIGN_OUTPUT_PORT 0xbb
+#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0xbd
+
+#define IVTV_API_ENC_HALT_FW 0xc3
+#define IVTV_API_ENC_GETVER 0xc4
+#define IVTV_API_ASSIGN_GOP_CLOSURE 0xc5
+#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0xc7
+#define IVTV_API_CONFIG_VBI 0xc8
+#define IVTV_API_ASSIGN_DMA_BLOCKLEN 0xc9
+#define IVTV_API_SCHED_DMA_TO_HOST 0xcc
+#define IVTV_API_INITIALIZE_INPUT 0xcd
+
+#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0xd0
+#define IVTV_API_PAUSE_ENCODER 0xd2
+#define IVTV_API_REFRESH_INPUT 0xd3
+#define IVTV_API_EVENT_NOTIFICATION 0xd5
+#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0xd6
+#define IVTV_API_ASSIGN_PLACEHOLDER 0xd8
+#define IVTV_API_MUTE_VIDEO 0xd9
+#define IVTV_API_MUTE_AUDIO 0xda
+
+#define IVTV_API_OSD_DMA_FROM_HOST 0xff
+
+/* Firmware mailbox flags - definitions found from ivtv */
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_DRIVER_BUSY 0x00000001
+
+
+static int pvr2_write_encoder_words(struct pvr2_hdw *hdw,
+ const u32 *data, unsigned int dlen)
+{
+ unsigned int idx;
+ int ret;
+ unsigned int offs = 0;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x01. Remaining 32 bit words are
+ spread out into chunks of 7 bytes each, little-endian ordered,
+ offset at zero within each 2 blank bytes following and a
+ single byte that is 0x44 plus the offset of the word. Repeat
+ request for additional words, with offset adjusted
+ accordingly.
+
+ */
+ while (dlen) {
+ chunkCnt = 8;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
+ hdw->cmd_buffer[0] = 0x01;
+ for (idx = 0; idx < chunkCnt; idx++) {
+ hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs;
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7),
+ data[idx]);
+ }
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1+(chunkCnt*7),
+ 0,0);
+ if (ret) return ret;
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+static int pvr2_read_encoder_words(struct pvr2_hdw *hdw,int statusFl,
+ u32 *data, unsigned int dlen)
+{
+ unsigned int idx;
+ int ret;
+ unsigned int offs = 0;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x02 (status check) or 0x28 (read
+ back block of 32 bit words). Next 6 bytes must be zero,
+ followed by a single byte of 0x44+offset for portion to be
+ read. Returned data is packed set of 32 bits words that were
+ read.
+
+ */
+
+ while (dlen) {
+ chunkCnt = 16;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
+ hdw->cmd_buffer[0] = statusFl ? 0x02 : 0x28;
+ hdw->cmd_buffer[7] = 0x44 + offs;
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,8,
+ hdw->cmd_buffer,chunkCnt * 4);
+ if (ret) return ret;
+
+ for (idx = 0; idx < chunkCnt; idx++) {
+ data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
+ }
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd,
+ int args, ...)
+{
+ unsigned int poll_count;
+ int ret = 0;
+ va_list vl;
+ unsigned int idx;
+ u32 wrData[16];
+ u32 rdData[32];
+
+ /*
+
+ The encoder seems to speak entirely using blocks 32 bit words.
+ In ivtv driver terms, this is a mailbox which we populate with
+ data and watch what the hardware does with it. The first word
+ is a set of flags used to control the transaction, the second
+ word is the command to execute, the third byte is zero (ivtv
+ driver suggests that this is some kind of return value), and
+ the fourth byte is a specified timeout (windows driver always
+ uses 0x00060000 except for one case when it is zero). All
+ successive words are the argument words for the command.
+
+ First, write out the entire set of words, with the first word
+ being zero.
+
+ Next, write out just the first word again, but set it to
+ IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
+ probably means "go").
+
+ Next, read back 16 words as status. Check the first word,
+ which should have IVTV_MBOX_FIRMWARE_DONE set. If however
+ that bit is not set, then the command isn't done so repeat the
+ read.
+
+ Next, read back 32 words and compare with the original
+ arugments. Hopefully they will match.
+
+ Finally, write out just the first word again, but set it to
+ 0x0 this time (which probably means "idle").
+
+ */
+
+
+ LOCK_TAKE(hdw->ctl_lock); do {
+
+ wrData[0] = 0;
+ wrData[1] = cmd;
+ wrData[2] = 0;
+ wrData[3] = 0x00060000;
+ va_start(vl, args);
+ for (idx = 0; idx < args; idx++) {
+ wrData[idx+4] = va_arg(vl, u32);
+ }
+ va_end(vl);
+ args += 4;
+ while (args < sizeof(wrData)/sizeof(wrData[0])) {
+ wrData[args++] = 0;
+ }
+
+ ret = pvr2_write_encoder_words(hdw,wrData,args);
+ if (ret) break;
+ wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
+ ret = pvr2_write_encoder_words(hdw,wrData,1);
+ if (ret) break;
+ poll_count = 0;
+ while (1) {
+ if (poll_count < 10000000) poll_count++;
+ ret = pvr2_read_encoder_words(hdw,!0,rdData,1);
+ if (ret) break;
+ if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
+ break;
+ }
+ if (poll_count == 100) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** device's encoder"
+ " appears to be stuck"
+ " (status=0%08x)",rdData[0]);
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder command: 0x%02x",cmd);
+ for (idx = 4; idx < args; idx++) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder arg%d: 0x%08x",
+ idx-3,wrData[idx]);
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Giving up waiting."
+ " It is likely that"
+ " this is a bad idea...");
+ ret = -EBUSY;
+ break;
+ }
+ }
+ if (ret) break;
+ wrData[0] = 0x7;
+ ret = pvr2_read_encoder_words(hdw,0,rdData,16);
+ if (ret) break;
+ for (idx = 0; idx < args; idx++) {
+ if (rdData[idx] != wrData[idx]) {
+ pvr2_trace(
+ PVR2_TRACE_DEBUG,
+ "pvr2_encoder idx %02x mismatch exp:"
+ " %08x got: %08x",
+ idx,wrData[idx],rdData[idx]);
+ }
+ }
+
+ wrData[0] = 0x0;
+ ret = pvr2_write_encoder_words(hdw,wrData,1);
+ if (ret) break;
+
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+int pvr2_encoder_configure(struct pvr2_hdw *hdw)
+{
+ int ret = 0, audio, i;
+ int is_ntsc = (hdw->controls[PVR2_CID_VIDEOSTANDARD].value ==
+ PVR2_CVAL_VIDEOSTANDARD_NTSC_M);
+ int height = hdw->controls[PVR2_CID_VRES].value;
+ int width = hdw->controls[PVR2_CID_HRES].value;
+ int height_full = !hdw->controls[PVR2_CID_INTERLACE].value;
+
+ pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure");
+
+ /* set stream output port. */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_OUTPUT_PORT, 2,
+ 0x01, 0x01);
+
+ /* set the Program Index Information. We want I,P,B frames (max 400) */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_PGM_INDEX_INFO, 2,
+ 0x07, 0x0190);
+
+ /* NOTE : windows driver sends some 0xdc */
+
+ /* Mike Isely <isely@pobox.com> 19-Jun-2005 I've confirmed
+ that the Windows driver seems to issue these commands, but
+ right now I have no idea what these do (and neither does
+ the ivtv driver). But, if I leave them in, then mplayer
+ goes nuts with xrun errors. So for now we don't do this.
+ It sure would be nice to know what these are for. */
+#ifdef notdef
+ ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 5);
+ ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 2, 3, 1);
+ ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 8);
+#endif
+
+ /* Strange compared to ivtv data. */
+#ifdef notdef
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 2,
+ 0x0120, 0x0120);
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 2,
+ 0x0131, 0x0131);
+#endif
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 2,
+ 0xf0, 0xf0);
+
+ /* setup firmware to notify us about some events (don't know why...) */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_EVENT_NOTIFICATION, 4,
+ 0, 0, 0x10000000, 0xffffffff);
+
+ /* set fps to 25 or 30 (1 or 0)*/
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_FRAMERATE, 1,
+ is_ntsc ? 0 : 1);
+
+ /* set encoding resolution */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_FRAME_SIZE, 2,
+ (height_full ? height : (height / 2)),
+ width);
+ /* set encoding aspect ratio to 4:3 */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_ASPECT_RATIO, 1,
+ 0x02);
+
+ /* VBI */
+
+ if (hdw->config == pvr2_config_vbi) {
+ int lines = 2 * (is_ntsc ? 12 : 18);
+ int size = (4*((lines*1443+3)/4)) / lines;
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_CONFIG_VBI, 7,
+ 0xbd05, 1, 4,
+ 0x25256262, 0x387f7f7f,
+ lines , size);
+// 0x25256262, 0x13135454, lines , size);
+ /* select vbi lines */
+#define line_used(l) (is_ntsc ? (l >= 10 && l <= 21) : (l >= 6 && l <= 23))
+ for (i = 2 ; i <= 24 ; i++){
+ ret |= pvr2_write_encoder_vcmd(
+ hdw,IVTV_API_SELECT_VBI_LINE, 5,
+ i-1,line_used(i), 0, 0, 0);
+ ret |= pvr2_write_encoder_vcmd(
+ hdw,IVTV_API_SELECT_VBI_LINE, 5,
+ (i-1) | (1 << 31),
+ line_used(i), 0, 0, 0);
+ }
+ } else {
+ ret |= pvr2_write_encoder_vcmd(
+ hdw,IVTV_API_SELECT_VBI_LINE, 5,
+ 0xffffffff,0,0,0,0);
+ }
+
+ /* set stream type, depending on resolution. */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_STREAM_TYPE, 1,
+ height_full ? 0x0a : 0x0b);
+ /* set video bitrate */
+ ret |= pvr2_write_encoder_vcmd(
+ hdw, IVTV_API_ASSIGN_BITRATES, 3,
+ (hdw->controls[PVR2_CID_VBR].value ? 1 : 0),
+ hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].value,
+ (u32) (hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].value) / 400);
+ /* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_GOP_PROPERTIES, 2,
+ is_ntsc ? 0x0f : 0x0c, 0x03);
+
+ /* enable 3:2 pulldown */
+ ret |= pvr2_write_encoder_vcmd(hdw,IVTV_API_ASSIGN_3_2_PULLDOWN,1,0);
+
+ /* set GOP open/close property (open) */
+ ret |= pvr2_write_encoder_vcmd(hdw,IVTV_API_ASSIGN_GOP_CLOSURE,1,0);
+
+ /* set audio stream properties 0x40b9? 0100 0000 1011 1001 */
+ audio = (pvr_tbl_audiobitrate[hdw->controls[
+ PVR2_CID_AUDIOBITRATE].value] |
+ pvr_tbl_srate[hdw->controls[PVR2_CID_SRATE].value] |
+ hdw->controls[PVR2_CID_AUDIOLAYER].value << 2 |
+ (hdw->controls[PVR2_CID_AUDIOCRC].value ? 1 << 14 : 0) |
+ pvr_tbl_emphasis[hdw->controls[
+ PVR2_CID_AUDIOEMPHASIS].value]);
+
+ ret |= pvr2_write_encoder_vcmd(hdw,IVTV_API_ASSIGN_AUDIO_PROPERTIES,1,
+ audio);
+
+ /* set dynamic noise reduction filter to manual, Horiz/Vert */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2,
+ 0, 0x03);
+
+ /* dynamic noise reduction filter param */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2
+ , 0, 0);
+
+ /* dynamic noise reduction median filter */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_ASSIGN_CORING_LEVELS, 4,
+ 0, 0xff, 0, 0xff);
+
+ /* spacial prefiler parameter */
+ ret |= pvr2_write_encoder_vcmd(hdw,
+ IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2,
+ 0x01, 0x01);
+
+ /* initialize video input */
+ ret |= pvr2_write_encoder_vcmd(hdw, IVTV_API_INITIALIZE_INPUT, 0);
+
+ if (!ret) {
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_CFG;
+ }
+
+ return ret;
+}
+
+int pvr2_encoder_start(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* unmask some interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xbfffffff);
+
+ /* change some GPIO data */
+ pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
+ pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
+
+ if (hdw->config == pvr2_config_vbi) {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_BEGIN_CAPTURE,2,
+ 0x01,0x14);
+ } else if (hdw->config == pvr2_config_mpeg) {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_BEGIN_CAPTURE,2,
+ 0,0x13);
+ } else {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_BEGIN_CAPTURE,2,
+ 0,0x13);
+ }
+ if (!status) {
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_RUN;
+ }
+ return status;
+}
+
+int pvr2_encoder_stop(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* mask all interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xffffffff);
+
+ if (hdw->config == pvr2_config_vbi) {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_END_CAPTURE,3,
+ 0x01,0x01,0x14);
+ } else if (hdw->config == pvr2_config_mpeg) {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_END_CAPTURE,3,
+ 0x01,0,0x13);
+ } else {
+ status = pvr2_write_encoder_vcmd(hdw,IVTV_API_END_CAPTURE,3,
+ 0x01,0,0x13);
+ }
+
+ /* change some GPIO data */
+ /* Note: Bit d7 of dir appears to control the LED. So we shut it
+ off here. */
+ pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
+ pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
+
+ if (!status) {
+ hdw->subsys_enabled_mask &= ~PVR2_SUBSYS_ENC_RUN;
+ }
+ return status;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-encoder.h b/v4l_experimental/pvrusb2/pvrusb2-encoder.h
new file mode 100644
index 000000000..4a35e8ac0
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-encoder.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * $Id: pvrusb2-encoder.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_ENCODER_H
+#define __PVRUSB2_ENCODER_H
+
+struct pvr2_hdw;
+
+int pvr2_encoder_configure(struct pvr2_hdw *);
+int pvr2_encoder_start(struct pvr2_hdw *);
+int pvr2_encoder_stop(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_ENCODER_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h b/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h
new file mode 100644
index 000000000..ad5cc0731
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h
@@ -0,0 +1,159 @@
+/*
+ *
+ * $Id: pvrusb2-hdw-internal.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_HDW_INTERNAL_H
+#define __PVRUSB2_HDW_INTERNAL_H
+
+/*
+
+ This header sets up all the internal structures and definitions needed to
+ track and coordinate the driver's interaction with the hardware. ONLY
+ source files which actually implement part of that whole circus should be
+ including this header. Higher levels, like the external layers to the
+ various public APIs (V4L, sysfs, etc) should NOT ever include this
+ private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder,
+ etc will include this, but pvrusb2-v4l should not.
+
+*/
+
+#include "compat.h"
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <asm/semaphore.h>
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
+
+#define PVR2_VID_ENDPOINT 0x84
+#define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */
+#define PVR2_VBI_ENDPOINT 0x88
+
+#define PVR2_CTL_BUFFSIZE 64
+
+#define FREQTABLE_SIZE 500
+
+#define LOCK_TAKE(x) do { down(&x##_sem); x##_held = !0; } while (0)
+#define LOCK_GIVE(x) do { x##_held = 0; up(&x##_sem); } while (0)
+
+struct pvr2_ctl_state {
+ int value;
+ int dirty;
+};
+
+/* This structure contains all state data directly needed to
+ manipulate the hardware (as opposed to complying with a kernel
+ interface) */
+struct pvr2_hdw {
+ /* Underlying USB device handle */
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
+ /* Video spigot */
+ struct pvr2_stream *vid_stream;
+
+ /* Mutex for all hardware state control */
+ struct semaphore big_lock_sem;
+ int big_lock_held; /* For debugging */
+
+ char name[32];
+
+ /* I2C stuff */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ int i2c_linked;
+ struct i2c_client *i2c_tuner_client;
+ struct i2c_client *i2c_audio_client;
+ struct i2c_client *i2c_video_client;
+ struct i2c_client *i2c_tveeprom_client;
+
+ /* Frequency table */
+ unsigned int freqTable[FREQTABLE_SIZE];
+
+ /* Stuff for handling low level control interaction with device */
+ struct semaphore ctl_lock_sem;
+ int ctl_lock_held; /* For debugging */
+ struct urb *ctl_write_urb;
+ struct urb *ctl_read_urb;
+ unsigned char *ctl_write_buffer;
+ unsigned char *ctl_read_buffer;
+ volatile int ctl_write_pend_flag;
+ volatile int ctl_read_pend_flag;
+ volatile int ctl_timeout_flag;
+ struct completion ctl_done;
+ unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE];
+ int cmd_debug_state; // Low level command debugging info
+ unsigned char cmd_debug_code; //
+ unsigned int cmd_debug_write_len; //
+ unsigned int cmd_debug_read_len; //
+
+ int flag_ok; // device in known good state
+ int flag_disconnected; // flag_ok == 0 due to disconnect
+ int flag_init_ok; // true if structure is fully initialized
+ int flag_streaming_enabled; // true if streaming should be on
+
+ // CPU firmware info (used to help find / save firmware data)
+ char *fw_buffer;
+ unsigned int fw_size;
+
+ // Which subsystem pieces have been enabled / configured
+ unsigned long subsys_enabled_mask;
+
+ // Which subsystems are manipulated to enable streaming
+ unsigned long subsys_stream_mask;
+
+ /* Tuner / frequency control stuff */
+ unsigned int tuner_type;
+ unsigned long video_standards;
+
+ /* saa7115 info: 0=unknown 1=new decoder 2=old decoder */
+ int decoder_info;
+
+ int unit_number; /* ID for driver instance */
+ unsigned long serial_number; /* ID for hardware itself */
+
+ /* Minor number used by v4l logic (yes, this is a hack, as there should
+ be no v4l junk here). Probably a better way to do this. */
+ int v4l_minor_number;
+
+ enum pvr2_config config;
+
+ /* Information about what audio signal we're hearing */
+ int flag_stereo;
+ int flag_bilingual;
+
+ /* Every last bit of controllable state */
+ struct pvr2_ctl_state controls[PVR2_CID_COUNT];
+};
+
+int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw,
+ unsigned int ctl_id,int value);
+int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
+
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw.c b/v4l_experimental/pvrusb2/pvrusb2-hdw.c
new file mode 100644
index 000000000..6853db776
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-hdw.c
@@ -0,0 +1,2293 @@
+/*
+ *
+ * $Id: pvrusb2-hdw.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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 "compat.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <asm/semaphore.h>
+#include "pvrusb2.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-i2c.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-audio.h"
+#include "pvrusb2-video.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-debug.h"
+
+static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0};
+DECLARE_MUTEX(pvr2_unit_sem);
+
+static int hfull = 1;
+static int width = 720;
+static int initusbreset = 1;
+static int procreload = 0;
+
+module_param(initusbreset, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(initusbreset, "Do USB reset device on probe");
+module_param(procreload, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(procreload,
+ "Attempt init failure recovery with firmware reload");
+module_param(hfull, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(hfull, "full height video ?");
+module_param(width, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(width, "video width : 720,480,352");
+
+#define PVR2_CTL_WRITE_ENDPOINT 0x01
+#define PVR2_CTL_READ_ENDPOINT 0x81
+
+#define PVR2_GPIO_IN 0x9008
+#define PVR2_GPIO_OUT 0x900c
+#define PVR2_GPIO_DIR 0x9020
+
+#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
+
+#define PVR2_FIRMWARE_ENDPOINT 0x02
+
+/* size of a firmware chunk */
+#define FIRMWARE_CHUNK_SIZE 0x2000
+
+/* Various files we will load with firmware_entry */
+#define FIRMWARE1_FILE "pvrusb2.f1"
+#define FIRMWARE2_FILE "pvrusb2.f2"
+
+typedef int (*pvr2_ctl_set_func)(struct pvr2_hdw *,int ctl_id,int val);
+typedef int (*pvr2_ctl_get_func)(struct pvr2_hdw *,int ctl_id);
+
+struct pvr2_ctl_def {
+ const char *name;
+ pvr2_ctl_set_func set_func;
+ pvr2_ctl_get_func get_func;
+ int max_value;
+ int min_value;
+ int skip_init;
+ int default_value;
+ const char **value_defs_ptr;
+ unsigned int value_defs_count;
+};
+
+
+static const char *control_values_srate[] = {
+ [PVR2_CVAL_SRATE_48] = "48KHz",
+ [PVR2_CVAL_SRATE_44_1] = "44.1KHz",
+};
+
+
+static const char *control_values_audiobitrate[] = {
+ [PVR2_CVAL_AUDIOBITRATE_384] = "384kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_320] = "320kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_256] = "256kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_224] = "224kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_192] = "192kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_160] = "160kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_128] = "128kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_112] = "112kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_96] = "96kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_80] = "80kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_64] = "64kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_56] = "56kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_48] = "48kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_32] = "32kb/s",
+ [PVR2_CVAL_AUDIOBITRATE_VBR] = "VBR",
+};
+
+
+static const char *control_values_audioemphasis[] = {
+ [PVR2_CVAL_AUDIOEMPHASIS_NONE] = "None",
+ [PVR2_CVAL_AUDIOEMPHASIS_50_15] = "50/15us",
+ [PVR2_CVAL_AUDIOEMPHASIS_CCITT] = "CCITT J.17",
+};
+
+
+static const char *control_values_videostandard[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = "NTSC-M",
+ [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = "SECAM-L",
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = "PAL-BG",
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = "PAL-I",
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = "PAL-DK",
+};
+
+
+static const char *control_values_input[] = {
+ [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/
+ [PVR2_CVAL_INPUT_RADIO] = "radio",
+ [PVR2_CVAL_INPUT_SVIDEO] = "s-video",
+ [PVR2_CVAL_INPUT_COMPOSITE] = "composite",
+};
+
+
+static const char *control_values_audiomode[] = {
+ [PVR2_CVAL_AUDIOMODE_MONO] = "Mono",
+ [PVR2_CVAL_AUDIOMODE_STEREO] = "Stereo",
+ [PVR2_CVAL_AUDIOMODE_SAP] = "SAP",
+ [PVR2_CVAL_AUDIOMODE_LANG1] = "Lang1",
+ [PVR2_CVAL_AUDIOMODE_LANG2] = "Lang2",
+};
+
+
+static const char *control_values_hsm[] = {
+ [PVR2_CVAL_HSM_FAIL] = "Fail",
+ [PVR2_CVAL_HSM_HIGH] = "High",
+ [PVR2_CVAL_HSM_FULL] = "Full",
+};
+
+
+#define VDEF(x) \
+ .value_defs_ptr = x, \
+ .value_defs_count = (sizeof(x)/sizeof(x[0]))
+
+static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value);
+static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val);
+static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id);
+static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id,
+ int val);
+
+static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] =
+{
+ [PVR2_CID_BRIGHTNESS] = {
+ .name = "Brightness",
+ .min_value = 0,
+ .max_value = 255,
+ .default_value = 128,
+ },
+ [PVR2_CID_CONTRAST] = {
+ .name = "Contrast",
+ .min_value = 0,
+ .max_value = 127,
+ .default_value = 68,
+ },
+ [PVR2_CID_SATURATION] = {
+ .name = "Saturation",
+ .min_value = 0,
+ .max_value = 127,
+ .default_value = 64,
+ },
+ [PVR2_CID_HUE] = {
+ .name = "Hue",
+ .min_value = -128,
+ .max_value = 127,
+ .default_value = 0,
+ },
+ [PVR2_CID_VOLUME] = {
+ .name = "Volume",
+ .min_value = 0,
+ .max_value = 65535,
+ .default_value = 65535,
+ },
+ [PVR2_CID_BALANCE] = {
+ .name = "Balance",
+ .min_value = -32768,
+ .max_value = 32767,
+ .default_value = 0,
+ },
+ [PVR2_CID_BASS] = {
+ .name = "Bass",
+ .min_value = -32768,
+ .max_value = 32767,
+ .default_value = 0,
+ },
+ [PVR2_CID_TREBLE] = {
+ .name = "Treble",
+ .min_value = -32768,
+ .max_value = 32767,
+ .default_value = 0,
+ },
+ [PVR2_CID_MUTE] = {
+ .name = "Mute",
+ .min_value = 0,
+ .max_value = 1,
+ .default_value = 0,
+ },
+ [PVR2_CID_SRATE] = {
+ .name = "Sample rate",
+ .min_value = PVR2_CVAL_SRATE_MIN,
+ .max_value = PVR2_CVAL_SRATE_MAX,
+ .default_value = PVR2_CVAL_SRATE_48,
+ VDEF(control_values_srate),
+ },
+ [PVR2_CID_AUDIOBITRATE] = {
+ .name = "Audio Bitrate",
+ .min_value = PVR2_CVAL_AUDIOBITRATE_MIN,
+ .max_value = PVR2_CVAL_AUDIOBITRATE_MAX,
+ .default_value = PVR2_CVAL_AUDIOBITRATE_224,
+ VDEF(control_values_audiobitrate),
+ },
+ [PVR2_CID_AUDIOCRC] = {
+ .name = "Audio CRC",
+ .min_value = 0,
+ .max_value = 1,
+ .default_value = 1,
+ },
+ [PVR2_CID_AUDIOEMPHASIS] = {
+ .name = "Audio Emphasis",
+ .min_value = PVR2_CVAL_AUDIOEMPHASIS_MIN,
+ .max_value = PVR2_CVAL_AUDIOEMPHASIS_MAX,
+ .default_value = PVR2_CVAL_AUDIOEMPHASIS_NONE,
+ VDEF(control_values_audioemphasis),
+ },
+ [PVR2_CID_VBR] = {
+ .name = "Variable video bitrate",
+ .min_value = 0,
+ .max_value = 1,
+ .default_value = 0,
+ },
+ [PVR2_CID_AVERAGEVIDEOBITRATE] = {
+ .name = "Average video bitrate",
+ .min_value = 1,
+ .max_value = 20000000,
+ .default_value = 6000000,
+ },
+ [PVR2_CID_PEAKVIDEOBITRATE] = {
+ .name = "Peak video bitrate",
+ .min_value = 1,
+ .max_value = 20000000,
+ .default_value = 6000000,
+ },
+ [PVR2_CID_VIDEOSTANDARD] = {
+ .name = "Video Standard",
+ .min_value = PVR2_CVAL_VIDEOSTANDARD_MIN,
+ .max_value = PVR2_CVAL_VIDEOSTANDARD_MAX,
+ .default_value = PVR2_CVAL_VIDEOSTANDARD_NTSC_M,
+ VDEF(control_values_videostandard),
+ },
+ [PVR2_CID_INPUT] = {
+ .name = "Video Source",
+ .min_value = PVR2_CVAL_INPUT_MIN,
+ .max_value = PVR2_CVAL_INPUT_MAX,
+ .default_value = PVR2_CVAL_INPUT_TV,
+ VDEF(control_values_input),
+ },
+ [PVR2_CID_AUDIOMODE] = {
+ .name = "Audio Mode",
+ .min_value = PVR2_CVAL_AUDIOMODE_MIN,
+ .max_value = PVR2_CVAL_AUDIOMODE_MAX,
+ .default_value = PVR2_CVAL_AUDIOMODE_STEREO,
+ VDEF(control_values_audiomode),
+ },
+ [PVR2_CID_FREQUENCY] = {
+ .name = "Tuner Frequency (Hz)",
+ .min_value = 55250000L,
+ .max_value = 801250000L,
+ .default_value = 175250000L,
+ },
+ [PVR2_CID_HRES] = {
+ .name = "Horizontal capture resolution",
+ .min_value = 320,
+ .max_value = 720,
+ .default_value = 720,
+ },
+ [PVR2_CID_VRES] = {
+ .name = "Vertical capture resolution",
+ .min_value = 200,
+ .max_value = 625,
+ .default_value = 480,
+ },
+ [PVR2_CID_INTERLACE] = {
+ .name = "Interlace mode",
+ .min_value = 0,
+ .max_value = 1,
+ .default_value = 0,
+ },
+ [PVR2_CID_AUDIOLAYER] = {
+ .name = "Audio Layer",
+ .min_value = 0, /* This is all a wild guess */
+ .max_value = 3,
+ .default_value = 2, /* Appears to be all that is supported */
+ },
+ [PVR2_CID_CHANNEL] = {
+ .name = "Channel",
+ .min_value = 0,
+ .max_value = FREQTABLE_SIZE,
+ .default_value = 0,
+ },
+ [PVR2_CID_CHANPROG_ID] = {
+ .name = "Channel Program ID",
+ .min_value = 0,
+ .max_value = FREQTABLE_SIZE,
+ .default_value = 0,
+ },
+ [PVR2_CID_CHANPROG_FREQ] = {
+ .name = "Channel Program Frequency",
+ .min_value = 55250000L,
+ .max_value = 801250000L,
+ .skip_init = !0,
+ .set_func = pvr2_ctl_set_chanprog_id,
+ .get_func = pvr2_ctl_get_chanprog_id,
+ },
+ [PVR2_CID_SIGNAL_PRESENT] = {
+ .name = "Signal Present",
+ .min_value = 0,
+ .max_value = 1,
+ .get_func = pvr2_ctl_get_signal,
+ },
+ [PVR2_CID_STREAMING_ENABLED] = {
+ .name = "Streaming Enabled",
+ .min_value = 0,
+ .max_value = 1,
+ .get_func = pvr2_ctl_get_streaming,
+ },
+ [PVR2_CID_HSM] = {
+ .name = "USB Speed",
+ .min_value = PVR2_CVAL_HSM_MIN,
+ .max_value = PVR2_CVAL_HSM_MAX,
+ .get_func = pvr2_ctl_get_hsm,
+ VDEF(control_values_hsm),
+ },
+ [PVR2_CID_SUBSYS_MASK] = {
+ .name = "Subsystem enabled mask",
+ .min_value = 0,
+ .max_value = 0x7fffffff,
+ .skip_init = !0,
+ .get_func = pvr2_ctl_get_subsys_mask,
+ .set_func = pvr2_ctl_set_subsys_mask,
+ },
+ [PVR2_CID_SUBSYS_STREAM_MASK] = {
+ .name = "Subsystem stream mask",
+ .min_value = 0,
+ .max_value = 0x7fffffff,
+ .skip_init = !0,
+ .get_func = pvr2_ctl_get_subsys_stream_mask,
+ .set_func = pvr2_ctl_set_subsys_stream_mask,
+ },
+};
+
+#undef VDEF
+
+
+const char *pvr2_config_get_name(enum pvr2_config cfg)
+{
+ switch (cfg) {
+ case pvr2_config_empty: return "empty";
+ case pvr2_config_mpeg: return "mpeg";
+ case pvr2_config_vbi: return "vbi";
+ case pvr2_config_radio: return "radio";
+ }
+ return "<unknown>";
+}
+
+
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
+{
+ return hdw->usb_dev;
+}
+
+
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
+{
+ return hdw->serial_number;
+}
+
+
+struct pvr2_hdw *pvr2_hdw_find(int unit_number)
+{
+ if (unit_number < 0) return 0;
+ if (unit_number >= PVR_NUM) return 0;
+ return unit_pointers[unit_number];
+}
+
+
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
+{
+ return hdw->unit_number;
+}
+
+
+/*
+ * pvr2_upload_firmware1().
+ *
+ * Send the 8051 firmware to the device. After the upload, arrange for
+ * device to re-enumerate.
+ *
+ * NOTE : the pointer to the firmware data given by request_firmware()
+ * is not suitable for an usb transaction.
+ *
+ */
+int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = 0;
+ void *fw_ptr;
+ unsigned int pipe;
+ int ret;
+ u16 address;
+ const char *firmware_file = FIRMWARE1_FILE;
+
+ trace_firmware("pvr2_upload_firmware1");
+
+ usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0);
+ usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
+
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ ret = request_firmware(&fw_entry, firmware_file, &hdw->usb_dev->dev);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware failed for '%s'", firmware_file);
+ return ret;
+ }
+
+ if (fw_entry->size != 0x2000){
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size");
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ fw_ptr = kmalloc(0x800, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
+ chunk. */
+
+ ret = 0;
+ for(address = 0; address < fw_entry->size; address += 0x800) {
+ memcpy(fw_ptr, fw_entry->data + address, 0x800);
+ ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
+ 0, fw_ptr, 0x800, HZ);
+ }
+
+ trace_firmware("Upload done, releasing device's CPU");
+
+ /* Now release the CPU. It will disconnect and reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ trace_firmware("Upload done (%d bytes sent)",ret);
+
+ /* We should have written 8192 bytes */
+ return (ret == 8192) ? 0 : -EIO;
+}
+
+
+/*
+ * pvr2_upload_firmware2()
+ *
+ * This uploads encoder firmware on endpoint 2.
+ *
+ */
+
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = 0;
+ void *fw_ptr;
+ unsigned int pipe, fw_len, fw_done;
+ int actual_length;
+ int ret = 0;
+
+ trace_firmware("pvr2_upload_firmware2");
+
+ /* First prepare firmware loading */
+ ret |= pvr2_hdw_cmd_soft_reset(hdw);
+ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_hdw_cmd_deep_reset(hdw);
+ ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
+ ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
+ ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
+ ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
+ ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
+ ret |= pvr2_write_u8(hdw, 0x52, 0);
+ ret |= pvr2_write_u16(hdw, 0x0600, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload prep failed, ret=%d",ret);
+ return ret;
+ }
+
+ /* Now send firmware */
+
+ ret = request_firmware(&fw_entry, FIRMWARE2_FILE, &hdw->usb_dev->dev);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware failed for '%s'", FIRMWARE2_FILE);
+ return ret;
+ }
+
+ fw_len = fw_entry->size;
+
+ if (fw_len % FIRMWARE_CHUNK_SIZE) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "size of %s must be a multiple of 8192B",
+ FIRMWARE2_FILE);
+ release_firmware(fw_entry);
+ return -1;
+ }
+
+ fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "failed to allocate memory for firmware2 upload");
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
+
+ for (fw_done = 0 ; (fw_done < fw_len) && !ret ;
+ fw_done += FIRMWARE_CHUNK_SIZE ) {
+ int i;
+ memcpy(fw_ptr, fw_entry->data + fw_done, FIRMWARE_CHUNK_SIZE);
+ /* Usbsnoop log shows that we must swap bytes... */
+ for (i = 0; i < FIRMWARE_CHUNK_SIZE/4 ; i++)
+ ((u32 *)fw_ptr)[i] = ___swab32(((u32 *)fw_ptr)[i]);
+
+ ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,
+ FIRMWARE_CHUNK_SIZE,
+ &actual_length, HZ);
+ ret |= (actual_length != FIRMWARE_CHUNK_SIZE);
+ }
+
+ trace_firmware("upload of %s : %i / %i ",
+ FIRMWARE2_FILE,fw_done,fw_len);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload transfer failure");
+ return ret;
+ }
+
+ /* Finish upload */
+
+ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
+ ret |= pvr2_write_u16(hdw, 0x0600, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload post-proc failure");
+ } else {
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_FIRMWARE;
+ }
+ return ret;
+}
+
+
+#define FIRMWARE_RECOVERY_BITS \
+ (PVR2_SUBSYS_ENC_CFG | \
+ PVR2_SUBSYS_ENC_RUN | \
+ PVR2_SUBSYS_ENC_FIRMWARE | \
+ PVR2_SUBSYS_USBSTREAM_RUN)
+
+/*
+
+ This single function is key to pretty much everything. The pvrusb2
+ device can logically be viewed as a series of subsystems which can be
+ stopped / started or unconfigured / configured. To get things streaming,
+ one must configure everything and start everything, but there may be
+ various reasons over time to deconfigure something or stop something.
+ This function handles all of this activity. Everything EVERYWHERE that
+ must affect a subsystem eventually comes here to do the work.
+
+ The current state of all subsystems is represented by a single bit mask,
+ known as subsys_enabled_mask. The bit positions are defined by the
+ PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any
+ time the set of configured or active subsystems can be queried just by
+ looking at that mask. To change bits in that mask, this function here
+ must be called. The "msk" argument indicates which bit positions to
+ change, and the "val" argument defines the new values for the positions
+ defined by "msk".
+
+ There is a priority ordering of starting / stopping things, and for
+ multiple requested changes, this function implements that ordering.
+ (Thus we will act on a request to load encoder firmware before we
+ configure the encoder.) In addition to priority ordering, there is a
+ recovery strategy implemented here. If a particular step fails and we
+ detect that failure, this function will clear the affected subsystem bits
+ and restart. Thus we have a means for recovering from a dead encoder:
+ Clear all bits that correspond to subsystems that we need to restart /
+ reconfigure and start over.
+
+*/
+void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val)
+{
+ unsigned long nmsk;
+ unsigned long vmsk;
+ int ret;
+ unsigned int tryCount = 0;
+
+ if (!hdw->flag_ok) return;
+
+ msk &= PVR2_SUBSYS_ALL;
+
+ for (;;) {
+ tryCount++;
+ vmsk = hdw->subsys_enabled_mask & PVR2_SUBSYS_ALL;
+ nmsk = (vmsk & ~msk) | (val & msk);
+ if (!(nmsk ^ vmsk)) break;
+ if (tryCount > 4) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Too many retries when configuring device;"
+ " giving up");
+ pvr2_hdw_render_useless(hdw);
+ break;
+ }
+ if (tryCount > 1) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Retrying device reconfiguration");
+ }
+ pvr2_trace(PVR2_TRACE_INIT,
+ "subsys mask changing 0x%lx:0x%lx"
+ " from 0x%lx to 0x%lx",
+ msk,val,hdw->subsys_enabled_mask,nmsk);
+
+ vmsk = (nmsk ^ hdw->subsys_enabled_mask) &
+ hdw->subsys_enabled_mask;
+ if (vmsk) {
+ if (vmsk & PVR2_SUBSYS_ENC_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_stop");
+ ret = pvr2_encoder_stop(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ if (vmsk & PVR2_SUBSYS_USBSTREAM_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_hdw_cmd_usbstream(0)");
+ pvr2_hdw_cmd_usbstream(hdw,0);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_enable_output(0)");
+ pvr2_decoder_enable_output(hdw,0);
+ }
+ if (vmsk & PVR2_SUBSYS_CFG_ALL) {
+ hdw->subsys_enabled_mask &=
+ ~(vmsk & PVR2_SUBSYS_CFG_ALL);
+ }
+ }
+ vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk;
+ if (vmsk) {
+ if (vmsk & PVR2_SUBSYS_ENC_FIRMWARE) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_upload_firmware2");
+ ret = pvr2_upload_firmware2(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failure uploading encoder"
+ " firmware");
+ pvr2_hdw_render_useless(hdw);
+ break;
+ }
+ }
+ if (vmsk & PVR2_SUBSYS_TUNER_CFG_STD) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_tuner_set_standard");
+ pvr2_tuner_set_standard(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_TUNER_CFG_FREQ) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_tuner_set_freq");
+ pvr2_tuner_set_freq(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_AUDIO_CFG_STD) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_audio_set_standard");
+ pvr2_audio_set_standard(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_AUDIO_CFG_MODE) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_audio_set_stereo");
+ pvr2_audio_set_stereo(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_AUDIO_CFG_VBBTM) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_audio_setvolume");
+ pvr2_audio_setvolume(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_NORM) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_set_norm");
+ pvr2_decoder_set_norm(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_INPUT) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_set_input");
+ pvr2_decoder_set_input(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_SIZE) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_set_size");
+ pvr2_decoder_set_size(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_AUDIO) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_set_audio");
+ pvr2_decoder_set_audio(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_BCSH) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_set_bcsh");
+ pvr2_decoder_set_bcsh(hdw);
+ }
+ if (vmsk & PVR2_SUBSYS_ENC_CFG) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_configure");
+ ret = pvr2_encoder_configure(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_decoder_enable_output(1)");
+ pvr2_decoder_enable_output(hdw,!0);
+ }
+ if (vmsk & PVR2_SUBSYS_USBSTREAM_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_hdw_cmd_usbstream(1)");
+ pvr2_hdw_cmd_usbstream(hdw,!0);
+ }
+ if (vmsk & PVR2_SUBSYS_ENC_RUN) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_start");
+ ret = pvr2_encoder_start(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ }
+ }
+}
+
+
+void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk)
+{
+ pvr2_hdw_subsys_bit_chg(hdw,msk,msk);
+}
+
+
+void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk)
+{
+ pvr2_hdw_subsys_bit_chg(hdw,msk,0);
+}
+
+
+unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
+{
+ return hdw->subsys_enabled_mask;
+}
+
+
+unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
+{
+ return hdw->subsys_stream_mask;
+}
+
+
+void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,
+ unsigned long val)
+{
+ unsigned long val2;
+ msk &= PVR2_SUBSYS_ALL;
+ val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk));
+ pvr2_trace(PVR2_TRACE_INIT,
+ "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx",
+ msk,val,hdw->subsys_stream_mask,val2);
+ hdw->subsys_stream_mask = val2;
+}
+
+
+void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,
+ unsigned long val)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id)
+{
+ return hdw->flag_streaming_enabled != 0;
+}
+
+
+int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+{
+ if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
+ if (enableFl) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ enable");
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0);
+ } else {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ disable");
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
+ }
+ if (!hdw->flag_ok) return -EIO;
+ hdw->flag_streaming_enabled = enableFl != 0;
+ return 0;
+}
+
+
+int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
+{
+ return hdw->flag_streaming_enabled != 0;
+}
+
+
+int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
+{
+ int ret;
+ LOCK_TAKE(hdw->big_lock); do {
+ ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
+ enum pvr2_config config)
+{
+ unsigned long sm = hdw->subsys_enabled_mask;
+ if (!hdw->flag_ok) return -EIO;
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
+ hdw->config = config;
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm);
+ return 0;
+}
+
+
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
+{
+ int ret;
+ if (!hdw->flag_ok) return -EIO;
+ LOCK_TAKE(hdw->big_lock);
+ ret = pvr2_hdw_set_stream_type_no_lock(hdw,config);
+ LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ if (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints == 0) {
+ if (pvr2_upload_firmware1(hdw) != 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failure uploading firmware1");
+ }
+ return;
+ }
+
+ if (initusbreset) {
+ pvr2_hdw_device_reset(hdw);
+ }
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_i2c_init(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ if (pvr2_upload_firmware2(hdw)){
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!");
+ pvr2_hdw_render_useless(hdw);
+ return;
+ }
+
+ for (idx = 0; idx < PVR2_CID_COUNT; idx++) {
+ if (control_defs[idx].skip_init) continue;
+ pvr2_hdw_set_ctl_value_internal(
+ hdw,idx,control_defs[idx].default_value);
+ }
+
+ pvr2_reset_ctl_endpoints(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ if (!pvr2_tuner_get_default_type(hdw)) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: Tuner type overridden to %d",
+ hdw->tuner_type);
+ }
+
+ pvr2_tuner_set_type(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_eeprom_set_default_standard(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_hdw_commit_ctl_internal(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ hdw->vid_stream = pvr2_stream_create();
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
+ if (hdw->vid_stream) {
+ pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
+ PVR2_VID_ENDPOINT);
+ }
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ hdw->flag_init_ok = !0;
+}
+
+
+int pvr2_hdw_setup(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_setup_low(hdw);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
+ hdw,hdw->flag_ok,hdw->flag_init_ok);
+ if (pvr2_hdw_dev_ok(hdw)) {
+ if (pvr2_hdw_init_ok(hdw)) {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device initialization"
+ " completed successfully.");
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device firmware (re)load executed;"
+ " it should now reset and reconnect.");
+ }
+ break;
+ }
+ if (procreload) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempting pvrusb2 recovery by reloading"
+ " primary firmware.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "If this works, device should disconnect"
+ " and reconnect in a sane state.");
+ pvr2_upload_firmware1(hdw);
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** pvrusb2 device hardware"
+ " appears to be jammed"
+ " and I can't clear it.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "You might need to power cycle"
+ " the pvrusb2 device"
+ " in order to recover.");
+ }
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return hdw->flag_init_ok;
+}
+
+
+/* Create and return a structure for interacting with the underlying
+ hardware */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf)
+{
+ unsigned int idx,cnt1,cnt2;
+ struct pvr2_hdw *hdw;
+ __u8 ifnum;
+
+ hdw = kmalloc(sizeof(*hdw),GFP_KERNEL);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p",hdw);
+ if (!hdw) goto fail;
+ memset(hdw,0,sizeof(*hdw));
+ hdw->unit_number = -1;
+ hdw->v4l_minor_number = -1;
+ hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_write_buffer) goto fail;
+ hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_read_buffer) goto fail;
+ hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_write_urb) goto fail;
+ hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_read_urb) goto fail;
+
+ down(&pvr2_unit_sem); do {
+ for (idx = 0; idx < PVR_NUM; idx++) {
+ if (unit_pointers[idx]) continue;
+ hdw->unit_number = idx;
+ unit_pointers[idx] = hdw;
+ break;
+ }
+ } while (0); up(&pvr2_unit_sem);
+
+ cnt1 = 0;
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
+ cnt1 += cnt2;
+ if (hdw->unit_number >= 0) {
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
+ ('a' + hdw->unit_number));
+ cnt1 += cnt2;
+ }
+ if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
+ hdw->name[cnt1] = 0;
+
+ pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
+ hdw->unit_number,hdw->name);
+
+ hdw->tuner_type = -1;
+ hdw->flag_ok = !0;
+ /* Initialize the mask of subsystems that we will shut down when we
+ stop streaming. */
+ hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL;
+ hdw->subsys_stream_mask |= PVR2_SUBSYS_ENC_CFG;
+
+ pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx",
+ hdw->subsys_stream_mask);
+
+ hdw->usb_intf = intf;
+ hdw->usb_dev = interface_to_usbdev(intf);
+
+ ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ usb_set_interface(hdw->usb_dev,ifnum,0);
+
+ init_MUTEX(&hdw->ctl_lock_sem);
+ init_MUTEX(&hdw->big_lock_sem);
+
+
+ return hdw;
+ fail:
+ if (hdw) {
+ if (hdw->ctl_read_urb) usb_free_urb(hdw->ctl_read_urb);
+ if (hdw->ctl_write_urb) usb_free_urb(hdw->ctl_write_urb);
+ if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer);
+ if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer);
+ kfree(hdw);
+ }
+ return 0;
+}
+
+
+/* Remove _all_ associations between this driver and the underlying USB
+ layer. */
+void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
+{
+ if (hdw->flag_disconnected) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
+ if (hdw->ctl_read_urb) {
+ usb_kill_urb(hdw->ctl_read_urb);
+ usb_free_urb(hdw->ctl_read_urb);
+ hdw->ctl_read_urb = 0;
+ }
+ if (hdw->ctl_write_urb) {
+ usb_kill_urb(hdw->ctl_write_urb);
+ usb_free_urb(hdw->ctl_write_urb);
+ hdw->ctl_write_urb = 0;
+ }
+ if (hdw->ctl_read_buffer) {
+ kfree(hdw->ctl_read_buffer);
+ hdw->ctl_read_buffer = 0;
+ }
+ if (hdw->ctl_write_buffer) {
+ kfree(hdw->ctl_write_buffer);
+ hdw->ctl_write_buffer = 0;
+ }
+ pvr2_hdw_render_useless_unlocked(hdw);
+ hdw->flag_disconnected = !0;
+ hdw->usb_dev = 0;
+ hdw->usb_intf = 0;
+}
+
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+ if (hdw->fw_buffer) {
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = 0;
+ }
+ if (hdw->vid_stream) {
+ pvr2_stream_destroy(hdw->vid_stream);
+ hdw->vid_stream = 0;
+ }
+ pvr2_i2c_done(hdw);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ down(&pvr2_unit_sem); do {
+ if ((hdw->unit_number >= 0) &&
+ (hdw->unit_number < PVR_NUM) &&
+ (unit_pointers[hdw->unit_number] == hdw)) {
+ unit_pointers[hdw->unit_number] = 0;
+ }
+ } while (0); up(&pvr2_unit_sem);
+ kfree(hdw);
+}
+
+
+int pvr2_hdw_init_ok(struct pvr2_hdw *hdw)
+{
+ return hdw->flag_init_ok;
+}
+
+
+int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
+{
+ return (hdw && hdw->flag_ok);
+}
+
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
+ LOCK_TAKE(hdw->big_lock);
+ LOCK_TAKE(hdw->ctl_lock);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ LOCK_GIVE(hdw->ctl_lock);
+ LOCK_GIVE(hdw->big_lock);
+}
+
+
+static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value)
+{
+ /* This is a special case; the value to store is to an array, and
+ the element to select is determined by PVR_CID_CHANPROG_ID. */
+ int id = hdw->controls[PVR2_CID_CHANPROG_ID].value;
+ if ((id < 1) || (id > FREQTABLE_SIZE)) return 0;
+ hdw->freqTable[id-1] = value;
+ if (hdw->controls[PVR2_CID_CHANNEL].value == id) {
+ /* If the current channel happens to be the slot we just
+ set, then act like the current channel just got changed
+ so we'll update that too. */
+ hdw->controls[PVR2_CID_CHANNEL].dirty = !0;
+ }
+ return 0;
+}
+
+
+static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id)
+{
+ /* This is a special case; the value to return is from an array,
+ and the element to select is determined by
+ PVR_CID_CHANPROG_ID. */
+ int id = hdw->controls[PVR2_CID_CHANPROG_ID].value;
+ if ((id < 1) || (id > FREQTABLE_SIZE)) return 0;
+ return hdw->freqTable[id-1];
+}
+
+
+/* Retrieve current value for a given control */
+int pvr2_hdw_get_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ int ret = 0;
+
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (control_defs[ctl_id].get_func) {
+ ret = control_defs[ctl_id].get_func(hdw,ctl_id);
+ break;
+ }
+ ret = hdw->controls[ctl_id].value;
+ } while(0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+/* Return true if control is writable */
+int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ if (control_defs[ctl_id].get_func && !control_defs[ctl_id].set_func) {
+ return 0;
+ }
+ return !0;
+}
+
+
+/* Retrieve legal minimum value for a given control */
+int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ return control_defs[ctl_id].min_value;
+}
+
+
+/* Retrieve legal maximum value for a given control */
+int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ return control_defs[ctl_id].max_value;
+}
+
+
+/* Set current value for given control - normally this is just stored and
+ the hardware isn't updated until the commit function is called. */
+int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw,
+ unsigned int ctl_id,int value)
+{
+ if (ctl_id >= PVR2_CID_COUNT) return -EINVAL;
+ if (value < control_defs[ctl_id].min_value) return -EINVAL;
+ if (value > control_defs[ctl_id].max_value) return -EINVAL;
+ if (control_defs[ctl_id].set_func) {
+ return control_defs[ctl_id].set_func(hdw,ctl_id,value);
+ } else if (control_defs[ctl_id].get_func) {
+ /* If there's no "set" function yet there is still a "get"
+ function, then treat this as a read-only value. */
+ return -EINVAL;
+ }
+ hdw->controls[ctl_id].value = value;
+ hdw->controls[ctl_id].dirty = !0;
+ return 0;
+}
+
+
+/* Set current value for given control - this is just stored; the hardware
+ isn't updated until the commit function is called. */
+int pvr2_hdw_set_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id,int value)
+{
+ int ret;
+ LOCK_TAKE(hdw->big_lock); do {
+ ret = pvr2_hdw_set_ctl_value_internal(hdw,ctl_id,value);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve string name for a given control value (returns a null pointer
+ for any invalid combinations). */
+const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *hdw,
+ unsigned int ctl_id,
+ int value)
+{
+ struct pvr2_ctl_def *cdef;
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ cdef = control_defs + ctl_id;
+ if (! cdef->value_defs_ptr) return 0;
+ if (value >= cdef->value_defs_count) return 0;
+ return cdef->value_defs_ptr[value];
+}
+
+
+/* Retrieve string name for given control */
+const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ if (ctl_id >= PVR2_CID_COUNT) return 0;
+ return control_defs[ctl_id].name;
+}
+
+
+/* Commit all control changes made up to this point. Subsystems can be
+ indirectly affected by these changes. For a given set of things being
+ committed, we'll clear the affected subsystem bits and then once we're
+ done committing everything we'll make a request to restore the subsystem
+ state(s) back to their previous value before this function was called.
+ Thus we can automatically reconfigure affected pieces of the driver as
+ controls are changed. */
+int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+{
+ unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
+ unsigned long stale_subsys_mask = 0;
+ unsigned int idx;
+ int value;
+ const char *ctl_name;
+ const char *ctl_value;
+ int commit_flag = 0;
+
+ /* Let's see if the channel changed and we have to update the
+ frequency because of it. This setup means one can tune the
+ receiver either by just setting the channel (using the frequency
+ table), or by directly programming the frequency. How do we
+ resolve the obvious conflict here? The direct frequency takes
+ priority; if directly set then we commit that value and force
+ the channel to zero which is interpreted to mean "none". If on
+ the other hand we see that the channel has been set and it's a
+ legal value, then we copy that into the frequency this. The
+ metaphor here is similar to when you tune your digital radio:
+ You an either set a frequency directly or punch up a
+ pre-programmed station. Either way a frequency is set, and if
+ you do use a preset, then the radio also shows you which preset
+ it is - until you override that by directly entering a new
+ frequency. */
+ if (hdw->controls[PVR2_CID_FREQUENCY].dirty) {
+ /* Frequency has been directly set, so clear out the
+ channel. */
+ hdw->controls[PVR2_CID_CHANNEL].value = 0;
+ } else if (hdw->controls[PVR2_CID_CHANNEL].dirty) {
+ int id = hdw->controls[PVR2_CID_CHANNEL].value;
+ if ((id > 0) && (id <= FREQTABLE_SIZE)) {
+ if (hdw->controls[PVR2_CID_FREQUENCY].value !=
+ hdw->freqTable[id-1]) {
+ hdw->controls[PVR2_CID_FREQUENCY].value =
+ hdw->freqTable[id-1];
+ hdw->controls[PVR2_CID_FREQUENCY].dirty = !0;
+ }
+ }
+ }
+
+ for (idx = 0; idx < PVR2_CID_COUNT; idx++) {
+ if (!hdw->controls[idx].dirty) continue;
+ if (!commit_flag) {
+ commit_flag = !0;
+ }
+ value = hdw->controls[idx].value;
+ ctl_name = control_defs[idx].name;
+ if (control_defs[idx].value_defs_ptr) {
+ if (value < control_defs[idx].value_defs_count) {
+ ctl_value =
+ control_defs[idx].value_defs_ptr[value];
+ } else {
+ ctl_value = "<out of range>";
+ }
+ } else {
+ ctl_value = "<integer>";
+ }
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*--TRACE_COMMIT--*/ \"%s\" <-- %d (%s)",
+ ctl_name,value,ctl_value);
+ }
+
+ if (!commit_flag) {
+ /* Nothing has changed */
+ return 0;
+ }
+
+ /* When video standard changes, reset the hres and vres values -
+ but if the user has pending changes there, then let the changes
+ take priority. */
+ if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty) {
+ if (!hdw->controls[PVR2_CID_VRES].dirty) {
+ /* Rewrite the vertical resolution to be
+ appropriate to the video standard that has been
+ selected. */
+ int nvres = hdw->controls[PVR2_CID_VRES].value;
+ switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) {
+ case PVR2_CVAL_VIDEOSTANDARD_NTSC_M:
+ nvres = 480;
+ break;
+ default:
+ nvres = 576;
+ }
+ if (nvres != hdw->controls[PVR2_CID_VRES].value) {
+ hdw->controls[PVR2_CID_VRES].value = nvres;
+ hdw->controls[PVR2_CID_VRES].dirty = !0;
+ }
+ }
+ if (!hdw->controls[PVR2_CID_INTERLACE].dirty) {
+ if (!hdw->controls[PVR2_CID_INTERLACE].value) {
+ hdw->controls[PVR2_CID_INTERLACE].value = 0;
+ hdw->controls[PVR2_CID_INTERLACE].dirty = !0;
+ }
+ }
+ }
+
+ if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty) {
+ hdw->controls[PVR2_CID_FREQUENCY].dirty = !0;
+ stale_subsys_mask |= (PVR2_SUBSYS_DIGITIZER_CFG_NORM |
+ PVR2_SUBSYS_TUNER_CFG_STD |
+ PVR2_SUBSYS_AUDIO_CFG_STD);
+ }
+
+ if (hdw->controls[PVR2_CID_HRES].dirty ||
+ hdw->controls[PVR2_CID_VRES].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_SIZE;
+ }
+
+ if (hdw->controls[PVR2_CID_BRIGHTNESS].dirty ||
+ hdw->controls[PVR2_CID_CONTRAST].dirty ||
+ hdw->controls[PVR2_CID_SATURATION].dirty ||
+ hdw->controls[PVR2_CID_HUE].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_BCSH;
+ }
+
+ if (hdw->controls[PVR2_CID_SRATE].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_AUDIO;
+ }
+
+ if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty ||
+ hdw->controls[PVR2_CID_VRES].dirty ||
+ hdw->controls[PVR2_CID_HRES].dirty ||
+ hdw->controls[PVR2_CID_INTERLACE].dirty ||
+ hdw->controls[PVR2_CID_VBR].dirty ||
+ hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].dirty ||
+ hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].dirty ||
+ hdw->controls[PVR2_CID_AUDIOBITRATE].dirty ||
+ hdw->controls[PVR2_CID_SRATE].dirty ||
+ hdw->controls[PVR2_CID_AUDIOLAYER].dirty ||
+ hdw->controls[PVR2_CID_AUDIOCRC].dirty ||
+ hdw->controls[PVR2_CID_AUDIOEMPHASIS].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_ENC_CFG;
+ }
+
+ if (hdw->controls[PVR2_CID_AUDIOMODE].dirty ||
+ hdw->controls[PVR2_CID_INPUT].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_AUDIO_CFG_MODE;
+ }
+
+ if (hdw->controls[PVR2_CID_VOLUME].dirty ||
+ hdw->controls[PVR2_CID_BALANCE].dirty ||
+ hdw->controls[PVR2_CID_BASS].dirty ||
+ hdw->controls[PVR2_CID_TREBLE].dirty ||
+ hdw->controls[PVR2_CID_MUTE].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_AUDIO_CFG_VBBTM;
+ }
+
+ if (hdw->controls[PVR2_CID_INPUT].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_INPUT;
+ }
+
+ if (hdw->controls[PVR2_CID_FREQUENCY].dirty) {
+ stale_subsys_mask |= PVR2_SUBSYS_TUNER_CFG_FREQ;
+ }
+
+ if (stale_subsys_mask & (PVR2_SUBSYS_DIGITIZER_CFG_NORM |
+ PVR2_SUBSYS_TUNER_CFG_STD |
+ PVR2_SUBSYS_AUDIO_CFG_STD |
+ PVR2_SUBSYS_DIGITIZER_CFG_SIZE |
+ PVR2_SUBSYS_DIGITIZER_CFG_AUDIO |
+ PVR2_SUBSYS_ENC_CFG)) {
+ stale_subsys_mask |= hdw->subsys_stream_mask;
+ }
+
+ for (idx = 0; idx < PVR2_CID_COUNT; idx++) {
+ hdw->controls[idx].dirty = 0;
+ }
+
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0);
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask);
+
+ return 0;
+}
+
+
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_commit_ctl_internal(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return 0;
+}
+
+
+/* Find out how many controls there are. Legal ids are numbered from 1
+ through this value. */
+unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *hdw)
+{
+ return PVR2_CID_COUNT;
+}
+
+
+/* Return bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw)
+{
+ unsigned int msk = 0;
+ switch (hdw->controls[PVR2_CID_INPUT].value) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_RADIO:
+ if (pvr2_decoder_is_tuned(hdw)) {
+ msk |= PVR2_SIGNAL_OK;
+ if (pvr2_audio_update_status(hdw) == 0) {
+ if (hdw->flag_stereo) {
+ msk |= PVR2_SIGNAL_STEREO;
+ }
+ if (hdw->flag_bilingual) {
+ msk |= PVR2_SIGNAL_SAP;
+ }
+ }
+ }
+ break;
+ default:
+ msk |= PVR2_SIGNAL_OK | PVR2_SIGNAL_STEREO;
+ }
+ return msk;
+}
+
+
+static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id)
+{
+ return hdw->subsys_enabled_mask;
+}
+
+
+static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val)
+{
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,val);
+ return 0;
+}
+
+
+static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id)
+{
+ return hdw->subsys_stream_mask;
+}
+
+
+static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id,
+ int val)
+{
+ pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,~0,val);
+ return 0;
+}
+
+
+static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id)
+{
+ int result = pvr2_hdw_is_hsm(hdw);
+ if (result < 0) return PVR2_CVAL_HSM_FAIL;
+ if (result) return PVR2_CVAL_HSM_HIGH;
+ return PVR2_CVAL_HSM_FULL;
+}
+
+
+int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
+{
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = 0x0b;
+ result = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ result = (hdw->cmd_buffer[0] != 0);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ return result;
+}
+
+
+static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id)
+{
+ return ((pvr2_hdw_get_signal_status_internal(hdw) & PVR2_SIGNAL_OK) ?
+ 1 : 0);
+}
+
+
+/* Return bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw)
+{
+ unsigned int msk = 0;
+ LOCK_TAKE(hdw->big_lock); do {
+ msk = pvr2_hdw_get_signal_status_internal(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return msk;
+}
+
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
+{
+ return hp->vid_stream;
+}
+
+
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
+{
+ int ret;
+ u16 address;
+ unsigned int pipe;
+ LOCK_TAKE(hdw->big_lock); do {
+ if ((hdw->fw_buffer == 0) == !enable_flag) break;
+
+ if (!enable_flag) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Cleaning up after CPU firmware fetch");
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = 0;
+ hdw->fw_size = 0;
+ /* Now release the CPU. It will disconnect and
+ reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+ break;
+ }
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Preparing to suck out CPU firmware");
+ hdw->fw_size = 0x2000;
+ hdw->fw_buffer = kmalloc(hdw->fw_size,GFP_KERNEL);
+ if (!hdw->fw_buffer) {
+ hdw->fw_size = 0;
+ break;
+ }
+
+ memset(hdw->fw_buffer,0,hdw->fw_size);
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* download the firmware from address 0000-1fff in 2048
+ (=0x800) bytes chunk. */
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,"Grabbing CPU firmware");
+ pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
+ for(address = 0; address < hdw->fw_size; address += 0x800) {
+ ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0xc0,
+ address,0,
+ hdw->fw_buffer+address,0x800,HZ);
+ if (ret < 0) break;
+ }
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,"Done grabbing CPU firmware");
+
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
+{
+ return hdw->fw_buffer != 0;
+}
+
+
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
+ char *buf,unsigned int cnt)
+{
+ int ret = -EINVAL;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (!buf) break;
+ if (!cnt) break;
+
+ if (!hdw->fw_buffer) {
+ ret = -EIO;
+ break;
+ }
+
+ if (offs >= hdw->fw_size) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d EOF",
+ offs);
+ ret = 0;
+ break;
+ }
+
+ if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
+
+ memcpy(buf,hdw->fw_buffer+offs,cnt);
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d cnt=%d",
+ offs,cnt);
+ ret = cnt;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+
+ return ret;
+}
+
+
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw)
+{
+ return hdw->v4l_minor_number;
+}
+
+
+/* Store the v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v)
+{
+ hdw->v4l_minor_number = v;
+}
+
+
+void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw)
+{
+ if (!hdw->usb_dev) return;
+ usb_settoggle(hdw->usb_dev, PVR2_CTL_WRITE_ENDPOINT & 0xf,
+ !(PVR2_CTL_WRITE_ENDPOINT & USB_DIR_IN), 0);
+ usb_settoggle(hdw->usb_dev, PVR2_CTL_READ_ENDPOINT & 0xf,
+ !(PVR2_CTL_READ_ENDPOINT & USB_DIR_IN), 0);
+ usb_clear_halt(hdw->usb_dev,
+ usb_rcvbulkpipe(hdw->usb_dev,
+ PVR2_CTL_READ_ENDPOINT & 0x7f));
+ usb_clear_halt(hdw->usb_dev,
+ usb_sndbulkpipe(hdw->usb_dev,
+ PVR2_CTL_WRITE_ENDPOINT & 0x7f));
+}
+
+
+static void pvr2_ctl_write_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_write_pend_flag = 0;
+ if (hdw->ctl_read_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_read_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_read_pend_flag = 0;
+ if (hdw->ctl_write_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ hdw->ctl_timeout_flag = !0;
+ if (hdw->ctl_write_pend_flag && hdw->ctl_write_urb) {
+ usb_unlink_urb(hdw->ctl_write_urb);
+ }
+ if (hdw->ctl_read_pend_flag && hdw->ctl_read_urb) {
+ usb_unlink_urb(hdw->ctl_read_urb);
+ }
+ }
+}
+
+
+int pvr2_send_request(struct pvr2_hdw *hdw,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len)
+{
+ unsigned int idx;
+ int status;
+ struct timer_list timer;
+ if (!hdw->ctl_lock_held) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " without lock!!");
+ status = -EINVAL;
+ goto done;
+ }
+ if (!hdw->flag_ok) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when device not ok");
+ return -EIO;
+ }
+ if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when USB is disconnected");
+ return -EIO;
+ }
+
+ /* Ensure that we have sane parameters */
+ if (!write_data) write_len = 0;
+ if (!read_data) read_len = 0;
+ if (write_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-write transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if (read_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-read transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if ((!write_len) && (!read_len)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute null control transfer?");
+ return -EINVAL;
+ }
+
+ hdw->cmd_debug_state = 1;
+ if (write_len) {
+ hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
+ } else {
+ hdw->cmd_debug_code = 0;
+ }
+ hdw->cmd_debug_write_len = write_len;
+ hdw->cmd_debug_read_len = read_len;
+
+ /* Initialize common stuff */
+ init_completion(&hdw->ctl_done);
+ hdw->ctl_timeout_flag = 0;
+ hdw->ctl_write_pend_flag = 0;
+ hdw->ctl_read_pend_flag = 0;
+ init_timer(&timer);
+ timer.expires = jiffies + HZ*4;
+ timer.data = (unsigned long)hdw;
+ timer.function = pvr2_ctl_timeout;
+
+ if (write_len) {
+ hdw->cmd_debug_state = 2;
+ /* Transfer write data to internal buffer */
+ for (idx = 0; idx < write_len; idx++) {
+ hdw->ctl_write_buffer[idx] =
+ ((unsigned char *)write_data)[idx];
+ }
+ /* Initiate a write request */
+ usb_fill_bulk_urb(hdw->ctl_write_urb,
+ hdw->usb_dev,
+ usb_sndbulkpipe(hdw->usb_dev,
+ PVR2_CTL_WRITE_ENDPOINT),
+ hdw->ctl_write_buffer,
+ write_len,
+ pvr2_ctl_write_complete,
+ hdw);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ hdw->ctl_write_urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+ hdw->ctl_write_urb->actual_length = 0;
+ hdw->ctl_write_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit write-control"
+ " URB status=%d",status);
+ hdw->ctl_write_pend_flag = 0;
+ }
+ }
+
+ if (read_len) {
+ hdw->cmd_debug_state = 3;
+ memset(hdw->ctl_read_buffer,0x43,read_len);
+ /* Initiate a read request */
+ usb_fill_bulk_urb(hdw->ctl_read_urb,
+ hdw->usb_dev,
+ usb_rcvbulkpipe(hdw->usb_dev,
+ PVR2_CTL_READ_ENDPOINT),
+ hdw->ctl_read_buffer,
+ read_len,
+ pvr2_ctl_read_complete,
+ hdw);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ hdw->ctl_read_urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+ hdw->ctl_read_urb->actual_length = 0;
+ hdw->ctl_read_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit read-control"
+ " URB status=%d",status);
+ hdw->ctl_read_pend_flag = 0;
+ }
+ }
+
+ /* Start timer */
+ add_timer(&timer);
+
+ /* Now wait for all I/O to complete */
+ hdw->cmd_debug_state = 4;
+ while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ wait_for_completion(&hdw->ctl_done);
+ }
+ hdw->cmd_debug_state = 5;
+
+ /* Stop timer */
+ del_timer_sync(&timer);
+
+ hdw->cmd_debug_state = 6;
+ status = 0;
+
+ if (hdw->ctl_timeout_flag) {
+ status = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (write_len) {
+ /* Validate results of write request */
+ if ((hdw->ctl_write_urb->status != 0) &&
+ (hdw->ctl_write_urb->status != -ENOENT) &&
+ (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_write_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the write */
+ status = hdw->ctl_write_urb->status;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB failure, status=%d",
+ status);
+ goto done;
+ }
+ if (hdw->ctl_write_urb->actual_length < write_len) {
+ /* Failed to write enough data */
+ status = -EIO;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB short,"
+ " expected=%d got=%d",
+ write_len,
+ hdw->ctl_write_urb->actual_length);
+ goto done;
+ }
+ }
+ if (read_len) {
+ /* Validate results of read request */
+ if ((hdw->ctl_read_urb->status != 0) &&
+ (hdw->ctl_read_urb->status != -ENOENT) &&
+ (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_read_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the read */
+ status = hdw->ctl_read_urb->status;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB failure, status=%d",
+ status);
+ goto done;
+ }
+ if (hdw->ctl_read_urb->actual_length < read_len) {
+ /* Failed to read enough data */
+ status = -EIO;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB short,"
+ " expected=%d got=%d",
+ read_len,hdw->ctl_read_urb->actual_length);
+ goto done;
+ }
+ /* Transfer retrieved data out from internal buffer */
+ for (idx = 0; idx < read_len; idx++) {
+ ((unsigned char *)read_data)[idx] =
+ hdw->ctl_read_buffer[idx];
+ }
+ }
+
+ done:
+ hdw->cmd_debug_state = 0;
+ if (status < 0) {
+ pvr2_hdw_render_useless_unlocked(hdw);
+ }
+ return status;
+}
+
+
+int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = 0x04; /* write register prefix */
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
+{
+ int ret = 0;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = 0x05; /* read register prefix */
+ hdw->cmd_buffer[1] = 0;
+ hdw->cmd_buffer[2] = 0;
+ hdw->cmd_buffer[3] = 0;
+ hdw->cmd_buffer[4] = 0;
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+ ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
+ *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = (data >> 8) & 0xff;
+ hdw->cmd_buffer[1] = data & 0xff;
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 2, hdw->cmd_buffer, res);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = data;
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 1, hdw->cmd_buffer, res);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+{
+ if (!hdw->flag_ok) return;
+ pvr2_trace(PVR2_TRACE_INIT,"render_useless");
+ hdw->flag_ok = 0;
+ if (hdw->vid_stream) {
+ pvr2_stream_setup(hdw->vid_stream,0,0);
+ }
+ hdw->flag_streaming_enabled = 0;
+ hdw->subsys_enabled_mask = 0;
+}
+
+
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->ctl_lock);
+ pvr2_hdw_render_useless_unlocked(hdw);
+ LOCK_GIVE(hdw->ctl_lock);
+}
+
+
+void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
+{
+ int ret;
+ pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
+ ret = usb_lock_device_for_reset(hdw->usb_dev,0);
+ if (ret == 1) {
+ ret = usb_reset_device(hdw->usb_dev);
+ usb_unlock_device(hdw->usb_dev);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to lock USB device ret=%d",ret);
+ }
+}
+
+
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
+{
+ char da[1];
+ unsigned int pipe;
+ int ret;
+
+ if (!hdw->usb_dev) return;
+
+ pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
+
+ da[0] = val ? 0x01 : 0x00;
+
+ /* Write the CPUCS register on the 8051. The lsb of the register
+ is the reset bit; a 1 asserts reset while a 0 clears it. */
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "cpureset_assert(%d) error=%d",val,ret);
+ pvr2_hdw_render_useless(hdw);
+ }
+}
+
+
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
+ hdw->flag_ok = !0;
+ hdw->cmd_buffer[0] = 0xdd;
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ return status;
+}
+
+
+int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc soft reset");
+ hdw->cmd_buffer[0] = 0xde;
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ return status;
+}
+
+
+int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = (runFl ? 0x36 : 0x37);
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ if (!status) {
+ hdw->subsys_enabled_mask =
+ ((hdw->subsys_enabled_mask &
+ ~PVR2_SUBSYS_USBSTREAM_RUN) |
+ (runFl ? PVR2_SUBSYS_USBSTREAM_RUN : 0));
+ }
+ return status;
+}
+
+
+void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
+{
+ ptr->big_lock_held = hdw->big_lock_held;
+ ptr->ctl_lock_held = hdw->ctl_lock_held;
+ ptr->flag_ok = hdw->flag_ok;
+ ptr->flag_disconnected = hdw->flag_disconnected;
+ ptr->flag_init_ok = hdw->flag_init_ok;
+ ptr->flag_streaming_enabled = hdw->flag_streaming_enabled;
+ ptr->subsys_flags = hdw->subsys_enabled_mask;
+ ptr->cmd_debug_state = hdw->cmd_debug_state;
+ ptr->cmd_code = hdw->cmd_debug_code;
+ ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
+ ptr->cmd_debug_read_len = hdw->cmd_debug_read_len;
+ ptr->cmd_debug_timeout = hdw->ctl_timeout_flag;
+ ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag;
+ ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag;
+ ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status;
+ ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status;
+}
+
+
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
+}
+
+
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
+}
+
+
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
+}
+
+
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing 0x%x:0x%x"
+ " from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
+}
+
+
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw.h b/v4l_experimental/pvrusb2/pvrusb2-hdw.h
new file mode 100644
index 000000000..55feeaac2
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-hdw.h
@@ -0,0 +1,415 @@
+/*
+ *
+ * $Id: pvrusb2-hdw.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_HDW_H
+#define __PVRUSB2_HDW_H
+
+#include <linux/usb.h>
+#include "pvrusb2-io.h"
+
+/* Definition of state variables that we can inspect & change. Numbers are
+ assigned from zero counting up with no gaps. */
+#define PVR2_CID_BRIGHTNESS 0
+#define PVR2_CID_CONTRAST 1
+#define PVR2_CID_SATURATION 2
+#define PVR2_CID_HUE 3
+#define PVR2_CID_VOLUME 4
+#define PVR2_CID_BALANCE 5
+#define PVR2_CID_BASS 6
+#define PVR2_CID_TREBLE 7
+#define PVR2_CID_MUTE 8
+#define PVR2_CID_SRATE 9
+#define PVR2_CID_AUDIOBITRATE 10
+#define PVR2_CID_AUDIOCRC 11
+#define PVR2_CID_AUDIOEMPHASIS 12
+#define PVR2_CID_VBR 13
+#define PVR2_CID_AVERAGEVIDEOBITRATE 14
+#define PVR2_CID_PEAKVIDEOBITRATE 15
+#define PVR2_CID_VIDEOSTANDARD 16
+#define PVR2_CID_INPUT 17
+#define PVR2_CID_AUDIOMODE 18
+#define PVR2_CID_FREQUENCY 19 // Units of Hz
+#define PVR2_CID_HRES 20
+#define PVR2_CID_VRES 21
+#define PVR2_CID_INTERLACE 22
+#define PVR2_CID_AUDIOLAYER 23
+#define PVR2_CID_CHANNEL 24
+#define PVR2_CID_CHANPROG_ID 25
+#define PVR2_CID_CHANPROG_FREQ 26
+#define PVR2_CID_SIGNAL_PRESENT 27
+#define PVR2_CID_STREAMING_ENABLED 28
+#define PVR2_CID_HSM 29
+#define PVR2_CID_SUBSYS_MASK 30
+#define PVR2_CID_SUBSYS_STREAM_MASK 31
+
+/* Number of state variables */
+#define PVR2_CID_COUNT 32
+
+/* Legal values for the SRATE state variable */
+#define PVR2_CVAL_SRATE_48 0
+#define PVR2_CVAL_SRATE_44_1 1
+#define PVR2_CVAL_SRATE_MIN PVR2_CVAL_SRATE_48
+#define PVR2_CVAL_SRATE_MAX PVR2_CVAL_SRATE_44_1
+
+/* Legal values for the AUDIOBITRATE state variable */
+#define PVR2_CVAL_AUDIOBITRATE_384 0
+#define PVR2_CVAL_AUDIOBITRATE_320 1
+#define PVR2_CVAL_AUDIOBITRATE_256 2
+#define PVR2_CVAL_AUDIOBITRATE_224 3
+#define PVR2_CVAL_AUDIOBITRATE_192 4
+#define PVR2_CVAL_AUDIOBITRATE_160 5
+#define PVR2_CVAL_AUDIOBITRATE_128 6
+#define PVR2_CVAL_AUDIOBITRATE_112 7
+#define PVR2_CVAL_AUDIOBITRATE_96 8
+#define PVR2_CVAL_AUDIOBITRATE_80 9
+#define PVR2_CVAL_AUDIOBITRATE_64 10
+#define PVR2_CVAL_AUDIOBITRATE_56 11
+#define PVR2_CVAL_AUDIOBITRATE_48 12
+#define PVR2_CVAL_AUDIOBITRATE_32 13
+#define PVR2_CVAL_AUDIOBITRATE_VBR 14
+#define PVR2_CVAL_AUDIOBITRATE_MIN PVR2_CVAL_AUDIOBITRATE_384
+#define PVR2_CVAL_AUDIOBITRATE_MAX PVR2_CVAL_AUDIOBITRATE_VBR
+
+/* Legal values for the AUDIOEMPHASIS state variable */
+#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0
+#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1
+#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2
+#define PVR2_CVAL_AUDIOEMPHASIS_MIN PVR2_CVAL_AUDIOEMPHASIS_NONE
+#define PVR2_CVAL_AUDIOEMPHASIS_MAX PVR2_CVAL_AUDIOEMPHASIS_CCITT
+
+/* Legal values for the VIDEOSTANDARD state variable */
+#define PVR2_CVAL_VIDEOSTANDARD_NTSC_M 0
+#define PVR2_CVAL_VIDEOSTANDARD_SECAM_L 1
+#define PVR2_CVAL_VIDEOSTANDARD_PAL_BG 2
+#define PVR2_CVAL_VIDEOSTANDARD_PAL_I 3
+#define PVR2_CVAL_VIDEOSTANDARD_PAL_DK 4
+#define PVR2_CVAL_VIDEOSTANDARD_MIN PVR2_CVAL_VIDEOSTANDARD_NTSC_M
+#define PVR2_CVAL_VIDEOSTANDARD_MAX PVR2_CVAL_VIDEOSTANDARD_PAL_DK
+
+/* Legal values for the INPUT state variable */
+#define PVR2_CVAL_INPUT_TV 0
+#define PVR2_CVAL_INPUT_SVIDEO 1
+#define PVR2_CVAL_INPUT_COMPOSITE 2
+#define PVR2_CVAL_INPUT_RADIO 3
+#define PVR2_CVAL_INPUT_MIN PVR2_CVAL_INPUT_TV
+#define PVR2_CVAL_INPUT_MAX PVR2_CVAL_INPUT_RADIO
+
+/* Legal values for the AUDIOMODE state variable */
+#define PVR2_CVAL_AUDIOMODE_MONO 0
+#define PVR2_CVAL_AUDIOMODE_STEREO 1
+#define PVR2_CVAL_AUDIOMODE_SAP 2
+#define PVR2_CVAL_AUDIOMODE_LANG1 3
+#define PVR2_CVAL_AUDIOMODE_LANG2 4
+#define PVR2_CVAL_AUDIOMODE_MIN PVR2_CVAL_AUDIOMODE_MONO
+#define PVR2_CVAL_AUDIOMODE_MAX PVR2_CVAL_AUDIOMODE_LANG2
+
+/* Values that pvr2_hdw_get_signal_status() returns */
+#define PVR2_SIGNAL_OK 0x0001
+#define PVR2_SIGNAL_STEREO 0x0002
+#define PVR2_SIGNAL_SAP 0x0004
+
+/* Legal values for PVR2_CID_HSM */
+#define PVR2_CVAL_HSM_FAIL 0
+#define PVR2_CVAL_HSM_FULL 1
+#define PVR2_CVAL_HSM_HIGH 2
+#define PVR2_CVAL_HSM_MIN PVR2_CVAL_HSM_FAIL
+#define PVR2_CVAL_HSM_MAX PVR2_CVAL_HSM_HIGH
+
+/* Subsystem definitions - these are various pieces that can be
+ independently stopped / started. Usually you don't want to mess with
+ this directly (let the driver handle things itself), but it is useful
+ for debugging. */
+#define PVR2_SUBSYS_ENC_FIRMWARE 0x00000001
+#define PVR2_SUBSYS_TUNER_CFG_STD 0x00000002
+#define PVR2_SUBSYS_TUNER_CFG_FREQ 0x00000004
+#define PVR2_SUBSYS_AUDIO_CFG_VBBTM 0x00000008
+#define PVR2_SUBSYS_AUDIO_CFG_STD 0x00000010
+#define PVR2_SUBSYS_AUDIO_CFG_MODE 0x00000020
+#define PVR2_SUBSYS_DIGITIZER_CFG_NORM 0x00000040
+#define PVR2_SUBSYS_DIGITIZER_CFG_INPUT 0x00000080
+#define PVR2_SUBSYS_DIGITIZER_CFG_SIZE 0x00000100
+#define PVR2_SUBSYS_DIGITIZER_CFG_AUDIO 0x00000200
+#define PVR2_SUBSYS_DIGITIZER_CFG_BCSH 0x00000400
+#define PVR2_SUBSYS_ENC_CFG 0x00000800
+#define PVR2_SUBSYS_DIGITIZER_RUN 0x00001000
+#define PVR2_SUBSYS_USBSTREAM_RUN 0x00002000
+#define PVR2_SUBSYS_ENC_RUN 0x00004000
+
+#define PVR2_SUBSYS_TUNER_CFG_ALL ( \
+ PVR2_SUBSYS_TUNER_CFG_STD | \
+ PVR2_SUBSYS_TUNER_CFG_FREQ )
+#define PVR2_SUBSYS_AUDIO_CFG_ALL ( \
+ PVR2_SUBSYS_AUDIO_CFG_MODE | \
+ PVR2_SUBSYS_AUDIO_CFG_STD | \
+ PVR2_SUBSYS_AUDIO_CFG_VBBTM )
+#define PVR2_SUBSYS_DIGITIZER_CFG_ALL ( \
+ PVR2_SUBSYS_DIGITIZER_CFG_NORM | \
+ PVR2_SUBSYS_DIGITIZER_CFG_INPUT | \
+ PVR2_SUBSYS_DIGITIZER_CFG_SIZE | \
+ PVR2_SUBSYS_DIGITIZER_CFG_AUDIO | \
+ PVR2_SUBSYS_DIGITIZER_CFG_BCSH )
+#define PVR2_SUBSYS_CFG_ALL ( \
+ PVR2_SUBSYS_ENC_FIRMWARE | \
+ PVR2_SUBSYS_TUNER_CFG_ALL | \
+ PVR2_SUBSYS_AUDIO_CFG_ALL | \
+ PVR2_SUBSYS_DIGITIZER_CFG_ALL | \
+ PVR2_SUBSYS_ENC_CFG )
+#define PVR2_SUBSYS_RUN_ALL ( \
+ PVR2_SUBSYS_DIGITIZER_RUN | \
+ PVR2_SUBSYS_USBSTREAM_RUN | \
+ PVR2_SUBSYS_ENC_RUN )
+#define PVR2_SUBSYS_ALL ( \
+ PVR2_SUBSYS_CFG_ALL | \
+ PVR2_SUBSYS_RUN_ALL )
+
+enum pvr2_config {
+ pvr2_config_empty,
+ pvr2_config_mpeg,
+ pvr2_config_vbi,
+ pvr2_config_radio,
+};
+
+const char *pvr2_config_get_name(enum pvr2_config);
+
+struct pvr2_hdw;
+
+/* Create and return a structure for interacting with the underlying
+ hardware */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf);
+
+/* Get pointer to structure given unit number */
+struct pvr2_hdw *pvr2_hdw_find(int unit_number);
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *);
+
+/* Set up the structure and attempt to put the device into a usable state.
+ This can be a time-consuming operation, which is why it is not done
+ internally as part of the create() step. Return value is exactly the
+ same as pvr2_hdw_init_ok(). */
+int pvr2_hdw_setup(struct pvr2_hdw *);
+
+/* Initialization succeeded */
+int pvr2_hdw_init_ok(struct pvr2_hdw *);
+
+/* Return true if in the ready (normal) state */
+int pvr2_hdw_dev_ok(struct pvr2_hdw *);
+
+/* Return small integer number [1..N] for logical instance number of this
+ device. This is useful for indexing array-valued module parameters. */
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *);
+
+/* Get pointer to underlying USB device */
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *);
+
+/* Retrieve serial number of device */
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *);
+
+/* Retrieve current value for a given control */
+int pvr2_hdw_get_ctl_value(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Return true if control is writable */
+int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Retrieve legal minimum value for a given control */
+int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Retrieve legal maximum value for a given control */
+int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Set current value for given control - this is just stored; the hardware
+ isn't updated until the commit function is called. */
+int pvr2_hdw_set_ctl_value(struct pvr2_hdw *,unsigned int ctl_id,int value);
+
+/* Retrieve string name for given control */
+const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Retrieve string name for a given control value (returns a null pointer
+ for any invalid combinations). */
+const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *,
+ unsigned int ctl_id,int value);
+
+/* Commit all control changes made up to this point */
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
+
+/* Find out how many controls there are. Legal ids are numbered from 0
+ through this value - 1. */
+unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *);
+
+/* Return PVR2_SIGNAL_XXXX bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *);
+
+/* Query device and see if it thinks it is on a high-speed USB link */
+int pvr2_hdw_is_hsm(struct pvr2_hdw *);
+
+/* Turn streaming on/off */
+int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
+
+/* Find out if streaming is on */
+int pvr2_hdw_get_streaming(struct pvr2_hdw *);
+
+/* Configure the type of stream to generate */
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
+
+/* Enable / disable various pieces of hardware. Items to change are
+ identified by bit positions within msk, and new state for each item is
+ identified by corresponding bit positions within val. */
+void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val);
+
+/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,msk) */
+void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk);
+
+/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,0) */
+void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk);
+
+/* Retrieve mask indicating which pieces of hardware are currently enabled
+ / configured. */
+unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *);
+
+/* Adjust mask of what get shut down when streaming is stopped. This is a
+ debugging aid. */
+void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val);
+
+/* Retrieve mask indicating which pieces of hardware are disabled when
+ streaming is turned off. */
+unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
+
+
+/* Enable / disable retrieval of CPU firmware. This must be enabled before
+ pvr2_hdw_cpufw_get() will function. Note that doing this may prevent
+ the device from running (and leaving this mode may imply a device
+ reset). */
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, int enable_flag);
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *);
+
+/* Retrieve a piece of the CPU's firmware at the given offset. Return
+ value is the number of bytes retrieved or zero if we're past the end or
+ an error otherwise (e.g. if firmware retrieval is not enabled). */
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs,
+ char *buf,unsigned int cnt);
+
+/* Retrieve previously stored v4l minor device number */
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *);
+
+/* Store the v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,int);
+
+
+/* The following entry points are all lower level things you normally don't
+ want to worry about. */
+
+/* Attempt to recover from a USB foul-up (in practice I find that if you
+ have to do this, then it's already too late). */
+void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw);
+
+/* Issue a command and get a response from the device. LOTS of higher
+ level stuff is built on this. */
+int pvr2_send_request(struct pvr2_hdw *, void *, unsigned int,
+ void *, unsigned int);
+
+/* Slightly higher level device communication functions. */
+int pvr2_write_register(struct pvr2_hdw *, u16, u32);
+int pvr2_read_register(struct pvr2_hdw *, u16, u32 *);
+int pvr2_write_u16(struct pvr2_hdw *, u16, int);
+int pvr2_write_u8(struct pvr2_hdw *, u8, int);
+
+/* Call if for any reason we can't talk to the hardware anymore - this will
+ cause the driver to stop flailing on the device. */
+void pvr2_hdw_render_useless(struct pvr2_hdw *);
+void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *);
+
+/* Set / clear 8051's reset bit */
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
+
+/* Execute a USB-commanded device reset */
+void pvr2_hdw_device_reset(struct pvr2_hdw *);
+
+/* Execute hard reset command (after this point it's likely that the
+ encoder will have to be reconfigured). This also clears the "useless"
+ state. */
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *);
+
+/* Execute simple reset command */
+int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *);
+
+/* Stop / start video stream transport */
+int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
+
+/* Direct manipulation of GPIO bits */
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val);
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
+
+/* This data structure is specifically for the next function... */
+struct pvr2_hdw_debug_info {
+ int big_lock_held;
+ int ctl_lock_held;
+ int flag_ok;
+ int flag_disconnected;
+ int flag_init_ok;
+ int flag_streaming_enabled;
+ unsigned long subsys_flags;
+ int cmd_debug_state;
+ int cmd_debug_write_len;
+ int cmd_debug_read_len;
+ int cmd_debug_write_pend;
+ int cmd_debug_read_pend;
+ int cmd_debug_timeout;
+ int cmd_debug_rstatus;
+ int cmd_debug_wstatus;
+ unsigned char cmd_code;
+};
+
+/* Non-intrusively retrieve internal state info - this is useful for
+ diagnosing lockups. Note that this operation is completed without any
+ kind of locking and so it is not atomic and may yield inconsistent
+ results. This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Cause encoder firmware to be uploaded into the device. This is normally
+ done autonomously, but the interface is exported here because it is also
+ a debugging aid. */
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
+
+
+#endif /* __PVRUSB2_HDW_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-i2c.c b/v4l_experimental/pvrusb2/pvrusb2-i2c.c
new file mode 100644
index 000000000..d5cb7467a
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-i2c.c
@@ -0,0 +1,441 @@
+/*
+ *
+ * $Id: pvrusb2-i2c.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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 "compat.h"
+#include "pvrusb2-i2c.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-debug.h"
+
+#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
+
+#ifndef I2C_DRIVERID_SAA7115
+// Using temporary hack for missing I2C driver-ID for saa7115
+#define I2C_DRIVERID_SAA7115 I2C_DRIVERID_EXP1
+#endif
+
+#ifndef I2C_DRIVERID_TVEEPROM
+// Using temporary hack for missing I2C driver-ID for tveeprom
+#define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
+#endif
+
+/*
+
+ This module attempts to implement a compliant I2C adapter for the pvrusb2
+ device. By doing this we can then make use of existing functionality in
+ V4L (e.g. tuner.c) rather than rolling our own.
+
+*/
+
+static unsigned int i2c_scan = 0;
+module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 length) /* Size of data to write */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+#ifdef notdef
+ trace_i2c("pvr2_i2c_write");
+#endif
+
+ if (!data) length = 0;
+ if (length > (sizeof(hdw->cmd_buffer) - 3)) return -ENOTSUPP;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write */
+ hdw->cmd_buffer[0] = 0x08; /* write prefix */
+ hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */
+ hdw->cmd_buffer[2] = length; /* length of what follows */
+ if (length) memcpy(hdw->cmd_buffer + 3, data, length);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ length + 3,
+ hdw->cmd_buffer,
+ 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_write[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+#ifdef notdef
+ trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret,
+ hdw->cmd_buffer[0]);
+#endif
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 dlen, /* Size of data to write */
+ u8 *res, /* Where to put data we read */
+ u16 rlen) /* Amount of data to read */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+#ifdef notdef
+ trace_i2c("pvr2_i2c_read");
+#endif
+
+ if (!data) dlen = 0;
+ if (dlen > (sizeof(hdw->cmd_buffer) - 4)) return -ENOTSUPP;
+ if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write followed by a read */
+ hdw->cmd_buffer[0] = 0x09; /* read prefix */
+ hdw->cmd_buffer[1] = dlen; /* arg length */
+ hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one
+ more byte (status). */
+ hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */
+ if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ 4 + dlen,
+ hdw->cmd_buffer,
+ rlen + 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_read[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+
+#ifdef notdef
+ trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d",
+ i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]);
+#endif
+ /* Copy back the result */
+ if (res && rlen) {
+ if (ret) {
+ /* Error, just blank out the return buffer */
+ memset(res, 0, rlen);
+ } else {
+ memcpy(res, hdw->cmd_buffer + 1, rlen);
+ }
+ }
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+/* This is a very, very limited I2C adapter implementation. We can only
+ support what we actually know will work on the device... */
+static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ int ret = -ENOTSUPP;
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
+
+ if ((msgs[0].flags & I2C_M_NOSTART)) {
+ trace_i2c("i2c refusing I2C_M_NOSTART");
+ goto done;
+ }
+
+ if (num == 1) {
+ if (msgs[0].flags & I2C_M_RD) {
+ /* Simple read */
+ u16 tcnt,bcnt,offs;
+ /* If the read is short enough we'll do the whole
+ thing atomically. Otherwise we have no choice
+ but to break apart the reads. */
+ tcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (pvr2_i2c_read(hdw,msgs[0].addr,
+ 0,0,
+ msgs[0].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ }
+ ret = 1;
+ goto done;
+ } else {
+ /* Simple write */
+ ret = 1;
+ if (pvr2_i2c_write(hdw,msgs[0].addr,
+ msgs[0].buf,msgs[0].len)) {
+ ret = -EIO;
+ }
+ goto done;
+ }
+ } else if (num == 2) {
+ if ((!((msgs[0].flags & I2C_M_RD))) &&
+ (msgs[1].flags & I2C_M_RD)) {
+ u16 tcnt,bcnt,wcnt,offs;
+ /* Write followed by atomic read. If the read
+ portion is short enough we'll do the whole thing
+ atomically. Otherwise we have no choice but to
+ break apart the reads. */
+ tcnt = msgs[1].len;
+ wcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt || wcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (pvr2_i2c_read(hdw,msgs[0].addr,
+ msgs[0].buf,wcnt,
+ msgs[1].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ wcnt = 0;
+ }
+ ret = 2;
+ goto done;
+ } else {
+ trace_i2c("i2c refusing complex transfer"
+ " read0=%d read1=%d",
+ (msgs[0].flags & I2C_M_RD),
+ (msgs[1].flags & I2C_M_RD));
+ }
+ } else {
+ trace_i2c("i2c refusing %d phase transfer",num);
+ }
+
+ done:
+ return ret;
+}
+
+static int pvr2_i2c_control(struct i2c_adapter *adapter,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int pvr2_i2c_attach_inform(struct i2c_client *client)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+ int id;
+ id = client->driver->id;
+ trace_i2c("i2c_attach [client=%s @ 0x%x id=0x%x]",
+ client->name,
+ client->addr,id);
+ if (!(hdw->i2c_audio_client) && (id == I2C_DRIVERID_MSP3400)) {
+ trace_i2c("attaching msp3400 I2C client");
+ hdw->i2c_audio_client = client;
+ }
+ if (!(hdw->i2c_tuner_client) && (id == I2C_DRIVERID_TUNER)) {
+ trace_i2c("attaching tuner I2C client");
+ hdw->i2c_tuner_client = client;
+ pvr2_tuner_set_type(hdw);
+ }
+ if (!(hdw->i2c_video_client) && (id == I2C_DRIVERID_SAA7115)) {
+ trace_i2c("attaching saa7115 I2C client");
+ hdw->i2c_video_client = client;
+ }
+ if (!(hdw->i2c_tveeprom_client) && (id == I2C_DRIVERID_TVEEPROM)) {
+ trace_i2c("attaching tveeprom I2C client");
+ hdw->i2c_tveeprom_client = client;
+ }
+
+ return 0;
+}
+
+int pvr2_i2c_cmd(struct i2c_client *cp,unsigned int cmd,void *arg)
+{
+ int stat;
+ if (!cp) return -EINVAL;
+ if (!(cp->driver)) return -EINVAL;
+ if (!(cp->driver->command)) return -EINVAL;
+ if (!try_module_get(cp->driver->owner)) return -EAGAIN;
+ stat = cp->driver->command(cp,cmd,arg);
+ module_put(cp->driver->owner);
+ return stat;
+}
+
+int pvr2_i2c_tuner_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+{
+ int stat = pvr2_i2c_cmd(hdw->i2c_tuner_client,cmd,arg);
+ if (stat < 0) trace_i2c("pvr2_i2c_tuner_cmd failed with status %d",
+ stat);
+ return stat;
+}
+
+int pvr2_i2c_msp3400_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+{
+ int stat = pvr2_i2c_cmd(hdw->i2c_audio_client,cmd,arg);
+ if (stat < 0) trace_i2c("pvr2_i2c_msp3400_cmd failed with status %d",
+ stat);
+ return stat;
+}
+
+int pvr2_i2c_saa7115_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+{
+ int stat = pvr2_i2c_cmd(hdw->i2c_video_client,cmd,arg);
+ if (stat < 0) trace_i2c("pvr2_i2c_saa7115_cmd failed with status %d",
+ stat);
+ return stat;
+}
+
+int pvr2_i2c_tveeprom_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+{
+ int stat = pvr2_i2c_cmd(hdw->i2c_tveeprom_client,cmd,arg);
+ if (stat < 0) {
+ trace_i2c("pvr2_i2c_tveeprom_cmd failed with status %d",stat);
+ }
+ return stat;
+}
+
+static int pvr2_i2c_detach_inform(struct i2c_client *client)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+ trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x]",
+ client->name,
+ client->addr);
+ if (hdw->i2c_audio_client == client) {
+ trace_i2c("detaching msp3400 I2C client");
+ hdw->i2c_audio_client = 0;
+ }
+ if (hdw->i2c_tuner_client == client) {
+ trace_i2c("detaching tuner I2C client");
+ hdw->i2c_tuner_client = 0;
+ }
+ if (hdw->i2c_video_client == client) {
+ trace_i2c("detaching saa7115 I2C client");
+ hdw->i2c_video_client = 0;
+ }
+ if (hdw->i2c_tveeprom_client == client) {
+ trace_i2c("detaching tveeprom I2C client");
+ hdw->i2c_tveeprom_client = 0;
+ }
+
+ return 0;
+}
+
+static struct i2c_algorithm pvr2_i2c_algo_template = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ .id = I2C_ALGO_BIT | I2C_HW_B_BT848,
+#endif
+ .master_xfer = pvr2_i2c_xfer,
+ .algo_control = pvr2_i2c_control,
+ .functionality = pvr2_i2c_functionality,
+};
+
+static struct i2c_adapter pvr2_i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .class = I2C_CLASS_TV_ANALOG,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ .id = I2C_ALGO_BIT | I2C_HW_B_BT848,
+#else
+ .id = I2C_HW_B_BT848,
+#endif
+ .client_register = pvr2_i2c_attach_inform,
+ .client_unregister = pvr2_i2c_detach_inform,
+};
+
+static void do_i2c_scan(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[1];
+ int i,rc;
+ msg[0].addr = 0;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = 0;
+ msg[0].buf = 0;
+ for (i = 0; i < 128; i++) {
+ msg[0].addr = i;
+ rc = i2c_transfer(&hdw->i2c_adap,msg,
+ sizeof(msg)/sizeof(msg[0]));
+ if (rc < 0) continue;
+ printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i);
+ }
+}
+
+void pvr2_i2c_init(struct pvr2_hdw *hdw)
+{
+ memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
+ memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo));
+ strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ strlcpy(hdw->i2c_algo.name,hdw->name,sizeof(hdw->i2c_algo.name));
+#endif
+ hdw->i2c_adap.algo = &hdw->i2c_algo;
+ hdw->i2c_adap.algo_data = hdw;
+ i2c_add_adapter(&hdw->i2c_adap);
+ hdw->i2c_linked = !0;
+ if (i2c_scan) do_i2c_scan(hdw);
+}
+
+void pvr2_i2c_done(struct pvr2_hdw *hdw)
+{
+ if (hdw->i2c_linked) {
+ i2c_del_adapter(&hdw->i2c_adap);
+ hdw->i2c_linked = 0;
+ }
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-i2c.h b/v4l_experimental/pvrusb2/pvrusb2-i2c.h
new file mode 100644
index 000000000..5379990b9
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-i2c.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * $Id: pvrusb2-i2c.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_I2C_H
+#define __PVRUSB2_I2C_H
+
+struct pvr2_hdw;
+
+void pvr2_i2c_init(struct pvr2_hdw *);
+void pvr2_i2c_done(struct pvr2_hdw *);
+int pvr2_i2c_tuner_cmd(struct pvr2_hdw *,unsigned int,void *);
+int pvr2_i2c_msp3400_cmd(struct pvr2_hdw *,unsigned int,void *);
+int pvr2_i2c_saa7115_cmd(struct pvr2_hdw *,unsigned int,void *);
+int pvr2_i2c_tveeprom_cmd(struct pvr2_hdw *,unsigned int,void *);
+
+#endif /* __PVRUSB2_I2C_H */
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-io.c b/v4l_experimental/pvrusb2/pvrusb2-io.c
new file mode 100644
index 000000000..935978a14
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-io.c
@@ -0,0 +1,682 @@
+/*
+ *
+ * $Id: pvrusb2-io.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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 "pvrusb2-io.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+
+#define BUFFER_SIG 0x47653271
+
+// #define SANITY_CHECK_BUFFERS
+
+#ifdef notdef
+#define BUFFER_CHECK(bp) do { \
+ if ((bp)->signature != BUFFER_SIG) { \
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
+ "Buffer %p is bad at %s:%d", \
+ (bp),__FILE__,__LINE__); \
+ pvr2_buffer_describe(bp,"BadSig"); \
+ BUG(); \
+ } \
+} while (0)
+#endif
+
+#ifdef SANITY_CHECK_BUFFERS
+#define BUFFER_CHECK(bp) do { \
+ if ((bp)->signature != BUFFER_SIG) { \
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
+ "Buffer %p is bad at %s:%d", \
+ (bp),__FILE__,__LINE__); \
+ pvr2_buffer_describe(bp,"BadSig"); \
+ BUG(); \
+ } \
+} while (0)
+#else
+#define BUFFER_CHECK(bp) do {} while(0)
+#endif
+
+struct pvr2_stream {
+ /* Buffers queued for reading */
+ struct list_head queued_list;
+ unsigned int q_count;
+ unsigned int q_bcount;
+ /* Buffers with retrieved data */
+ struct list_head ready_list;
+ unsigned int r_count;
+ unsigned int r_bcount;
+ /* Buffers available for use */
+ struct list_head idle_list;
+ unsigned int i_count;
+ unsigned int i_bcount;
+ /* Pointers to all buffers */
+ struct pvr2_buffer **buffers;
+ /* Array size of buffers */
+ unsigned int buffer_slot_count;
+ /* Total buffers actually in circulation */
+ unsigned int buffer_total_count;
+ /* Designed number of buffers to be in circulation */
+ unsigned int buffer_target_count;
+ /* Executed when ready list become non-empty */
+ pvr2_stream_callback callback_func;
+ void *callback_data;
+ /* Context for transfer endpoint */
+ struct usb_device *dev;
+ int endpoint;
+ /* Overhead for mutex enforcement */
+ spinlock_t list_lock;
+ struct semaphore sem;
+};
+
+struct pvr2_buffer {
+ int id;
+ int signature;
+ enum pvr2_buffer_state state;
+ void *ptr; /* Pointer to storage area */
+ unsigned int max_count; /* Size of storage area */
+ unsigned int used_count; /* Amount of valid data in storage area */
+ int status; /* Transfer result status */
+ struct pvr2_stream *stream;
+ struct list_head list_overhead;
+ struct urb *purb;
+};
+
+const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
+{
+ switch (st) {
+ case pvr2_buffer_state_none: return "none";
+ case pvr2_buffer_state_idle: return "idle";
+ case pvr2_buffer_state_queued: return "queued";
+ case pvr2_buffer_state_ready: return "ready";
+ }
+ return "unknown";
+}
+
+void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg)
+{
+ pvr2_trace(PVR2_TRACE_INFO,
+ "buffer%s%s %p state=%s id=%d status=%d"
+ " stream=%p purb=%p sig=0x%x",
+ (msg ? " " : ""),
+ (msg ? msg : ""),
+ bp,
+ (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
+ (bp ? bp->id : 0),
+ (bp ? bp->status : 0),
+ (bp ? bp->stream : 0),
+ (bp ? bp->purb : 0),
+ (bp ? bp->signature : 0));
+}
+
+static void pvr2_buffer_remove(struct pvr2_buffer *bp)
+{
+ unsigned int *cnt;
+ unsigned int *bcnt;
+ unsigned int ccnt;
+ struct pvr2_stream *sp = bp->stream;
+ switch (bp->state) {
+ case pvr2_buffer_state_idle:
+ cnt = &sp->i_count;
+ bcnt = &sp->i_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_queued:
+ cnt = &sp->q_count;
+ bcnt = &sp->q_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_ready:
+ cnt = &sp->r_count;
+ bcnt = &sp->r_bcount;
+ ccnt = bp->used_count;
+ break;
+ default:
+ return;
+ }
+ list_del_init(&bp->list_overhead);
+ (*cnt)--;
+ (*bcnt) -= ccnt;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s dec cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),*bcnt,*cnt);
+ bp->state = pvr2_buffer_state_none;
+}
+
+static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_none));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
+{
+ int fl;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_ready));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ fl = (sp->r_count == 0);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->ready_list);
+ bp->state = pvr2_buffer_state_ready;
+ (sp->r_count)++;
+ sp->r_bcount += bp->used_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->r_bcount,sp->r_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ return fl;
+}
+
+static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_idle));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->idle_list);
+ bp->state = pvr2_buffer_state_idle;
+ (sp->i_count)++;
+ sp->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->i_bcount,sp->i_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_queued));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->queued_list);
+ bp->state = pvr2_buffer_state_queued;
+ (sp->q_count)++;
+ sp->q_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->q_bcount,sp->q_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
+{
+ if (bp->state == pvr2_buffer_state_queued) {
+ usb_kill_urb(bp->purb);
+ }
+}
+
+static int pvr2_buffer_init(struct pvr2_buffer *bp,
+ struct pvr2_stream *sp,
+ unsigned int id)
+{
+ memset(bp,0,sizeof(*bp));
+ bp->signature = BUFFER_SIG;
+ bp->id = id;
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp);
+ bp->stream = sp;
+ bp->state = pvr2_buffer_state_none;
+ INIT_LIST_HEAD(&bp->list_overhead);
+ bp->purb = usb_alloc_urb(0,GFP_KERNEL);
+ if (! bp->purb) return -ENOMEM;
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"create");
+#endif
+ return 0;
+}
+
+static void pvr2_buffer_done(struct pvr2_buffer *bp)
+{
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"delete");
+#endif
+ pvr2_buffer_wipe(bp);
+ pvr2_buffer_set_none(bp);
+ bp->signature = 0;
+ bp->stream = 0;
+ if (bp->purb) usb_free_urb(bp->purb);
+ pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/"
+ " bufferDone %p",bp);
+}
+
+static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ unsigned int scnt;
+
+ /* Allocate buffers pointer array in multiples of 32 entries */
+ if (cnt == sp->buffer_total_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ poolResize "
+ " stream=%p cur=%d adj=%+d",
+ sp,
+ sp->buffer_total_count,
+ cnt-sp->buffer_total_count);
+
+ scnt = cnt & ~0x1f;
+ if (cnt > scnt) scnt += 0x20;
+
+ if (cnt > sp->buffer_total_count) {
+ if (scnt > sp->buffer_slot_count) {
+ struct pvr2_buffer **nb;
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ if (sp->buffer_slot_count) {
+ memcpy(nb,sp->buffers,
+ sp->buffer_slot_count * sizeof(*nb));
+ kfree(sp->buffers);
+ }
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ while (sp->buffer_total_count < cnt) {
+ struct pvr2_buffer *bp;
+ bp = kmalloc(sizeof(*bp),GFP_KERNEL);
+ if (!bp) return -ENOMEM;
+ ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count);
+ if (ret) {
+ kfree(bp);
+ return -ENOMEM;
+ }
+ sp->buffers[sp->buffer_total_count] = bp;
+ (sp->buffer_total_count)++;
+ pvr2_buffer_set_idle(bp);
+ }
+ } else {
+ while (sp->buffer_total_count > cnt) {
+ struct pvr2_buffer *bp;
+ bp = sp->buffers[sp->buffer_total_count - 1];
+ /* Paranoia */
+ sp->buffers[sp->buffer_total_count - 1] = 0;
+ (sp->buffer_total_count)--;
+ pvr2_buffer_done(bp);
+ kfree(bp);
+ }
+ if (scnt < sp->buffer_slot_count) {
+ struct pvr2_buffer **nb = 0;
+ if (scnt) {
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ memcpy(nb,sp->buffers,scnt * sizeof(*nb));
+ }
+ kfree(sp->buffers);
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ unsigned int cnt;
+
+ if (sp->buffer_total_count == sp->buffer_target_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/"
+ " poolCheck stream=%p cur=%d tgt=%d",
+ sp,sp->buffer_total_count,sp->buffer_target_count);
+
+ if (sp->buffer_total_count < sp->buffer_target_count) {
+ return pvr2_stream_buffer_count(sp,sp->buffer_target_count);
+ }
+
+ cnt = 0;
+ while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
+ bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
+ if (bp->state != pvr2_buffer_state_idle) break;
+ cnt++;
+ }
+ if (cnt) {
+ pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt);
+ }
+
+ return 0;
+}
+
+static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
+{
+ struct list_head *lp;
+ struct pvr2_buffer *bp1;
+ while ((lp = sp->queued_list.next) != &sp->queued_list) {
+ bp1 = list_entry(lp,struct pvr2_buffer,list_overhead);
+ pvr2_buffer_wipe(bp1);
+ /* At this point, we should be guaranteed that no
+ completion callback may happen on this buffer. But it's
+ possible that it might have completed after we noticed
+ it but before we wiped it. So double check its status
+ here first. */
+ if (bp1->state != pvr2_buffer_state_queued) continue;
+ pvr2_buffer_set_idle(bp1);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+}
+
+static void pvr2_stream_init(struct pvr2_stream *sp)
+{
+ spin_lock_init(&sp->list_lock);
+ init_MUTEX(&sp->sem);
+ INIT_LIST_HEAD(&sp->queued_list);
+ INIT_LIST_HEAD(&sp->ready_list);
+ INIT_LIST_HEAD(&sp->idle_list);
+}
+
+static void pvr2_stream_done(struct pvr2_stream *sp)
+{
+ down(&sp->sem); do {
+ pvr2_stream_internal_flush(sp);
+ pvr2_stream_buffer_count(sp,0);
+ } while (0); up(&sp->sem);
+}
+
+static void buffer_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_buffer *bp = urb->context;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ bp->used_count = 0;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
+ bp,urb->status,urb->actual_length);
+ if ((!(urb->status)) ||
+ (urb->status == -ENOENT) ||
+ (urb->status == -ECONNRESET) ||
+ (urb->status == -ESHUTDOWN)) {
+ bp->used_count = urb->actual_length;
+ }
+ bp->status = urb->status;
+ pvr2_buffer_set_ready(bp);
+ if (sp && sp->callback_func) {
+ sp->callback_func(sp->callback_data);
+ }
+}
+
+struct pvr2_stream *pvr2_stream_create(void)
+{
+ struct pvr2_stream *sp;
+ sp = kmalloc(sizeof(*sp),GFP_KERNEL);
+ if (!sp) return sp;
+ memset(sp,0,sizeof(*sp));
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp);
+ pvr2_stream_init(sp);
+ return sp;
+}
+
+void pvr2_stream_destroy(struct pvr2_stream *sp)
+{
+ if (!sp) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp);
+ pvr2_stream_done(sp);
+ kfree(sp);
+}
+
+void pvr2_stream_setup(struct pvr2_stream *sp,
+ struct usb_device *dev,
+ int endpoint)
+{
+ down(&sp->sem); do {
+ pvr2_stream_internal_flush(sp);
+ sp->dev = dev;
+ sp->endpoint = endpoint;
+ } while(0); up(&sp->sem);
+}
+
+void pvr2_stream_set_callback(struct pvr2_stream *sp,
+ pvr2_stream_callback func,
+ void *data)
+{
+ unsigned long irq_flags;
+ down(&sp->sem); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ sp->callback_data = data;
+ sp->callback_func = func;
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); up(&sp->sem);
+}
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
+{
+ return sp->buffer_target_count;
+}
+
+int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ if (sp->buffer_target_count == cnt) return 0;
+ down(&sp->sem); do {
+ sp->buffer_target_count = cnt;
+ ret = pvr2_stream_achieve_buffer_count(sp);
+ } while(0); up(&sp->sem);
+ return ret;
+}
+
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->idle_list.next;
+ if (lp == &sp->idle_list) return 0;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->ready_list.next;
+ if (lp == &sp->ready_list) return 0;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id)
+{
+ if (id < 0) return 0;
+ if (id >= sp->buffer_total_count) return 0;
+ return sp->buffers[id];
+}
+
+int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
+{
+ return sp->r_count;
+}
+
+int pvr2_stream_get_idle_count(struct pvr2_stream *sp)
+{
+ return sp->i_count;
+}
+
+void pvr2_stream_flush(struct pvr2_stream *sp)
+{
+ down(&sp->sem); do {
+ pvr2_stream_internal_flush(sp);
+ } while(0); up(&sp->sem);
+}
+
+void pvr2_stream_kill(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ down(&sp->sem); do {
+ pvr2_stream_internal_flush(sp);
+ while ((bp = pvr2_stream_get_ready_buffer(sp)) != 0) {
+ pvr2_buffer_set_idle(bp);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+ } while(0); up(&sp->sem);
+}
+
+int pvr2_buffer_queue(struct pvr2_buffer *bp)
+{
+#undef SEED_BUFFER
+#ifdef SEED_BUFFER
+ unsigned int idx;
+ unsigned int val;
+#endif
+ int ret = 0;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ down(&sp->sem); do {
+ pvr2_buffer_wipe(bp);
+ if (!sp->dev) {
+ ret = -EIO;
+ break;
+ }
+ pvr2_buffer_set_queued(bp);
+#ifdef SEED_BUFFER
+ for (idx = 0; idx < (bp->max_count) / 4; idx++) {
+ val = bp->id << 24;
+ val |= idx;
+ ((unsigned int *)(bp->ptr))[idx] = val;
+ }
+#endif
+ bp->status = -EINPROGRESS;
+ usb_fill_bulk_urb(bp->purb, // struct urb *urb
+ sp->dev, // struct usb_device *dev
+ // endpoint (below)
+ usb_rcvbulkpipe(sp->dev,sp->endpoint),
+ bp->ptr, // void *transfer_buffer
+ bp->max_count, // int buffer_length
+ buffer_complete,
+ bp);
+ usb_submit_urb(bp->purb,GFP_KERNEL);
+ } while(0); up(&sp->sem);
+ return ret;
+}
+
+int pvr2_buffer_idle(struct pvr2_buffer *bp)
+{
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ down(&sp->sem); do {
+ pvr2_buffer_wipe(bp);
+ pvr2_buffer_set_idle(bp);
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+ } while(0); up(&sp->sem);
+ return 0;
+}
+
+int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt)
+{
+ int ret = 0;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ down(&sp->sem); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if (bp->state != pvr2_buffer_state_idle) {
+ ret = -EPERM;
+ } else {
+ bp->ptr = ptr;
+ bp->stream->i_bcount -= bp->max_count;
+ bp->max_count = cnt;
+ bp->stream->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferPool "
+ " %8s cap cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(
+ pvr2_buffer_state_idle),
+ bp->stream->i_bcount,bp->stream->i_count);
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); up(&sp->sem);
+ return ret;
+}
+
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
+{
+ return bp->used_count;
+}
+
+int pvr2_buffer_get_status(struct pvr2_buffer *bp)
+{
+ return bp->status;
+}
+
+enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *bp)
+{
+ return bp->state;
+}
+
+int pvr2_buffer_get_id(struct pvr2_buffer *bp)
+{
+ return bp->id;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-io.h b/v4l_experimental/pvrusb2/pvrusb2-io.h
new file mode 100644
index 000000000..17e01345b
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-io.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * $Id: pvrusb2-io.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_IO_H
+#define __PVRUSB2_IO_H
+
+#include <linux/usb.h>
+#include <linux/list.h>
+
+typedef void (*pvr2_stream_callback)(void *);
+
+enum pvr2_buffer_state {
+ pvr2_buffer_state_none = 0, // Not on any list
+ pvr2_buffer_state_idle = 1, // Buffer is ready to be used again
+ pvr2_buffer_state_queued = 2, // Buffer has been queued for filling
+ pvr2_buffer_state_ready = 3, // Buffer has data available
+};
+
+struct pvr2_stream;
+struct pvr2_buffer;
+
+const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
+
+/* Initialize / tear down stream structure */
+struct pvr2_stream *pvr2_stream_create(void);
+void pvr2_stream_destroy(struct pvr2_stream *);
+void pvr2_stream_setup(struct pvr2_stream *,
+ struct usb_device *dev,int endpoint);
+void pvr2_stream_set_callback(struct pvr2_stream *,
+ pvr2_stream_callback func,
+ void *data);
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *);
+int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int);
+
+/* Get a pointer to a buffer that is either idle, ready, or is specified
+ named. */
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id);
+
+/* Find out how many buffers are idle or ready */
+int pvr2_stream_get_idle_count(struct pvr2_stream *);
+int pvr2_stream_get_ready_count(struct pvr2_stream *);
+
+/* Kill all pending operations */
+void pvr2_stream_flush(struct pvr2_stream *);
+
+/* Kill all pending buffers and throw away any ready buffers as well */
+void pvr2_stream_kill(struct pvr2_stream *);
+
+/* Set up the actual storage for a buffer */
+int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt);
+
+/* Find out size of data in the given ready buffer */
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *);
+
+/* Retrieve completion code for given ready buffer */
+int pvr2_buffer_get_status(struct pvr2_buffer *);
+
+/* Retrieve state of given buffer */
+enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *);
+
+/* Retrieve ID of given buffer */
+int pvr2_buffer_get_id(struct pvr2_buffer *);
+
+/* Start reading into given buffer (kill it if needed) */
+int pvr2_buffer_queue(struct pvr2_buffer *);
+
+/* Move buffer back to idle pool (kill it if needed) */
+int pvr2_buffer_idle(struct pvr2_buffer *);
+
+#endif /* __PVRUSB2_IO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-ioread.c b/v4l_experimental/pvrusb2/pvrusb2-ioread.c
new file mode 100644
index 000000000..5517e545c
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-ioread.c
@@ -0,0 +1,357 @@
+/*
+ *
+ * $Id: pvrusb2-ioread.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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 "pvrusb2-ioread.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#define BUFFER_COUNT 32
+#define BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_ioread {
+ struct pvr2_stream *stream;
+ char *buffer_storage[BUFFER_COUNT];
+ int resid_offs;
+ int enabled;
+ int stream_running;
+ int spigot_open;
+ struct semaphore sem;
+};
+
+static int pvr2_ioread_init(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ cp->stream = 0;
+ init_MUTEX(&cp->sem);
+
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL);
+ if (!(cp->buffer_storage[idx])) break;
+ }
+
+ if (idx < BUFFER_COUNT) {
+ // An allocation appears to have failed
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void pvr2_ioread_done(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ pvr2_ioread_setup(cp,0);
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+}
+
+struct pvr2_ioread *pvr2_ioread_create(void)
+{
+ struct pvr2_ioread *cp;
+ cp = kmalloc(sizeof(*cp),GFP_KERNEL);
+ if (!cp) return 0;
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
+ memset(cp,0,sizeof(*cp));
+ if (pvr2_ioread_init(cp) < 0) {
+ kfree(cp);
+ return 0;
+ }
+ return cp;
+}
+
+void pvr2_ioread_destroy(struct pvr2_ioread *cp)
+{
+ if (!cp) return;
+ pvr2_ioread_done(cp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp);
+ kfree(cp);
+}
+
+static void pvr2_ioread_stop(struct pvr2_ioread *cp)
+{
+ if (!(cp->enabled)) return;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp);
+ pvr2_stream_kill(cp->stream);
+ cp->resid_offs = 0;
+ cp->enabled = 0;
+ cp->stream_running = 0;
+ cp->spigot_open = 0;
+}
+
+static int pvr2_ioread_start(struct pvr2_ioread *cp)
+{
+ int stat;
+ struct pvr2_buffer *bp;
+ if (cp->enabled) return 0;
+ if (!(cp->stream)) return 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp);
+ cp->resid_offs = 0;
+ while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != 0) {
+ stat = pvr2_buffer_queue(bp);
+ if (stat < 0) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_start id=%p"
+ " error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ return stat;
+ }
+ }
+ cp->enabled = !0;
+ cp->stream_running = 0;
+ cp->spigot_open = 0;
+ return 0;
+}
+
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)
+{
+ return cp->stream;
+}
+
+int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
+{
+ int ret;
+ unsigned int idx;
+ struct pvr2_buffer *bp;
+
+ down(&cp->sem); do {
+ if (cp->stream) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (tear-down) id=%p",cp);
+ pvr2_ioread_stop(cp);
+ pvr2_stream_kill(cp->stream);
+ pvr2_stream_set_buffer_count(cp->stream,0);
+ cp->stream = 0;
+ }
+ if (sp) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (setup) id=%p",cp);
+ pvr2_stream_kill(sp);
+ ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
+ if (ret < 0) return ret;
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(sp,idx);
+ pvr2_buffer_set_buffer(bp,
+ cp->buffer_storage[idx],
+ BUFFER_SIZE);
+ }
+ cp->stream = sp;
+ }
+ } while (0); up(&cp->sem);
+
+ return 0;
+}
+
+int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
+{
+ int ret = 0;
+ if ((!fl) == (!(cp->enabled))) return ret;
+
+ down(&cp->sem); do {
+ if (fl) {
+ ret = pvr2_ioread_start(cp);
+ } else {
+ pvr2_ioread_stop(cp);
+ }
+ } while (0); up(&cp->sem);
+ return ret;
+}
+
+int pvr2_ioread_get_enabled(struct pvr2_ioread *cp)
+{
+ return cp->enabled != 0;
+}
+
+int pvr2_ioread_avail(struct pvr2_ioread *cp)
+{
+ int ret;
+ if (!(cp->enabled)) {
+ // Stream is not enabled; so this is an I/O error
+ return -EIO;
+ }
+
+ ret = 0;
+ if (cp->stream_running) {
+ if (!pvr2_stream_get_ready_count(cp->stream)) {
+ // No data available at all right now.
+ ret = -EAGAIN;
+ }
+ } else {
+ if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) {
+ // Haven't buffered up enough yet; try again later
+ ret = -EAGAIN;
+ }
+ }
+
+ if ((!(cp->spigot_open)) != (!(ret == 0))) {
+ cp->spigot_open = (ret == 0);
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ data is %s",
+ cp->spigot_open ? "available" : "pending");
+ }
+
+ return ret;
+}
+
+int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt)
+{
+ unsigned int copied_cnt;
+ unsigned int bcnt;
+ const char *src;
+ int stat;
+ struct pvr2_buffer *bp;
+ int ret = 0;
+ unsigned int req_cnt = cnt;
+
+ if (!cnt) {
+ pvr2_trace(PVR2_TRACE_TRAP,
+ "/*---TRACE_READ---*/ pvr2_ioread_read id=%p"
+ " ZERO Request? Returning zero.",cp);
+ return 0;
+ }
+
+ stat = pvr2_ioread_avail(cp);
+ if (stat < 0) return stat;
+
+ cp->stream_running = !0;
+
+ down(&cp->sem); do {
+
+ // Suck data out of the buffers and copy to the user
+ copied_cnt = 0;
+ if (!buf) cnt = 0;
+ while (cnt) {
+ bp = pvr2_stream_get_ready_buffer(cp->stream);
+ if (!bp) break; // Nothing ready; done
+
+ bcnt = pvr2_buffer_get_count(bp);
+ if (!bcnt) {
+ // Nothing transferred. Was there an
+ // error?
+ stat = pvr2_buffer_get_status(bp);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " buffer_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ ret = -EIO;
+ break;
+ }
+ src = 0;
+ } else {
+ // Calculate buffer offset and count
+ src = cp->buffer_storage[
+ pvr2_buffer_get_id(bp)];
+ if (cp->resid_offs > bcnt) {
+ cp->resid_offs = bcnt;
+ }
+ src += cp->resid_offs;
+ bcnt -= cp->resid_offs;
+ }
+ if (bcnt > cnt) {
+ // Can't read the entire buffer this time
+ // so remember how far in we're going to
+ // get so we can resume it later
+ bcnt = cnt;
+ cp->resid_offs += bcnt;
+ } else {
+ // We will be able to get the whole thing,
+ // so clear the residual offset
+ cp->resid_offs = 0;
+ }
+ if (bcnt) {
+ if (copy_to_user(buf,src,bcnt)) {
+ // User supplied a bad pointer?
+ // Give up - this *will* cause data
+ // to be lost.
+ ret = -EFAULT;
+ break;
+ }
+ cnt -= bcnt;
+ buf += bcnt;
+ copied_cnt += bcnt;
+ }
+ if (cp->resid_offs) break; // If there's a residual
+ // offset, get out
+ // Queue the buffer now that we've consumed it.
+ stat = pvr2_buffer_queue(bp);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " queue_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ ret = stat;
+ break;
+ }
+ }
+
+ } while (0); up(&cp->sem);
+
+ if (!ret) {
+ if (copied_cnt) {
+ // If anything was copied, return that count
+ ret = copied_cnt;
+ } else {
+ // Nothing copied; suggest to caller that another
+ // attempt should be tried again later
+ ret = -EAGAIN;
+ }
+ }
+
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ pvr2_ioread_read"
+ " id=%p request=%d result=%d",
+ cp,req_cnt,ret);
+ return ret;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-ioread.h b/v4l_experimental/pvrusb2/pvrusb2-ioread.h
new file mode 100644
index 000000000..ec2a23325
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-ioread.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id: pvrusb2-ioread.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_IOREAD_H
+#define __PVRUSB2_IOREAD_H
+
+#include "pvrusb2-io.h"
+
+struct pvr2_ioread;
+
+struct pvr2_ioread *pvr2_ioread_create(void);
+void pvr2_ioread_destroy(struct pvr2_ioread *);
+int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *);
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *);
+int pvr2_ioread_set_enabled(struct pvr2_ioread *,int);
+int pvr2_ioread_get_enabled(struct pvr2_ioread *);
+int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt);
+int pvr2_ioread_avail(struct pvr2_ioread *);
+
+#endif /* __PVRUSB2_IOREAD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-main.c b/v4l_experimental/pvrusb2/pvrusb2-main.c
new file mode 100644
index 000000000..4ae24f6b8
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-main.c
@@ -0,0 +1,177 @@
+/*
+ *
+ * $Id: pvrusb2-main.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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 "compat.h"
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/videodev.h>
+
+#include "pvrusb2-version.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-context.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#include "pvrusb2-sysfs.h"
+
+#define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>"
+#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner"
+
+#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \
+ PVR2_TRACE_INFO| \
+ PVR2_TRACE_TRAP| \
+ PVR2_TRACE_FIRMWARE| \
+ PVR2_TRACE_EEPROM | \
+ PVR2_TRACE_INIT | \
+ PVR2_TRACE_I2C | \
+ PVR2_TRACE_START_STOP | \
+ PVR2_TRACE_CTL | \
+ PVR2_TRACE_DEBUGIFC | \
+ 0)
+
+int debug = DEFAULT_DEBUG_MASK;
+
+module_param(debug,int,S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug trace mask");
+
+static struct pvr2_sysfs_class *class_ptr = 0;
+
+static struct usb_device_id pvr_table[] = {
+ { USB_DEVICE(0x2040, 0x2900) },
+ { }
+};
+
+static void pvr_setup_attach(struct pvr2_context *pvr)
+{
+ /* Create association with v4l layer */
+ pvr2_v4l2_create(pvr);
+ pvr2_sysfs_create(pvr,class_ptr);
+}
+
+static int pvr_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct pvr2_context *pvr;
+
+ /* Create underlying hardware interface */
+ pvr = pvr2_context_create(intf,pvr_setup_attach);
+ if (!pvr) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to create hdw handler");
+ return -ENOMEM;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr);
+
+ usb_set_intfdata(intf, pvr);
+
+ return 0;
+}
+
+/*
+ * pvr_disconnect()
+ *
+ */
+static void pvr_disconnect(struct usb_interface *intf)
+{
+ struct pvr2_context *pvr = usb_get_intfdata(intf);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr);
+
+ usb_set_intfdata (intf, NULL);
+ pvr2_context_disconnect(pvr);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr);
+
+}
+
+static struct usb_driver pvr_driver = {
+ owner: THIS_MODULE,
+ name: "pvrusb2",
+ id_table: pvr_table,
+ probe: pvr_probe,
+ disconnect: pvr_disconnect
+};
+
+/*
+ * pvr_init() / pvr_exit()
+ *
+ * This code is run to initialize/exit the driver.
+ *
+ */
+static int __init pvr_init(void)
+{
+ int ret;
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init");
+
+ /* Auto-load various support modules (with which we may
+ indirectly interact) */
+ request_module("tuner");
+ request_module("tveeprom");
+ request_module("msp3400");
+ request_module("saa7115");
+
+ class_ptr = pvr2_sysfs_class_create();
+
+ ret = usb_register(&pvr_driver);
+
+ if (ret == 0)
+ info(DRIVER_DESC " : " DRIVER_VERSION);
+ if (debug) info("Debug mask is %d (0x%x)",debug,debug);
+
+ return ret;
+}
+
+static void __exit pvr_exit(void)
+{
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_exit");
+
+ pvr2_sysfs_class_destroy(class_ptr);
+
+ usb_deregister(&pvr_driver);
+}
+
+module_init(pvr_init);
+module_exit(pvr_exit);
+
+MODULE_DEVICE_TABLE (usb, pvr_table);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-sysfs.c b/v4l_experimental/pvrusb2/pvrusb2-sysfs.c
new file mode 100644
index 000000000..42e012d96
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-sysfs.c
@@ -0,0 +1,800 @@
+/*
+ *
+ * $Id: pvrusb2-sysfs.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include "pvrusb2-sysfs.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-debugifc.h"
+
+#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__)
+
+static char *item_names[PVR2_CID_COUNT] = {
+ [PVR2_CID_BRIGHTNESS] = "ctl_brightness",
+ [PVR2_CID_CONTRAST] = "ctl_contrast",
+ [PVR2_CID_SATURATION] = "ctl_saturation",
+ [PVR2_CID_HUE] = "ctl_hue",
+ [PVR2_CID_VOLUME] = "ctl_volume",
+ [PVR2_CID_BALANCE] = "ctl_balance",
+ [PVR2_CID_BASS] = "ctl_bass",
+ [PVR2_CID_TREBLE] = "ctl_treble",
+ [PVR2_CID_MUTE] = "ctl_mute",
+ [PVR2_CID_SRATE] = "ctl_srate",
+ [PVR2_CID_AUDIOBITRATE] = "ctl_audio_bitrate",
+ [PVR2_CID_AUDIOCRC] = "ctl_audio_crc",
+ [PVR2_CID_AUDIOEMPHASIS] = "ctl_audio_emphasis",
+ [PVR2_CID_VBR] = "ctl_vbr",
+ [PVR2_CID_AVERAGEVIDEOBITRATE] = "ctl_video_average_bitrate",
+ [PVR2_CID_PEAKVIDEOBITRATE] = "ctl_video_peak_bitrate",
+ [PVR2_CID_VIDEOSTANDARD] = "ctl_video_standard",
+ [PVR2_CID_INPUT] = "ctl_input",
+ [PVR2_CID_AUDIOMODE] = "ctl_audio_mode",
+ [PVR2_CID_FREQUENCY] = "ctl_frequency",
+ [PVR2_CID_HRES] = "ctl_resolution_hor",
+ [PVR2_CID_VRES] = "ctl_resolution_ver",
+ [PVR2_CID_INTERLACE] = "ctl_interlace",
+ [PVR2_CID_AUDIOLAYER] = "ctl_audio_layer",
+ [PVR2_CID_CHANNEL] = "ctl_channel",
+ [PVR2_CID_CHANPROG_ID] = "ctl_freq_table_channel",
+ [PVR2_CID_CHANPROG_FREQ] = "ctl_freq_table_value",
+ [PVR2_CID_SIGNAL_PRESENT] = "ctl_signal_present",
+ [PVR2_CID_STREAMING_ENABLED] = "ctl_streaming_enabled",
+ [PVR2_CID_HSM] = "ctl_usb_speed",
+ [PVR2_CID_SUBSYS_MASK] = "ctl_debug_subsys_mask",
+ [PVR2_CID_SUBSYS_STREAM_MASK] = "ctl_debug_subsys_stream_mask",
+};
+
+struct pvr2_sysfs {
+ struct pvr2_channel channel;
+ struct class_device *class_dev;
+ struct pvr2_sysfs_debugifc *debugifc;
+ struct pvr2_sysfs_ctl_item *item_first;
+ struct pvr2_sysfs_ctl_item *item_last;
+ struct sysfs_ops kops;
+ struct kobj_type ktype;
+ struct class_device_attribute attr_v4l_minor_number;
+ struct class_device_attribute attr_unit_number;
+};
+
+struct pvr2_sysfs_debugifc {
+ struct class_device_attribute attr_debugcmd;
+ struct class_device_attribute attr_debuginfo;
+};
+
+struct pvr2_sysfs_ctl_item {
+ struct class_device_attribute attr_name;
+ struct class_device_attribute attr_min;
+ struct class_device_attribute attr_max;
+ struct class_device_attribute attr_enum;
+ struct class_device_attribute attr_val;
+ int attr_id;
+ struct pvr2_sysfs *chptr;
+ struct pvr2_sysfs_ctl_item *item_next;
+ struct attribute *attr_gen[5];
+ struct attribute_group grp;
+};
+
+struct pvr2_sysfs_class {
+ struct class class;
+};
+
+static ssize_t show_name(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ const char *name;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ name = pvr2_hdw_get_ctl_name(sfp->channel.hdw,id);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name);
+
+ if (!name) return -EINVAL;
+
+ return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+}
+
+static ssize_t show_min(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ int val;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ val = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %d",sfp,id,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%d\n",val);
+}
+
+static ssize_t show_max(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ int val;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ val = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %d",sfp,id,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%d\n",val);
+}
+
+static ssize_t show_val_int(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ int val;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_int(cid=%d) is %d",
+ sfp,id,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%d\n",val);
+}
+
+static ssize_t show_val_enum(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ int val;
+ const char *name;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id);
+
+ name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_enum(cid=%d) is %s (%d)",
+ sfp,id,name,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+}
+
+static ssize_t show_enum(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ int minval,maxval,val;
+ const char *name;
+ ssize_t cnt = 0;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id);
+ maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id);
+ for (val = minval; val <= maxval; val++) {
+ name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val);
+ cnt += scnprintf(buf+cnt,PAGE_SIZE-cnt,"%s\n",name);
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id);
+ return cnt;
+}
+
+static int store_val_any(int id,struct pvr2_sysfs *sfp,
+ const char *buf,unsigned int count)
+{
+ int val,minval,maxval;
+ int ch,ret;
+ const char *nv;
+ unsigned int nl;
+ int negfl;
+
+ /* Trim leading / trailing whitespace */
+ while (count) {
+ ch = buf[0];
+ if ((ch > 32) && (ch < 127)) break;
+ buf++;
+ count--;
+ }
+ while (count) {
+ ch = buf[count-1];
+ if ((ch > 32) && (ch < 127)) break;
+ count--;
+ }
+
+ /* Is this an enum? Look for a string value */
+ minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id);
+ maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id);
+ for (val = minval; val <= maxval; val++) {
+ nv = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val);
+ if ((!nv) && (val == minval)) break; /* Not an enum */
+ pvr2_sysfs_trace("pvr2_sysfs(%p) trying ctl_id %d val %d",
+ sfp,id,val);
+ if (!nv) {
+ pvr2_sysfs_trace("pvr2_sysfs(%p) no pointer",sfp);
+ continue;
+ }
+ nl = strlen(nv);
+ if (nl != count) {
+ pvr2_sysfs_trace("pvr2_sysfs(%p) count mismatch"
+ " %d != %d",
+ sfp,count,nl);
+ continue;
+ }
+ if (memcmp(buf,nv,nl)) {
+ pvr2_sysfs_trace(
+ "pvr2_sysfs(%p) name mismatch"
+ " >>%.*s<< != >>%.*s<<",
+ sfp,nl,buf,nl,nv);
+ continue;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)"
+ " is enum %s",
+ sfp,id,nv);
+ ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val);
+ pvr2_hdw_commit_ctl(sfp->channel.hdw);
+ return 0;
+ }
+ if (val > minval) {
+ pvr2_sysfs_trace(
+ "pvr2_sysfs(%p) store_val_any(cid=%d)"
+ " unmatched enum >>%.*s<<",
+ sfp,id,count,buf);
+ }
+
+ /* Try to parse as a number */
+ negfl = 0;
+ val = 0;
+ if (count) {
+ if (buf[0] == '-') {
+ negfl = !0;
+ buf++;
+ count--;
+ } else if (buf[0] == '+') {
+ buf++;
+ count--;
+ }
+ }
+ while (count) {
+ ch = buf[0];
+ if ((ch < '0') || (ch > '9')) break;
+ val = val * 10;
+ val += (ch - '0');
+ buf++;
+ count--;
+ }
+ if (!count) {
+ if (negfl) val = -val;
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)"
+ " int is %d",
+ sfp,id,val);
+
+ ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val);
+ pvr2_hdw_commit_ctl(sfp->channel.hdw);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int store_val_multi(int id,struct pvr2_sysfs *sfp,
+ const char *buf,unsigned int count)
+{
+ unsigned int count2;
+ int ret;
+
+ while (count) {
+ count2 = 0;
+ while ((count2 < count) && (buf[count2] != '\n')) count2++;
+ ret = store_val_any(id,sfp,buf,count2);
+ if (ret < 0) return ret;
+ if (count2 < count) count2++;
+ buf += count2;
+ count -= count2;
+ }
+ return 0;
+}
+
+static ssize_t store_val_int(int id,struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ ret = store_val_multi(id,sfp,buf,count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+static ssize_t store_val_enum(int id,struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ ret = store_val_multi(id,sfp,buf,count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+/*
+ Mike Isely <isely@pobox.com> 30-April-2005
+
+ This next batch of horrible preprocessor hackery is needed because the
+ kernel's class_device_attribute mechanism fails to pass the actual
+ attribute through to the show / store functions, which means we have no
+ way to package up any attribute-specific parameters, like for example the
+ control id. So we work around this brain-damage by encoding the control
+ id into the show / store functions themselves and pick the function based
+ on the control id we're setting up. These macros try to ease the pain.
+ Yuck.
+*/
+
+#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \
+static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,char *buf) \
+{ return sf_name(ctl_id,class_dev,buf); }
+
+#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \
+static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,const char *buf,size_t count) \
+{ return sf_name(ctl_id,class_dev,buf,count); }
+
+#define CREATE_BATCH(ctl_id) \
+CREATE_SHOW_INSTANCE(show_name,ctl_id) \
+CREATE_SHOW_INSTANCE(show_min,ctl_id) \
+CREATE_SHOW_INSTANCE(show_max,ctl_id) \
+CREATE_SHOW_INSTANCE(show_val_int,ctl_id) \
+CREATE_SHOW_INSTANCE(show_val_enum,ctl_id) \
+CREATE_SHOW_INSTANCE(show_enum,ctl_id) \
+CREATE_STORE_INSTANCE(store_val_int,ctl_id) \
+CREATE_STORE_INSTANCE(store_val_enum,ctl_id)
+
+CREATE_BATCH(0)
+ CREATE_BATCH(1)
+ CREATE_BATCH(2)
+ CREATE_BATCH(3)
+ CREATE_BATCH(4)
+ CREATE_BATCH(5)
+ CREATE_BATCH(6)
+ CREATE_BATCH(7)
+ CREATE_BATCH(8)
+ CREATE_BATCH(9)
+ CREATE_BATCH(10)
+ CREATE_BATCH(11)
+ CREATE_BATCH(12)
+ CREATE_BATCH(13)
+ CREATE_BATCH(14)
+ CREATE_BATCH(15)
+ CREATE_BATCH(16)
+ CREATE_BATCH(17)
+ CREATE_BATCH(18)
+ CREATE_BATCH(19)
+ CREATE_BATCH(20)
+ CREATE_BATCH(21)
+ CREATE_BATCH(22)
+ CREATE_BATCH(23)
+ CREATE_BATCH(24)
+ CREATE_BATCH(25)
+ CREATE_BATCH(26)
+ CREATE_BATCH(27)
+ CREATE_BATCH(28)
+ CREATE_BATCH(29)
+ CREATE_BATCH(30)
+ CREATE_BATCH(31)
+
+ struct pvr2_sysfs_func_set {
+ ssize_t (*show_name)(struct class_device *,char *);
+ ssize_t (*show_min)(struct class_device *,char *);
+ ssize_t (*show_max)(struct class_device *,char *);
+ ssize_t (*show_enum)(struct class_device *,char *);
+ ssize_t (*show_val_int)(struct class_device *,char *);
+ ssize_t (*show_val_enum)(struct class_device *,char *);
+ ssize_t (*store_val_int)(struct class_device *,
+ const char *,size_t);
+ ssize_t (*store_val_enum)(struct class_device *,
+ const char *,size_t);
+ };
+
+#define INIT_BATCH(ctl_id) \
+[ctl_id] = { \
+ .show_name = show_name_##ctl_id, \
+ .show_min = show_min_##ctl_id, \
+ .show_max = show_max_##ctl_id, \
+ .show_enum = show_enum_##ctl_id, \
+ .show_val_int = show_val_int_##ctl_id, \
+ .show_val_enum = show_val_enum_##ctl_id, \
+ .store_val_int = store_val_int_##ctl_id, \
+ .store_val_enum = store_val_enum_##ctl_id, \
+} \
+
+static struct pvr2_sysfs_func_set funcs[] = {
+ INIT_BATCH(0),
+ INIT_BATCH(1),
+ INIT_BATCH(2),
+ INIT_BATCH(3),
+ INIT_BATCH(4),
+ INIT_BATCH(5),
+ INIT_BATCH(6),
+ INIT_BATCH(7),
+ INIT_BATCH(8),
+ INIT_BATCH(9),
+ INIT_BATCH(10),
+ INIT_BATCH(11),
+ INIT_BATCH(12),
+ INIT_BATCH(13),
+ INIT_BATCH(14),
+ INIT_BATCH(15),
+ INIT_BATCH(16),
+ INIT_BATCH(17),
+ INIT_BATCH(18),
+ INIT_BATCH(19),
+ INIT_BATCH(20),
+ INIT_BATCH(21),
+ INIT_BATCH(22),
+ INIT_BATCH(23),
+ INIT_BATCH(24),
+ INIT_BATCH(25),
+ INIT_BATCH(26),
+ INIT_BATCH(27),
+ INIT_BATCH(28),
+ INIT_BATCH(29),
+ INIT_BATCH(30),
+ INIT_BATCH(31),
+};
+
+
+static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ struct pvr2_sysfs_func_set *fp;
+
+ if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) {
+ return;
+ }
+
+ fp = funcs + ctl_id;
+
+ cip = kmalloc(sizeof(*cip),GFP_KERNEL);
+ if (!cip) return;
+ memset(cip,0,sizeof(*cip));
+ pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
+
+ cip->attr_id = ctl_id;
+
+ cip->chptr = sfp;
+ cip->item_next = 0;
+ if (sfp->item_last) {
+ sfp->item_last->item_next = cip;
+ } else {
+ sfp->item_first = cip;
+ }
+ sfp->item_last = cip;
+
+ cip->attr_name.attr.owner = THIS_MODULE;
+ cip->attr_name.attr.name = "name";
+ cip->attr_name.attr.mode = S_IRUGO;
+ cip->attr_name.show = fp->show_name;
+
+ cip->attr_min.attr.owner = THIS_MODULE;
+ cip->attr_min.attr.name = "min_val";
+ cip->attr_min.attr.mode = S_IRUGO;
+ cip->attr_min.show = fp->show_min;
+
+ cip->attr_max.attr.owner = THIS_MODULE;
+ cip->attr_max.attr.name = "max_val";
+ cip->attr_max.attr.mode = S_IRUGO;
+ cip->attr_max.show = fp->show_max;
+
+ cip->attr_val.attr.owner = THIS_MODULE;
+ cip->attr_val.attr.name = "cur_val";
+ cip->attr_val.attr.mode = S_IRUGO;
+
+ cip->attr_enum.attr.owner = THIS_MODULE;
+ cip->attr_enum.attr.name = "enum_val";
+ cip->attr_enum.attr.mode = S_IRUGO;
+ cip->attr_enum.show = fp->show_enum;
+
+ if (pvr2_hdw_get_ctl_rw(sfp->channel.hdw,ctl_id)) {
+ cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
+ }
+
+ cip->attr_gen[0] = &cip->attr_name.attr;
+ cip->attr_gen[1] = &cip->attr_val.attr;
+ if (pvr2_hdw_get_ctl_value_name(
+ sfp->channel.hdw,ctl_id,
+ pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,ctl_id))) {
+ // Control is an enumeration
+ cip->attr_gen[2] = &cip->attr_enum.attr;
+ cip->attr_val.show = fp->show_val_enum;
+ cip->attr_val.store = fp->store_val_enum;
+ } else {
+ // Control is an integer
+ cip->attr_val.show = fp->show_val_int;
+ cip->attr_val.store = fp->store_val_int;
+ cip->attr_gen[2] = &cip->attr_min.attr;
+ cip->attr_gen[3] = &cip->attr_max.attr;
+ }
+
+ cip->grp.name = item_names[ctl_id];
+ cip->grp.attrs = cip->attr_gen;
+
+ sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
+}
+
+static ssize_t debuginfo_show(struct class_device *,char *);
+static ssize_t debugcmd_show(struct class_device *,char *);
+static ssize_t debugcmd_store(struct class_device *,const char *,size_t count);
+
+static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_debugifc *dip;
+ dip = kmalloc(sizeof(*dip),GFP_KERNEL);
+ if (!dip) return;
+ memset(dip,0,sizeof(*dip));
+ dip->attr_debugcmd.attr.owner = THIS_MODULE;
+ dip->attr_debugcmd.attr.name = "debugcmd";
+ dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP;
+ dip->attr_debugcmd.show = debugcmd_show;
+ dip->attr_debugcmd.store = debugcmd_store;
+ dip->attr_debuginfo.attr.owner = THIS_MODULE;
+ dip->attr_debuginfo.attr.name = "debuginfo";
+ dip->attr_debuginfo.attr.mode = S_IRUGO;
+ dip->attr_debuginfo.show = debuginfo_show;
+ sfp->debugifc = dip;
+ class_device_create_file(sfp->class_dev,&dip->attr_debugcmd);
+ class_device_create_file(sfp->class_dev,&dip->attr_debuginfo);
+}
+
+
+static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp)
+{
+ if (!sfp->debugifc) return;
+ class_device_remove_file(sfp->class_dev,
+ &sfp->debugifc->attr_debuginfo);
+ class_device_remove_file(sfp->class_dev,&sfp->debugifc->attr_debugcmd);
+ kfree(sfp->debugifc);
+ sfp->debugifc = 0;
+}
+
+
+static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp)
+{
+ unsigned int ctl_id;
+
+ for (ctl_id = 0;
+ ctl_id < (sizeof(item_names)/sizeof(item_names[0])); ctl_id++) {
+ if (!item_names[ctl_id]) continue;
+ pvr2_sysfs_add_control(sfp,ctl_id);
+ }
+}
+
+
+static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_ctl_item *cip1,*cip2;
+ for (cip1 = sfp->item_first; cip1; cip1 = cip2) {
+ cip2 = cip1->item_next;
+ sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp);
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1);
+ kfree(cip1);
+ }
+}
+
+
+static void pvr2_sysfs_class_release(struct class *class)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = container_of(class,struct pvr2_sysfs_class,class);
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+}
+
+
+static void pvr2_sysfs_release(struct class_device *class_dev)
+{
+ pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev);
+ kfree(class_dev);
+}
+
+
+static void class_dev_destroy(struct pvr2_sysfs *sfp)
+{
+ if (!sfp->class_dev) return;
+ pvr2_sysfs_tear_down_debugifc(sfp);
+ pvr2_sysfs_tear_down_controls(sfp);
+ class_device_remove_file(sfp->class_dev,&sfp->attr_v4l_minor_number);
+ class_device_remove_file(sfp->class_dev,&sfp->attr_unit_number);
+ pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev);
+ sfp->class_dev->class_data = 0;
+ class_device_unregister(sfp->class_dev);
+ sfp->class_dev = 0;
+}
+
+
+static ssize_t v4l_minor_number_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw));
+}
+
+
+static ssize_t unit_number_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_get_unit_number(sfp->channel.hdw));
+}
+
+
+static void class_dev_create(struct pvr2_sysfs *sfp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct usb_device *usb_dev;
+ struct class_device *class_dev;
+ usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw);
+ if (!usb_dev) return;
+ class_dev = kmalloc(sizeof(*class_dev),GFP_KERNEL);
+ if (!class_dev) return;
+ memset(class_dev,0,sizeof(*class_dev));
+
+ pvr2_sysfs_trace("Creating class_dev id=%p",class_dev);
+
+ class_dev->class = &class_ptr->class;
+ if (pvr2_hdw_get_sn(sfp->channel.hdw)) {
+ snprintf(class_dev->class_id,BUS_ID_SIZE,"sn-%lu",
+ pvr2_hdw_get_sn(sfp->channel.hdw));
+ } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) {
+ snprintf(class_dev->class_id,BUS_ID_SIZE,"unit-%c",
+ pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a');
+ } else {
+ kfree(class_dev);
+ return;
+ }
+
+ class_dev->dev = &usb_dev->dev;
+
+ sfp->class_dev = class_dev;
+ class_dev->class_data = sfp;
+ class_device_register(class_dev);
+
+ sfp->attr_v4l_minor_number.attr.owner = THIS_MODULE;
+ sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number";
+ sfp->attr_v4l_minor_number.attr.mode = S_IRUGO;
+ sfp->attr_v4l_minor_number.show = v4l_minor_number_show;
+ sfp->attr_v4l_minor_number.store = 0;
+ class_device_create_file(sfp->class_dev,&sfp->attr_v4l_minor_number);
+ sfp->attr_unit_number.attr.owner = THIS_MODULE;
+ sfp->attr_unit_number.attr.name = "unit_number";
+ sfp->attr_unit_number.attr.mode = S_IRUGO;
+ sfp->attr_unit_number.show = unit_number_show;
+ sfp->attr_unit_number.store = 0;
+ class_device_create_file(sfp->class_dev,&sfp->attr_unit_number);
+
+ pvr2_sysfs_add_controls(sfp);
+ pvr2_sysfs_add_debugifc(sfp);
+}
+
+
+static void pvr2_sysfs_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = container_of(chp,struct pvr2_sysfs,channel);
+ if (!sfp->channel.mc_head->disconnect_flag) return;
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp);
+ class_dev_destroy(sfp);
+ pvr2_channel_done(&sfp->channel);
+ kfree(sfp);
+}
+
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = kmalloc(sizeof(*sfp),GFP_KERNEL);
+ if (!sfp) return sfp;
+ memset(sfp,0,sizeof(*sfp));
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp);
+ pvr2_channel_init(&sfp->channel,mp);
+ sfp->channel.check_func = pvr2_sysfs_internal_check;
+
+ class_dev_create(sfp,class_ptr);
+ return sfp;
+}
+
+
+static int pvr2_sysfs_hotplug(struct class_device *cd,char **envp,
+ int numenvp,char *buf,int size)
+{
+ /* Even though we don't do anything here, we still need this function
+ because sysfs will still try to call it. */
+ return 0;
+}
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = kmalloc(sizeof(*clp),GFP_KERNEL);
+ if (!clp) return clp;
+ memset(clp,0,sizeof(*clp));
+ pvr2_sysfs_trace("Creating pvr2_sysfs_class id=%p",clp);
+ clp->class.name = "pvrusb2";
+ clp->class.class_release = pvr2_sysfs_class_release;
+ clp->class.release = pvr2_sysfs_release;
+ clp->class.hotplug = pvr2_sysfs_hotplug;
+ if (class_register(&clp->class)) {
+ pvr2_sysfs_trace(
+ "Registration failed for pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+ clp = 0;
+ }
+ return clp;
+}
+
+
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp)
+{
+ class_unregister(&clp->class);
+}
+
+
+static ssize_t debuginfo_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_store(struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+
+ ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count);
+ if (ret < 0) return ret;
+ return count;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-sysfs.h b/v4l_experimental/pvrusb2/pvrusb2-sysfs.h
new file mode 100644
index 000000000..20dcc3256
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-sysfs.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id: pvrusb2-sysfs.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_SYSFS_H
+#define __PVRUSB2_SYSFS_H
+
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#include "pvrusb2-context.h"
+
+struct pvr2_sysfs;
+struct pvr2_sysfs_class;
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void);
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *);
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *,
+ struct pvr2_sysfs_class *);
+
+#endif /* __PVRUSB2_SYSFS_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-tuner.c b/v4l_experimental/pvrusb2/pvrusb2-tuner.c
new file mode 100644
index 000000000..ffbc66294
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-tuner.c
@@ -0,0 +1,154 @@
+/*
+ *
+ * $Id: pvrusb2-tuner.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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 "pvrusb2.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-i2c.h"
+#include "pvrusb2-debug.h"
+#include "compat.h"
+#include <linux/videodev.h>
+#include <media/tuner.h>
+
+#define PVR_STD_I2C_ADDR 0x43
+
+static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
+
+module_param_array(tuner, int, NULL, 0444);
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+
+static u32 pvr_tbl_standard[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = 0x00d47009, /* PVR_STD_PAL_BG */
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = 0x00d4700a, /* PVR_STD_PAL_I */
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = 0x00d4700b, /* PVR_STD_PAL_DK */
+ [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = 0x00c4100b, /* PVR_STD_SECAM */
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = 0x00163044 /* PVR_STD_NTSC */
+};
+
+#define trace_tuner(...) pvr2_trace(PVR2_TRACE_TUNER,__VA_ARGS__)
+
+int pvr2_tuner_get_default_type(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = -1;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = tuner[unit_number];
+ }
+ if (tp < 0) return -EINVAL;
+ hdw->tuner_type = tp;
+ return 0;
+}
+
+int pvr2_tuner_set_freq(struct pvr2_hdw *hdw)
+{
+ int stat;
+ struct v4l2_frequency freq;
+ freq.frequency = hdw->controls[PVR2_CID_FREQUENCY].value / 62500;
+ freq.tuner = 0;
+ freq.type = V4L2_TUNER_ANALOG_TV;
+ trace_tuner("pvr2_tuner_set_freq(%d)",freq.frequency);
+ stat = pvr2_i2c_tuner_cmd(hdw,VIDIOC_S_FREQUENCY,&freq);
+ if (stat < 0) return stat;
+ /* This kicks the audio controller... */
+ stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOCSFREQ,0);
+ if (stat < 0) return stat;
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_TUNER_CFG_FREQ;
+ return 0;
+}
+
+int pvr2_tuner_set_type(struct pvr2_hdw *hdw)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+ struct tuner_setup setup;
+#endif
+ trace_tuner("pvr2_tuner_set_type(%d)",hdw->tuner_type);
+ if (((int)(hdw->tuner_type)) < 0) {
+ trace_tuner("tuner type not set yet");
+ return 0;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+ setup.addr = ADDR_UNSET;
+ setup.type = hdw->tuner_type;
+ setup.mode_mask = T_RADIO | T_ANALOG_TV;
+ /* We may really want mode_mask to be T_ANALOG_TV for now */
+ return pvr2_i2c_tuner_cmd(hdw,TUNER_SET_TYPE_ADDR,&setup);
+#else
+ return pvr2_i2c_tuner_cmd(hdw,TUNER_SET_TYPE,&hdw->tuner_type);
+#endif
+}
+
+int pvr2_tuner_set_standard(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[1];
+ u8 buff [4];
+ v4l2_std_id vs;
+ int cvstd = hdw->controls[PVR2_CID_VIDEOSTANDARD].value;
+
+ trace_tuner("pvr2_tuner_set_standard(%d)",
+ hdw->controls[PVR2_CID_VIDEOSTANDARD].value);
+
+ /* FIXME: Might be tuner dependant ? Need more hardware info... */
+ /* MCI 15-May-2005: I have absolutely no idea what this is doing
+ or who it is talking to. */
+ PVR2_DECOMPOSE_BE(
+ buff, 0,pvr_tbl_standard[cvstd]);
+ msg[0].addr = PVR_STD_I2C_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = sizeof(buff);
+ msg[0].buf = buff;
+ i2c_transfer(&hdw->i2c_adap,msg,sizeof(msg)/sizeof(msg[0]));
+
+ /* Also tell tuner.ko about the video standard. */
+ switch (cvstd) {
+ default:
+ case PVR2_CVAL_VIDEOSTANDARD_NTSC_M:
+ vs = V4L2_STD_NTSC;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_SECAM_L:
+ vs = V4L2_STD_SECAM;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_BG:
+ vs = V4L2_STD_PAL_BG;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_I:
+ vs = V4L2_STD_PAL_I;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_DK:
+ vs = V4L2_STD_PAL_DK;
+ break;
+ }
+ pvr2_i2c_tuner_cmd(hdw,VIDIOC_S_STD,&vs);
+
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_TUNER_CFG_STD;
+ return 0;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-tuner.h b/v4l_experimental/pvrusb2/pvrusb2-tuner.h
new file mode 100644
index 000000000..501f2af48
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-tuner.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * $Id: pvrusb2-tuner.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_TUNER_H
+#define __PVRUSB2_TUNER_H
+
+struct pvr2_hdw;
+
+int pvr2_tuner_get_default_type(struct pvr2_hdw *);
+int pvr2_tuner_set_freq(struct pvr2_hdw *);
+int pvr2_tuner_set_type(struct pvr2_hdw *);
+int pvr2_tuner_set_standard(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_TUNER_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-util.h b/v4l_experimental/pvrusb2/pvrusb2-util.h
new file mode 100644
index 000000000..e8c2cc940
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-util.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * $Id: pvrusb2-util.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_UTIL_H
+#define __PVRUSB2_UTIL_H
+
+#define PVR2_DECOMPOSE_LE(t,i,d) \
+ do { \
+ (t)[i] = (d) & 0xff;\
+ (t)[i+1] = ((d) >> 8) & 0xff;\
+ (t)[i+2] = ((d) >> 16) & 0xff;\
+ (t)[i+3] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_DECOMPOSE_BE(t,i,d) \
+ do { \
+ (t)[i+3] = (d) & 0xff;\
+ (t)[i+2] = ((d) >> 8) & 0xff;\
+ (t)[i+1] = ((d) >> 16) & 0xff;\
+ (t)[i] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_COMPOSE_LE(t,i) \
+ ((((u32)((t)[i+3])) << 24) | \
+ (((u32)((t)[i+2])) << 16) | \
+ (((u32)((t)[i+1])) << 8) | \
+ ((u32)((t)[i])))
+
+#define PVR2_COMPOSE_BE(t,i) \
+ ((((u32)((t)[i])) << 24) | \
+ (((u32)((t)[i+1])) << 16) | \
+ (((u32)((t)[i+2])) << 8) | \
+ ((u32)((t)[i+3])))
+
+
+#endif /* __PVRUSB2_UTIL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-v4l2.c b/v4l_experimental/pvrusb2/pvrusb2-v4l2.c
new file mode 100644
index 000000000..c10c1a4e6
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-v4l2.c
@@ -0,0 +1,1302 @@
+/*
+ *
+ * $Id: pvrusb2-v4l2.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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 "compat.h"
+#include <linux/kernel.h>
+#include <linux/videodev.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/videodev.h>
+
+
+#define PVR_WIDTH_DVD 720
+#define PVR_WIDTH_SVCD 480
+#define PVR_WIDTH_VCD 352
+
+#define PVR_HEIGHT_PAL 480
+#define PVR_HEIGHT_NTSC 576
+
+struct pvr2_v4l2_dev;
+struct pvr2_v4l2_fh;
+struct pvr2_v4l2;
+
+struct pvr2_v4l2_dev {
+ struct pvr2_v4l2 *v4lp;
+ struct video_device *vdev;
+ struct pvr2_context_stream *stream;
+ enum pvr2_config config;
+};
+
+struct pvr2_v4l2_fh {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_dev *dev_info;
+ enum v4l2_priority prio;
+ struct pvr2_ioread *rhp;
+ struct file *file;
+ struct pvr2_v4l2 *vhead;
+ struct pvr2_v4l2_fh *vnext;
+ struct pvr2_v4l2_fh *vprev;
+ wait_queue_head_t wait_data;
+ int fw_mode_flag;
+ struct semaphore sem;
+};
+
+struct pvr2_v4l2 {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_fh *vfirst;
+ struct pvr2_v4l2_fh *vlast;
+
+ struct v4l2_prio_state prio;
+
+ /* streams */
+ struct pvr2_v4l2_dev video_dev;
+};
+
+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 minor");
+
+#define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE)
+#define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1)
+#define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2)
+#define V4L2_CID_PVR_AUDIOEMPHASIS (V4L2_CID_PRIVATE_BASE+3)
+#define V4L2_CID_PVR_VBR (V4L2_CID_PRIVATE_BASE+4)
+#define V4L2_CID_PVR_VIDEOBITRATE (V4L2_CID_PRIVATE_BASE+5)
+#define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6)
+#define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7)
+#define V4L2_CID_PVR_INPUT (V4L2_CID_PRIVATE_BASE+8)
+#define V4L2_CID_PVR_AUDIOMODE (V4L2_CID_PRIVATE_BASE+9)
+#define V4L2_CID_PVR_FREQUENCY (V4L2_CID_PRIVATE_BASE+10)
+#define V4L2_CID_PVR_HRES (V4L2_CID_PRIVATE_BASE+11)
+#define V4L2_CID_PVR_VRES (V4L2_CID_PRIVATE_BASE+12)
+
+#define V4L2_CID_PVR_MAX (V4L2_CID_PRIVATE_BASE+12)
+
+struct v4l2_capability pvr_capability ={
+ .driver = "pvrusb2",
+ .card = "Hauppauge WinTV pvr-usb2",
+ .bus_info = "usb",
+ .version = KERNEL_VERSION(0,8,0),
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE),
+ .reserved = {0,0,0,0}
+};
+
+static struct v4l2_tuner pvr_v4l2_tuners[]= {
+ {
+ .index = 0,
+ .name = "TV Tuner",
+ .type = V4L2_TUNER_ANALOG_TV,
+ .capability = (V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2),
+ .rangelow = 0,
+ .rangehigh = 0,
+ .rxsubchans = V4L2_TUNER_SUB_STEREO,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .signal = 0,
+ .afc = 0,
+ .reserved = {0,0,0,0}
+ }
+#ifdef notdef
+ {
+ .index = 1,
+ .name = "Radio Tuner",
+ .type = V4L2_TUNER_RADIO,
+ .capability = (V4L2_TUNER_CAP_STEREO),
+ .rangelow = 0,
+ .rangehigh = 0,
+ .rxsubchans = 0,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .signal = 0,
+ .afc = 0,
+ .reserved = {0,0,0,0}
+ }
+#endif
+};
+
+struct v4l2_standard pvr_standards[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = {
+ .id = V4L2_STD_PAL_BG,
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ },
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = {
+ .id = V4L2_STD_PAL_I,
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ },
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = {
+ .id = V4L2_STD_PAL_DK,
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ },
+ [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = {
+ .id = V4L2_STD_SECAM,
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ },
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = {
+ .id = V4L2_STD_NTSC,
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }
+};
+
+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,
+ .reserved = { 0, 0, 0, 0 }
+ }
+};
+
+#define PVR_FORMAT_PIX 0
+#define PVR_FORMAT_VBI 1
+
+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,
+ .reserved = { 0, 0 }
+ }
+ }
+ }
+};
+
+static int cnv_cid_v4l2_pvr2(int id)
+{
+ switch (id) {
+ case V4L2_CID_BRIGHTNESS:
+ return PVR2_CID_BRIGHTNESS;
+ case V4L2_CID_SATURATION:
+ return PVR2_CID_SATURATION;
+ case V4L2_CID_CONTRAST:
+ return PVR2_CID_CONTRAST;
+ case V4L2_CID_HUE:
+ return PVR2_CID_HUE;
+ case V4L2_CID_AUDIO_VOLUME:
+ return PVR2_CID_VOLUME;
+ case V4L2_CID_AUDIO_BALANCE:
+ return PVR2_CID_BALANCE;
+ case V4L2_CID_AUDIO_BASS:
+ return PVR2_CID_BASS;
+ case V4L2_CID_AUDIO_TREBLE:
+ return PVR2_CID_TREBLE;
+ case V4L2_CID_AUDIO_MUTE:
+ return PVR2_CID_MUTE;
+ case V4L2_CID_PVR_SRATE:
+ return PVR2_CID_SRATE;
+ case V4L2_CID_PVR_AUDIOBITRATE:
+ return PVR2_CID_AUDIOBITRATE;
+ case V4L2_CID_PVR_AUDIOCRC:
+ return PVR2_CID_AUDIOCRC;
+ case V4L2_CID_PVR_AUDIOEMPHASIS:
+ return PVR2_CID_AUDIOEMPHASIS;
+ case V4L2_CID_PVR_VBR:
+ return PVR2_CID_VBR;
+ case V4L2_CID_PVR_VIDEOBITRATE:
+ return PVR2_CID_AVERAGEVIDEOBITRATE;
+ case V4L2_CID_PVR_VIDEOPEAK:
+ return PVR2_CID_PEAKVIDEOBITRATE;
+ case V4L2_CID_PVR_INPUT:
+ return PVR2_CID_INPUT;
+ case V4L2_CID_PVR_AUDIOMODE:
+ return PVR2_CID_AUDIOMODE;
+ case V4L2_CID_PVR_FREQUENCY:
+ return PVR2_CID_FREQUENCY;
+ case V4L2_CID_PVR_HRES:
+ return PVR2_CID_HRES;
+ case V4L2_CID_PVR_VRES:
+ return PVR2_CID_VRES;
+ }
+ return -1;
+}
+
+#ifdef notdef
+static int cnv_cid_pvr2_v4l2(int id)
+{
+ switch (id) {
+ case PVR2_CID_BRIGHTNESS:
+ return V4L2_CID_BRIGHTNESS;
+ case PVR2_CID_SATURATION:
+ return V4L2_CID_SATURATION;
+ case PVR2_CID_CONTRAST:
+ return V4L2_CID_CONTRAST;
+ case PVR2_CID_HUE:
+ return V4L2_CID_HUE;
+ case PVR2_CID_VOLUME:
+ return V4L2_CID_AUDIO_VOLUME;
+ case PVR2_CID_BALANCE:
+ return V4L2_CID_AUDIO_BALANCE;
+ case PVR2_CID_BASS:
+ return V4L2_CID_AUDIO_BASS;
+ case PVR2_CID_TREBLE:
+ return V4L2_CID_AUDIO_TREBLE;
+ case PVR2_CID_MUTE:
+ return V4L2_CID_AUDIO_MUTE;
+
+ return id + V4L2_CID_PRIVATE_BASE;
+ }
+ return -1;
+}
+#endif
+
+static const char *get_cmd_code(int cmd)
+{
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ return "VIDIOC_QUERYCAP";
+ case VIDIOC_G_PRIORITY:
+ return "VIDIOC_G_PRIORITY";
+ case VIDIOC_S_PRIORITY:
+ return "VIDIOC_S_PRIORITY";
+ case VIDIOC_ENUMSTD:
+ return "VIDIOC_ENUMSTD";
+ case VIDIOC_G_STD:
+ return "VIDIOC_G_STD";
+ case VIDIOC_S_STD:
+ return "VIDIOC_S_STD";
+ case VIDIOC_ENUMINPUT:
+ return "VIDIOC_ENUMINPUT";
+ case VIDIOC_G_INPUT:
+ return "VIDIOC_G_INPUT";
+ case VIDIOC_S_INPUT:
+ return "VIDIOC_S_INPUT";
+ case VIDIOC_ENUMAUDIO:
+ return "VIDIOC_ENUMAUDIO";
+ case VIDIOC_G_AUDIO:
+ return "VIDIOC_G_AUDIO";
+ case VIDIOC_S_AUDIO:
+ return "VIDIOC_S_AUDIO";
+ case VIDIOC_G_TUNER:
+ return "VIDIOC_G_TUNER";
+ case VIDIOC_S_TUNER:
+ return "VIDIOC_S_TUNER";
+ case VIDIOC_S_FREQUENCY:
+ return "VIDIOC_S_FREQUENCY";
+ case VIDIOC_G_FREQUENCY:
+ return "VIDIOC_G_FREQUENCY";
+ case VIDIOC_ENUM_FMT:
+ return "VIDIOC_ENUM_FMT";
+ case VIDIOC_G_FMT:
+ return "VIDIOC_G_FMT";
+ case VIDIOC_TRY_FMT:
+ return "VIDIOC_TRY_FMT";
+ case VIDIOC_S_FMT:
+ return "VIDIOC_S_FMT";
+ case VIDIOC_QBUF:
+ return "VIDIOC_QBUF";
+ case VIDIOC_QUERYBUF:
+ return "VIDIOC_QUERYBUF";
+ case VIDIOC_DQBUF:
+ return "VIDIOC_DQBUF";
+ case VIDIOC_REQBUFS:
+ return "VIDIOC_REQBUFS";
+ case VIDIOC_STREAMON:
+ return "VIDIOC_STREAMON";
+ case VIDIOC_STREAMOFF:
+ return "VIDIOC_STREAMOFF";
+ case VIDIOC_QUERYCTRL:
+ return "VIDIOC_QUERYCTRL";
+ case VIDIOC_QUERYMENU:
+ return "VIDIOC_QUERYMENU";
+ case VIDIOC_G_CTRL:
+ return "VIDIOC_G_CTRL";
+ case VIDIOC_S_CTRL:
+ return "VIDIOC_S_CTRL";
+ case VIDIOCGMBUF:
+ return "VIDIOCGMBUF";
+ default :
+ break;
+ }
+ return "unknown";
+}
+
+
+/*
+ * pvr_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+ struct pvr2_v4l2_dev *dev_info = fh->dev_info;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret = -EINVAL;
+
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl cmd=%s (%d)",get_cmd_code(cmd),cmd);
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ioctl failed - bad or no context");
+ return -EFAULT;
+ }
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ ret = v4l2_prio_check(&vp->prio, &fh->prio);
+ if (ret)
+ return ret;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&vp->prio);
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio);
+ break;
+ }
+
+ case VIDIOC_ENUMSTD:
+ {
+
+ struct v4l2_standard *vs = (struct v4l2_standard *)arg;
+ int idx = vs->index;
+
+ if ((vs->index < PVR2_CVAL_VIDEOSTANDARD_MIN) ||
+ (vs->index > PVR2_CVAL_VIDEOSTANDARD_MAX)) {
+ break;
+ }
+
+ memcpy(vs, &pvr_standards[idx], sizeof(struct v4l2_standard));
+ vs->index = idx;
+ strlcpy(vs->name,
+ pvr2_hdw_get_ctl_value_name(hdw,
+ PVR2_CID_VIDEOSTANDARD,
+ idx),
+ sizeof(vs->name));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *vs = (v4l2_std_id *)arg;
+
+ switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD)) {
+ default:
+ case PVR2_CVAL_VIDEOSTANDARD_NTSC_M:
+ *vs = V4L2_STD_NTSC;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_SECAM_L:
+ *vs = V4L2_STD_SECAM;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_BG:
+ *vs = V4L2_STD_PAL_BG;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_I:
+ *vs = V4L2_STD_PAL_I;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_DK:
+ *vs = V4L2_STD_PAL_DK;
+ break;
+ }
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *vs = (v4l2_std_id *)arg;
+ int val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M;
+
+ if (*vs & V4L2_STD_NTSC){
+ val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M;
+ } else if (*vs & V4L2_STD_PAL_BG){
+ val = PVR2_CVAL_VIDEOSTANDARD_PAL_BG;
+ } else if (*vs & V4L2_STD_PAL_I){
+ val = PVR2_CVAL_VIDEOSTANDARD_PAL_I;
+ } else if (*vs & V4L2_STD_PAL_DK){
+ val = PVR2_CVAL_VIDEOSTANDARD_PAL_DK;
+ } else if (*vs & V4L2_STD_SECAM){
+ val = PVR2_CVAL_VIDEOSTANDARD_SECAM_L;
+ }
+
+ pvr2_hdw_set_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD,val);
+
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ struct v4l2_input tmp;
+
+ if ((vi->index > PVR2_CVAL_INPUT_MAX) ||
+ (vi->index < PVR2_CVAL_INPUT_MIN)) {
+ break;
+ }
+
+ memset(&tmp,0,sizeof(tmp));
+ tmp.index = vi->index;
+ switch (vi->index) {
+ case PVR2_CVAL_INPUT_TV:
+ 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;
+ }
+
+ strlcpy(tmp.name,
+ pvr2_hdw_get_ctl_value_name(hdw,PVR2_CID_INPUT,
+ vi->index),
+ sizeof(tmp.name));
+
+ /* 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. */
+
+ memcpy(vi, &tmp, sizeof(tmp));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ vi->index = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INPUT);
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ ret = 0;
+ if (pvr2_hdw_set_ctl_value(hdw,PVR2_CID_INPUT,vi->index)) {
+ ret = -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_ENUMAUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOC_G_AUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOC_S_AUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+ unsigned int status_mask;
+ if (vt->index !=0) break;
+
+ status_mask = pvr2_hdw_get_signal_status(hdw);
+
+ memcpy(vt, &pvr_v4l2_tuners[vt->index],
+ sizeof(struct v4l2_tuner));
+
+ vt->signal = 0;
+ if (status_mask & PVR2_SIGNAL_OK) {
+ if (status_mask & PVR2_SIGNAL_STEREO) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+ if (status_mask & PVR2_SIGNAL_SAP) {
+ vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2);
+ }
+ vt->signal = 65535;
+ }
+
+ switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_AUDIOMODE)) {
+ case PVR2_CVAL_AUDIOMODE_MONO:
+ vt->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ case PVR2_CVAL_AUDIOMODE_STEREO:
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ break;
+ case PVR2_CVAL_AUDIOMODE_LANG1:
+ vt->audmode = V4L2_TUNER_MODE_LANG1;
+ break;
+ case PVR2_CVAL_AUDIOMODE_LANG2:
+ vt->audmode = V4L2_TUNER_MODE_LANG2;
+ break;
+ case PVR2_CVAL_AUDIOMODE_SAP:
+ vt->audmode = V4L2_TUNER_MODE_SAP;
+ break;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
+ int val = PVR2_CVAL_AUDIOMODE_STEREO;
+
+ if (vt->index != 0)
+ break;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ val = PVR2_CVAL_AUDIOMODE_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ val = PVR2_CVAL_AUDIOMODE_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ val = PVR2_CVAL_AUDIOMODE_LANG1;
+ break;
+ case V4L2_TUNER_MODE_SAP: // Also LANG2
+ val = PVR2_CVAL_AUDIOMODE_SAP;
+ break;
+ }
+
+ pvr2_hdw_set_ctl_value(hdw,PVR2_CID_AUDIOMODE,val);
+ ret = 0;
+ }
+
+ case VIDIOC_S_FREQUENCY:
+ {
+ const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+
+ pvr2_hdw_set_ctl_value(hdw,PVR2_CID_FREQUENCY,
+ vf->frequency * 62500);
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+ int val;
+
+ val = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_FREQUENCY);
+
+ val /= 62500;
+ vf->frequency = val;
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg;
+
+ /* Only one format is supported : mpeg.*/
+ if (fd->index != 0)
+ break;
+
+ memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ vf->fmt.pix.width =
+ pvr2_hdw_get_ctl_value(hdw,PVR2_CID_HRES);
+ if (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INTERLACE)) {
+ vf->fmt.pix.width /= 2;
+ }
+ vf->fmt.pix.height =
+ pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VRES);
+ ret = 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+
+ ret = 0;
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ int is_ntsc = (pvr2_hdw_get_ctl_value(
+ hdw,
+ PVR2_CID_VIDEOSTANDARD) ==
+ PVR2_CVAL_VIDEOSTANDARD_NTSC_M);
+ int hf = is_ntsc ? 480 : 576;
+ int hh = (int) (hf / 2);
+ int h = vf->fmt.pix.height;
+ int w = vf->fmt.pix.width;
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ if (w > 720)
+ vf->fmt.pix.width = 720;
+ vf->fmt.pix.width &= 0xff0;
+ vf->fmt.pix.height = (h > hh) ? hf : hh;
+
+ if (cmd == VIDIOC_S_FMT){
+ pvr2_hdw_set_ctl_value(
+ hdw,PVR2_CID_HRES,
+ vf->fmt.pix.width);
+ pvr2_hdw_set_ctl_value(
+ hdw,PVR2_CID_VRES,
+ vf->fmt.pix.height);
+ pvr2_hdw_set_ctl_value(
+ hdw,PVR2_CID_INTERLACE,
+ (vf->fmt.pix.height != hf));
+ }
+ } break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_STREAMON:
+ {
+ ret = pvr2_hdw_set_stream_type(hdw,dev_info->config);
+ if (ret < 0) return ret;
+ ret = pvr2_hdw_set_streaming(hdw,!0);
+ break;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ ret = pvr2_hdw_set_streaming(hdw,0);
+ break;
+ }
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
+ int pvr2_id = cnv_cid_v4l2_pvr2(vc->id);
+ if (pvr2_id < 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (pvr2_hdw_get_ctl_value_name(hdw,pvr2_id,0)) {
+ vc->type = V4L2_CTRL_TYPE_MENU;
+ } else {
+ vc->type = V4L2_CTRL_TYPE_INTEGER;
+ }
+ strlcpy(vc->name,pvr2_hdw_get_ctl_name(hdw,pvr2_id),
+ sizeof(vc->name));
+ vc->minimum = pvr2_hdw_get_ctl_min_value(hdw,pvr2_id);
+ vc->maximum = pvr2_hdw_get_ctl_max_value(hdw,pvr2_id);
+ vc->step = 1;
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_QUERYMENU:
+ {
+ struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg;
+ int pvr2_id = cnv_cid_v4l2_pvr2(vm->id);
+ const char *value_name;
+ if (pvr2_id < 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ value_name = pvr2_hdw_get_ctl_value_name(hdw,pvr2_id,
+ vm->index);
+ if (value_name) {
+ strlcpy(vm->name,value_name,sizeof(vm->name));
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ break;
+ }
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ int pvr2_id;
+
+ pvr2_id = cnv_cid_v4l2_pvr2(vc->id);
+ if (pvr2_id < 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = 0;
+ vc->value = pvr2_hdw_get_ctl_value(hdw,pvr2_id);
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ int pvr2_id;
+
+ pvr2_id = cnv_cid_v4l2_pvr2(vc->id);
+ if (pvr2_id < 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = pvr2_hdw_set_ctl_value(hdw,pvr2_id,vc->value);
+ break;
+ }
+
+ default :
+ ret = v4l_compat_translate_ioctl(inode,file,cmd,
+ arg,pvr2_v4l2_do_ioctl);
+ }
+
+ pvr2_hdw_commit_ctl(hdw);
+
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "pvr2_v4l2_do_ioctl failure, ret=%d (0x%x)",
+ ret,ret);
+ } else {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl complete, ret=%d (0x%x)",
+ ret,ret);
+ }
+ return ret;
+}
+
+
+static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
+{
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Unregistering v4l video device (%s, minor=%d)",
+ pvr2_config_get_name(dip->config),dip->vdev->minor);
+ video_unregister_device(dip->vdev);
+}
+
+
+static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
+{
+ pvr2_v4l2_dev_destroy(&vp->video_dev);
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
+ pvr2_channel_done(&vp->channel);
+ kfree(vp);
+}
+
+
+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;
+ if (vp->vfirst) return;
+ pvr2_v4l2_destroy_no_lock(vp);
+}
+
+
+int pvr2_v4l2_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+/* Temporary hack : use ivtv api until a v4l2 one is available. */
+#define IVTV_IOC_G_CODEC 0xFFEE7703
+#define IVTV_IOC_S_CODEC 0xFFEE7704
+ if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
+ return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl);
+}
+
+
+int pvr2_v4l2_release(struct inode *inode, struct file *file)
+{
+ struct pvr2_v4l2_fh *fhp = file->private_data;
+ struct pvr2_v4l2 *vp = fhp->vhead;
+ struct pvr2_context *mp = fhp->vhead->channel.mc_head;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
+
+ if (fhp->rhp) {
+ struct pvr2_stream *sp;
+ struct pvr2_hdw *hdw;
+ hdw = fhp->channel.mc_head->hdw;
+ pvr2_hdw_set_streaming(hdw,0);
+ sp = pvr2_ioread_get_stream(fhp->rhp);
+ if (sp) pvr2_stream_set_callback(sp,0,0);
+ pvr2_ioread_destroy(fhp->rhp);
+ fhp->rhp = 0;
+ }
+ v4l2_prio_close(&vp->prio, &fhp->prio);
+ file->private_data = NULL;
+
+ pvr2_hdw_v4l_store_minor_number(fhp->channel.mc_head->hdw,-1);
+
+ pvr2_context_enter(mp); do {
+ if (fhp->vnext) {
+ fhp->vnext->vprev = fhp->vprev;
+ } else {
+ vp->vlast = fhp->vprev;
+ }
+ if (fhp->vprev) {
+ fhp->vprev->vnext = fhp->vnext;
+ } else {
+ vp->vfirst = fhp->vnext;
+ }
+ fhp->vnext = 0;
+ fhp->vprev = 0;
+ fhp->vhead = 0;
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p",fhp);
+ kfree(fhp);
+ if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+ pvr2_v4l2_destroy_no_lock(vp);
+ }
+ } while (0); pvr2_context_exit(mp);
+ return 0;
+}
+
+
+int pvr2_v4l2_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct pvr2_v4l2_dev *dip =
+ (struct pvr2_v4l2_dev *)video_get_drvdata(vdev);
+ struct pvr2_v4l2_fh *fhp;
+ struct pvr2_v4l2 *vp = dip->v4lp;
+ struct pvr2_hdw *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 = kmalloc(sizeof(*fhp),GFP_KERNEL);
+ if (!fhp) {
+ return -ENOMEM;
+ }
+ memset(fhp,0,sizeof(*fhp));
+
+ init_MUTEX(&fhp->sem);
+ init_waitqueue_head(&fhp->wait_data);
+ fhp->dev_info = dip;
+
+ pvr2_context_enter(vp->channel.mc_head); do {
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+ pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+ fhp->vnext = 0;
+ fhp->vprev = vp->vlast;
+ if (vp->vlast) {
+ vp->vlast->vnext = fhp;
+ } else {
+ vp->vfirst = fhp;
+ }
+ vp->vlast = fhp;
+ fhp->vhead = vp;
+ } while (0); pvr2_context_exit(vp->channel.mc_head);
+
+ fhp->file = file;
+ file->private_data = fhp;
+ v4l2_prio_open(&vp->prio,&fhp->prio);
+
+ fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
+
+ 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;
+
+ /* First read() attempt. Try to claim the stream and start
+ it... */
+ if ((ret = pvr2_channel_claim_stream(&fh->channel,
+ fh->dev_info->stream)) != 0) {
+ /* Someone else must already have it */
+ return ret;
+ }
+
+ fh->rhp = pvr2_ioread_create();
+ if (!fh->rhp) {
+ pvr2_channel_claim_stream(&fh->channel,0);
+ return -ENOMEM;
+ }
+
+ hdw = fh->channel.mc_head->hdw;
+ sp = fh->dev_info->stream->stream;
+ pvr2_ioread_setup(fh->rhp,sp);
+ pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
+ pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
+ pvr2_hdw_set_streaming(hdw,!0);
+ ret = pvr2_ioread_set_enabled(fh->rhp,!0);
+
+ return ret;
+}
+
+
+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;
+ copy_to_user(buff,tbuf,c2);
+ 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 struct file_operations vdev_fops = {
+ .owner = THIS_MODULE,
+ .open = pvr2_v4l2_open,
+ .release = pvr2_v4l2_release,
+ .read = pvr2_v4l2_read,
+ .ioctl = pvr2_v4l2_ioctl,
+ .llseek = no_llseek,
+ .poll = pvr2_v4l2_poll,
+};
+
+
+#define VID_HARDWARE_PVRUSB2 38 /* FIXME : need a good value */
+
+static struct video_device vdev_template = {
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER,
+ .type2 = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE
+ | V4L2_CAP_TUNER | V4L2_CAP_AUDIO
+ | V4L2_CAP_READWRITE),
+ .hardware = VID_HARDWARE_PVRUSB2,
+ .fops = &vdev_fops,
+};
+
+
+static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ struct pvr2_v4l2 *vp,
+ enum pvr2_config cfg)
+{
+#ifdef notdef
+ struct usb_device *usbdev;
+#endif
+ int mindevnum;
+ int unit_number;
+ int v4l_type;
+ dip->v4lp = vp;
+ dip->config = cfg;
+
+#ifdef notdef
+ usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
+#endif
+
+ switch (cfg) {
+ case pvr2_config_mpeg:
+ v4l_type = VFL_TYPE_GRABBER;
+ dip->stream = &vp->channel.mc_head->video_stream;
+ break;
+ case pvr2_config_vbi:
+ v4l_type = VFL_TYPE_VBI;
+ break;
+ case pvr2_config_radio:
+ v4l_type = VFL_TYPE_RADIO;
+ break;
+ default:
+ /* Bail out (this should be impossible) */
+ err("Failed to set up pvrusb2 v4l dev"
+ " due to unrecognized config");
+ return;
+ }
+
+ if (!dip->stream) {
+ err("Failed to set up pvrusb2 v4l dev"
+ " due to missing stream instance");
+ return;
+ }
+
+ dip->vdev = video_device_alloc();
+ if (!dip->vdev) {
+ err("Alloc of pvrusb2 v4l video device failed");
+ return;
+ }
+
+ memcpy(dip->vdev,&vdev_template,sizeof(vdev_template));
+#ifdef notdef
+ /* ????? This relation may be problematic on a disconnect. Is this
+ really needed? I can't seem to find a reason for it. This
+ can't be a required thing - what if the video device being set
+ up doesn't have a real hardware device under it? */
+ dip->vdev->dev = &usbdev->dev;
+#endif
+ dip->vdev->release = video_device_release;
+ video_set_drvdata(dip->vdev,dip);
+
+ mindevnum = -1;
+ unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw);
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ mindevnum = video_nr[unit_number];
+ }
+ if ((video_register_device(dip->vdev, v4l_type, mindevnum) < 0) &&
+ (video_register_device(dip->vdev, v4l_type, -1) < 0)) {
+ err("Failed to register pvrusb2 v4l video device");
+ } else {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Registered pvrusb2 v4l device, minor=%d",
+ dip->vdev->minor);
+ }
+ pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
+ dip->vdev->minor);
+}
+
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
+{
+ struct pvr2_v4l2 *vp;
+
+ vp = kmalloc(sizeof(*vp),GFP_KERNEL);
+ if (!vp) return vp;
+ memset(vp,0,sizeof(*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 */
+ pvr2_v4l2_dev_init(&vp->video_dev,vp,pvr2_config_mpeg);
+
+
+ return vp;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-v4l2.h b/v4l_experimental/pvrusb2/pvrusb2-v4l2.h
new file mode 100644
index 000000000..9dd446469
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-v4l2.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id: pvrusb2-v4l2.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_V4L2_H
+#define __PVRUSB2_V4L2_H
+
+#include "pvrusb2-context.h"
+
+struct pvr2_v4l2;
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *);
+
+#endif /* __PVRUSB2_V4L2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-version.h b/v4l_experimental/pvrusb2/pvrusb2-version.h
new file mode 100644
index 000000000..5e3c98a68
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-version.h
@@ -0,0 +1,4 @@
+#ifndef _INCLUDED_PVRUSB2_DRIVER_VERSION
+#define _INCLUDED_PVRUSB2_DRIVER_VERSION
+#define DRIVER_VERSION "20051113 (from v4l tree)"
+#endif
diff --git a/v4l_experimental/pvrusb2/pvrusb2-video.c b/v4l_experimental/pvrusb2/pvrusb2-video.c
new file mode 100644
index 000000000..7b4e973c5
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-video.c
@@ -0,0 +1,216 @@
+/*
+ *
+ * $Id: pvrusb2-video.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device video processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+ This source file is specifically designed to interface with the
+ saa711x support that is available in the v4l available starting
+ with linux 2.6.15.
+
+*/
+
+#include "pvrusb2-i2c.h"
+#include "pvrusb2-video.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+
+#define pvr2_decoder_trace(...) pvr2_trace(PVR2_TRACE_DECODER,__VA_ARGS__)
+
+int pvr2_decoder_enable_output(struct pvr2_hdw *hdw,int fl)
+{
+ int status;
+ pvr2_decoder_trace("pvr2_decoder_enable_output(%d)",fl);
+ if (fl) {
+ status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_STREAMON,0);
+ } else {
+ status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_STREAMOFF,0);
+ }
+ if (!status) {
+ hdw->subsys_enabled_mask =
+ ((hdw->subsys_enabled_mask &
+ ~PVR2_SUBSYS_DIGITIZER_RUN) |
+ (fl ? PVR2_SUBSYS_DIGITIZER_RUN : 0));
+ }
+ return status;
+}
+
+int pvr2_decoder_set_input(struct pvr2_hdw *hdw)
+{
+ int status;
+ int v = 0;
+ pvr2_decoder_trace("pvr2_decoder_set_input(%d)",
+ hdw->controls[PVR2_CID_INPUT].value);
+ switch(hdw->controls[PVR2_CID_INPUT].value){
+ case PVR2_CVAL_INPUT_TV:
+ v = 4;
+ break;
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ v = 5;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ v = 8;
+ break;
+ case PVR2_CVAL_INPUT_RADIO:
+ // ????? No idea yet what to do here
+ default:
+ return -EINVAL;
+ }
+ status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_INPUT,&v);
+ if (!status) {
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_INPUT;
+ }
+ return status;
+}
+
+int pvr2_decoder_set_bcsh(struct pvr2_hdw *hdw)
+{
+ struct v4l2_control ctrl;
+ int ret;
+ memset(&ctrl,0,sizeof(ctrl));
+
+ pvr2_decoder_trace("pvr2_decoder_set_bcsh b=%d c=%d s=%d h=%d",
+ hdw->controls[PVR2_CID_BRIGHTNESS].value,
+ hdw->controls[PVR2_CID_CONTRAST].value,
+ hdw->controls[PVR2_CID_SATURATION].value,
+ hdw->controls[PVR2_CID_HUE].value);
+
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = hdw->controls[PVR2_CID_BRIGHTNESS].value;
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ if (ret) return ret;
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = hdw->controls[PVR2_CID_CONTRAST].value;
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ if (ret) return ret;
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = hdw->controls[PVR2_CID_SATURATION].value;
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ if (ret) return ret;
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = hdw->controls[PVR2_CID_HUE].value;
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ if (ret) return ret;
+
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_BCSH;
+ return 0;
+}
+
+int pvr2_decoder_is_tuned(struct pvr2_hdw *hdw)
+{
+ struct v4l2_tuner vt;
+ int ret;
+
+ memset(&vt,0,sizeof(vt));
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_G_TUNER,&vt);
+ if (ret < 0) return 0;
+ return vt.signal != 0;
+}
+
+int pvr2_decoder_set_size(struct pvr2_hdw *hdw)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memset(&fmt,0,sizeof(fmt));
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = hdw->controls[PVR2_CID_HRES].value;
+ fmt.fmt.pix.height = hdw->controls[PVR2_CID_VRES].value;
+
+ pvr2_decoder_trace("pvr2_decoder_set_size(%dx%d)",
+ fmt.fmt.pix.width,fmt.fmt.pix.height);
+
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_FMT,&fmt);
+ if (ret) return ret;
+
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_SIZE;
+ return 0;
+}
+
+int pvr2_decoder_set_audio(struct pvr2_hdw *hdw)
+{
+ int ret;
+ enum v4l2_audio_clock_freq val;
+
+ pvr2_decoder_trace("pvr2_decoder_set_audio %d",
+ hdw->controls[PVR2_CID_SRATE].value);
+ switch (hdw->controls[PVR2_CID_SRATE].value) {
+ default:
+ case PVR2_CVAL_SRATE_48:
+ val = V4L2_AUDCLK_48_KHZ;
+ break;
+ case PVR2_CVAL_SRATE_44_1:
+ val = V4L2_AUDCLK_441_KHZ;
+ break;
+ }
+ ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
+ if (ret) return ret;
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_AUDIO;
+ return 0;
+}
+
+int pvr2_decoder_set_norm(struct pvr2_hdw *hdw)
+{
+ v4l2_std_id std;
+ int status;
+ pvr2_decoder_trace("pvr2_decoder_set_norm %d",
+ hdw->controls[PVR2_CID_VIDEOSTANDARD].value);
+ switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) {
+ default:
+ case PVR2_CVAL_VIDEOSTANDARD_NTSC_M:
+ std = V4L2_STD_NTSC;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_SECAM_L:
+ std = V4L2_STD_SECAM;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_I:
+ std = V4L2_STD_PAL_I;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_DK:
+ std = V4L2_STD_PAL_DK;
+ break;
+ case PVR2_CVAL_VIDEOSTANDARD_PAL_BG:
+ std = V4L2_STD_PAL_BG;
+ break;
+ }
+ status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_STD,&std);
+ if (status) return status;
+ hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_NORM;
+ return 0;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2-video.h b/v4l_experimental/pvrusb2/pvrusb2-video.h
new file mode 100644
index 000000000..8c081ccd2
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-video.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * $Id: pvrusb2-video.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_VIDEO_H
+#define __PVRUSB2_VIDEO_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device video processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+*/
+
+struct pvr2_hdw;
+
+int pvr2_decoder_set_norm(struct pvr2_hdw *);
+int pvr2_decoder_set_input(struct pvr2_hdw *);
+int pvr2_decoder_set_size(struct pvr2_hdw *);
+int pvr2_decoder_set_audio(struct pvr2_hdw *);
+int pvr2_decoder_set_bcsh(struct pvr2_hdw *);
+int pvr2_decoder_is_tuned(struct pvr2_hdw *);
+int pvr2_decoder_enable_output(struct pvr2_hdw *,int);
+
+#endif /* __PVRUSB2_VIDEO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/v4l_experimental/pvrusb2/pvrusb2.h b/v4l_experimental/pvrusb2/pvrusb2.h
new file mode 100644
index 000000000..f4e99ad3b
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * $Id: pvrusb2.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_H
+#define __PVRUSB2_H
+
+/* Maximum number of pvrusb2 instances we can track at once. You
+ might want to increase this - however the driver operation will not
+ be impaired if it is too small. Instead additional units just
+ won't have an ID assigned and it might not be possible to specify
+ module paramters for those extra units. */
+#define PVR_NUM 20
+
+#endif /* __PVRUSB2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */