forked from luck/tmp_suning_uos_patched
V4L/DVB (4228a): pvrusb2 to kernel 2.6.18
Implement V4L2 driver for the Hauppauge PVR USB2 TV tuner. The Hauppauge PVR USB2 is a USB connected TV tuner with an embedded cx23416 hardware MPEG2 encoder. There are two major variants of this device; this driver handles both. Any V4L2 application which understands MPEG2 video stream data should be able to work with this device. Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
eb99adde31
commit
d855497edb
212
Documentation/video4linux/README.pvrusb2
Normal file
212
Documentation/video4linux/README.pvrusb2
Normal file
|
@ -0,0 +1,212 @@
|
|||
|
||||
$Id$
|
||||
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-chips-*.c - These modules implement the glue logic to
|
||||
tie together and configure various I2C modules as they attach to
|
||||
the I2C bus. There are two versions of this file. The "v4l2"
|
||||
version is intended to be used in-tree alongside V4L, where we
|
||||
implement just the logic that makes sense for a pure V4L
|
||||
environment. The "all" version is intended for use outside of
|
||||
V4L, where we might encounter other possibly "challenging" modules
|
||||
from ivtv or older kernel snapshots (or even the support modules
|
||||
in the standalone snapshot).
|
||||
|
||||
pvrusb2-i2c-cmd-v4l1.[ch] - This module implements generic V4L1
|
||||
compatible commands to the I2C modules. It is here where state
|
||||
changes inside the pvrusb2 driver are translated into V4L1
|
||||
commands that are in turn send to the various I2C modules.
|
||||
|
||||
pvrusb2-i2c-cmd-v4l2.[ch] - This module implements generic V4L2
|
||||
compatible commands to the I2C modules. It is here where state
|
||||
changes inside the pvrusb2 driver are translated into V4L2
|
||||
commands that are in turn send to the various I2C modules.
|
||||
|
||||
pvrusb2-i2c-core.[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 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. There are two versions of this; one is
|
||||
selected depending on the particular saa711[5x].ko that is found.
|
||||
|
||||
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
|
||||
|
|
@ -445,6 +445,8 @@ endmenu # encoder / decoder chips
|
|||
menu "V4L USB devices"
|
||||
depends on USB && VIDEO_DEV
|
||||
|
||||
source "drivers/media/video/pvrusb2/Kconfig"
|
||||
|
||||
source "drivers/media/video/em28xx/Kconfig"
|
||||
|
||||
config USB_DSBR
|
||||
|
|
|
@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
|
|||
obj-$(CONFIG_VIDEO_CX88) += cx88/
|
||||
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
|
||||
obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o
|
||||
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
|
||||
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
|
||||
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
|
||||
obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
|
||||
|
|
62
drivers/media/video/pvrusb2/Kconfig
Normal file
62
drivers/media/video/pvrusb2/Kconfig
Normal file
|
@ -0,0 +1,62 @@
|
|||
config VIDEO_PVRUSB2
|
||||
tristate "Hauppauge WinTV-PVR USB2 support"
|
||||
depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select VIDEO_TUNER
|
||||
select VIDEO_TVEEPROM
|
||||
select VIDEO_CX2341X
|
||||
select VIDEO_SAA711X
|
||||
select VIDEO_MSP3400
|
||||
---help---
|
||||
This is a video4linux driver for Conexant 23416 based
|
||||
usb2 personal video recorder devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pvrusb2
|
||||
|
||||
config VIDEO_PVRUSB2_24XXX
|
||||
bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series"
|
||||
depends on VIDEO_PVRUSB2 && EXPERIMENTAL
|
||||
select VIDEO_CX25840
|
||||
select VIDEO_WM8775
|
||||
---help---
|
||||
This option enables inclusion of additional logic to operate
|
||||
newer WinTV-PVR USB2 devices whose model number is of the
|
||||
form "24xxx" (leading prefix of "24" followed by 3 digits).
|
||||
To see if you may need this option, examine the white
|
||||
sticker on the underside of your device. Enabling this
|
||||
option will not harm support for older devices, however it
|
||||
is a separate option because of the experimental nature of
|
||||
this new feature.
|
||||
|
||||
If you are in doubt, say N.
|
||||
|
||||
Note: This feature is _very_ experimental. You have been
|
||||
warned.
|
||||
|
||||
config VIDEO_PVRUSB2_SYSFS
|
||||
bool "pvrusb2 sysfs support (EXPERIMENTAL)"
|
||||
default y
|
||||
depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL
|
||||
---help---
|
||||
This option enables the operation of a sysfs based
|
||||
interface for query and control of the pvrusb2 driver.
|
||||
|
||||
This is not generally needed for v4l applications,
|
||||
although certain applications are optimized to take
|
||||
advantage of this feature.
|
||||
|
||||
If you are in doubt, say Y.
|
||||
|
||||
Note: This feature is experimental and subject to change.
|
||||
|
||||
config VIDEO_PVRUSB2_DEBUGIFC
|
||||
bool "pvrusb2 debug interface"
|
||||
depends on VIDEO_PVRUSB2_SYSFS
|
||||
---help---
|
||||
This option enables the inclusion of a debug interface
|
||||
in the pvrusb2 driver, hosted through sysfs.
|
||||
|
||||
You do not need to select this option unless you plan
|
||||
on debugging the driver or performing a manual firmware
|
||||
extraction.
|
18
drivers/media/video/pvrusb2/Makefile
Normal file
18
drivers/media/video/pvrusb2/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
|
||||
obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
|
||||
|
||||
obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \
|
||||
pvrusb2-cx2584x-v4l.o \
|
||||
pvrusb2-wm8775.o
|
||||
|
||||
pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
|
||||
pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
|
||||
pvrusb2-encoder.o pvrusb2-video-v4l.o \
|
||||
pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \
|
||||
pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
|
||||
pvrusb2-ctrl.o pvrusb2-std.o \
|
||||
pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
|
||||
$(obj-pvrusb2-24xxx-y) \
|
||||
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
|
||||
|
||||
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
|
204
drivers/media/video/pvrusb2/pvrusb2-audio.c
Normal file
204
drivers/media/video/pvrusb2/pvrusb2-audio.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-audio.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/msp3400.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
struct pvr2_msp3400_handler {
|
||||
struct pvr2_hdw *hdw;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_i2c_handler i2c_handler;
|
||||
struct pvr2_audio_stat astat;
|
||||
unsigned long stale_mask;
|
||||
};
|
||||
|
||||
|
||||
/* This function selects the correct audio input source */
|
||||
static void set_stereo(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
struct v4l2_routing route;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo");
|
||||
|
||||
if (hdw->input_val == PVR2_CVAL_INPUT_TV) {
|
||||
struct v4l2_tuner vt;
|
||||
memset(&vt,0,sizeof(vt));
|
||||
vt.audmode = hdw->audiomode_val;
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt);
|
||||
}
|
||||
|
||||
route.input = MSP_INPUT_DEFAULT;
|
||||
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
|
||||
switch (hdw->input_val) {
|
||||
case PVR2_CVAL_INPUT_TV:
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_RADIO:
|
||||
/* Assume that msp34xx also handle FM decoding, in which case
|
||||
we're still using the tuner. */
|
||||
/* HV: actually it is more likely to be the SCART2 input if
|
||||
the ivtv experience is any indication. */
|
||||
route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
|
||||
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_SVIDEO:
|
||||
case PVR2_CVAL_INPUT_COMPOSITE:
|
||||
/* SCART 1 input */
|
||||
route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
|
||||
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
|
||||
break;
|
||||
}
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
|
||||
}
|
||||
|
||||
|
||||
static int check_stereo(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return (hdw->input_dirty ||
|
||||
hdw->audiomode_dirty);
|
||||
}
|
||||
|
||||
|
||||
struct pvr2_msp3400_ops {
|
||||
void (*update)(struct pvr2_msp3400_handler *);
|
||||
int (*check)(struct pvr2_msp3400_handler *);
|
||||
};
|
||||
|
||||
|
||||
static const struct pvr2_msp3400_ops msp3400_ops[] = {
|
||||
{ .update = set_stereo, .check = check_stereo},
|
||||
};
|
||||
|
||||
|
||||
static int msp3400_check(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (ctxt->stale_mask & msk) continue;
|
||||
if (msp3400_ops[idx].check(ctxt)) {
|
||||
ctxt->stale_mask |= msk;
|
||||
}
|
||||
}
|
||||
return ctxt->stale_mask != 0;
|
||||
}
|
||||
|
||||
|
||||
static void msp3400_update(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (!(ctxt->stale_mask & msk)) continue;
|
||||
ctxt->stale_mask &= ~msk;
|
||||
msp3400_ops[idx].update(ctxt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This reads back the current signal type */
|
||||
static int get_audio_status(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
struct v4l2_tuner vt;
|
||||
int stat;
|
||||
|
||||
memset(&vt,0,sizeof(vt));
|
||||
stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
|
||||
if (stat < 0) return stat;
|
||||
|
||||
ctxt->hdw->flag_stereo = (vt.audmode & V4L2_TUNER_MODE_STEREO) != 0;
|
||||
ctxt->hdw->flag_bilingual =
|
||||
(vt.audmode & V4L2_TUNER_MODE_LANG2) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
ctxt->hdw->audio_stat = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt,
|
||||
char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2");
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions msp3400_funcs = {
|
||||
.detach = (void (*)(void *))pvr2_msp3400_detach,
|
||||
.check = (int (*)(void *))msp3400_check,
|
||||
.update = (void (*)(void *))msp3400_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_msp3400_handler *ctxt;
|
||||
if (hdw->audio_stat) return 0;
|
||||
if (cp->handler) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->i2c_handler.func_data = ctxt;
|
||||
ctxt->i2c_handler.func_table = &msp3400_funcs;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
ctxt->astat.ctxt = ctxt;
|
||||
ctxt->astat.status = (int (*)(void *))get_audio_status;
|
||||
ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach;
|
||||
ctxt->stale_mask = (1 << (sizeof(msp3400_ops)/
|
||||
sizeof(msp3400_ops[0]))) - 1;
|
||||
cp->handler = &ctxt->i2c_handler;
|
||||
hdw->audio_stat = &ctxt->astat;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
40
drivers/media/video/pvrusb2/pvrusb2-audio.h
Normal file
40
drivers/media/video/pvrusb2/pvrusb2-audio.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
#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: ***
|
||||
*/
|
230
drivers/media/video/pvrusb2/pvrusb2-context.c
Normal file
230
drivers/media/video/pvrusb2/pvrusb2-context.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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-ioread.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_trigger_poll(struct pvr2_context *mp)
|
||||
{
|
||||
queue_work(mp->workqueue,&mp->workpoll);
|
||||
}
|
||||
|
||||
|
||||
static void pvr2_context_poll(struct pvr2_context *mp)
|
||||
{
|
||||
pvr2_context_enter(mp); do {
|
||||
pvr2_hdw_poll(mp->hdw);
|
||||
} while (0); pvr2_context_exit(mp);
|
||||
}
|
||||
|
||||
|
||||
static void pvr2_context_setup(struct pvr2_context *mp)
|
||||
{
|
||||
pvr2_context_enter(mp); do {
|
||||
if (!pvr2_hdw_dev_ok(mp->hdw)) break;
|
||||
pvr2_hdw_setup(mp->hdw);
|
||||
pvr2_hdw_setup_poll_trigger(
|
||||
mp->hdw,
|
||||
(void (*)(void *))pvr2_context_trigger_poll,
|
||||
mp);
|
||||
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,
|
||||
const struct usb_device_id *devid,
|
||||
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;
|
||||
mutex_init(&mp->mutex);
|
||||
mp->hdw = pvr2_hdw_create(intf,devid);
|
||||
if (!mp->hdw) {
|
||||
pvr2_context_destroy(mp);
|
||||
mp = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mp->workqueue = create_singlethread_workqueue("pvrusb2");
|
||||
INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp);
|
||||
INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp);
|
||||
queue_work(mp->workqueue,&mp->workinit);
|
||||
done:
|
||||
return mp;
|
||||
}
|
||||
|
||||
|
||||
void pvr2_context_enter(struct pvr2_context *mp)
|
||||
{
|
||||
mutex_lock(&mp->mutex);
|
||||
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)) {
|
||||
destroy_flag = !0;
|
||||
}
|
||||
pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
|
||||
mutex_unlock(&mp->mutex);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// This is the marker for the real beginning of a legitimate mpeg2 stream.
|
||||
static char stream_sync_key[] = {
|
||||
0x00, 0x00, 0x01, 0xba,
|
||||
};
|
||||
|
||||
struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
|
||||
struct pvr2_context_stream *sp)
|
||||
{
|
||||
struct pvr2_ioread *cp;
|
||||
cp = pvr2_ioread_create();
|
||||
if (!cp) return 0;
|
||||
pvr2_ioread_setup(cp,sp->stream);
|
||||
pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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: ***
|
||||
*/
|
92
drivers/media/video/pvrusb2/pvrusb2-context.h
Normal file
92
drivers/media/video/pvrusb2/pvrusb2-context.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/mutex.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_ioread; /* Low level stream structure */
|
||||
|
||||
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 mutex mutex;
|
||||
int disconnect_flag;
|
||||
|
||||
/* 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 work_struct workpoll;
|
||||
};
|
||||
|
||||
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,
|
||||
const struct usb_device_id *devid,
|
||||
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 *);
|
||||
struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
|
||||
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: ***
|
||||
*/
|
544
drivers/media/video/pvrusb2/pvrusb2-ctrl.c
Normal file
544
drivers/media/video/pvrusb2/pvrusb2-ctrl.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-ctrl.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
||||
/* Set the given control. */
|
||||
int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
|
||||
{
|
||||
return pvr2_ctrl_set_mask_value(cptr,~0,val);
|
||||
}
|
||||
|
||||
|
||||
/* Set/clear specific bits of the given control. */
|
||||
int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return -EINVAL;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->set_value != 0) {
|
||||
if (cptr->info->type == pvr2_ctl_bitmask) {
|
||||
mask &= cptr->info->def.type_bitmask.valid_bits;
|
||||
} else if (cptr->info->type == pvr2_ctl_int) {
|
||||
if (val < cptr->info->def.type_int.min_value) {
|
||||
break;
|
||||
}
|
||||
if (val > cptr->info->def.type_int.max_value) {
|
||||
break;
|
||||
}
|
||||
} else if (cptr->info->type == pvr2_ctl_enum) {
|
||||
if (val >= cptr->info->def.type_enum.count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = cptr->info->set_value(cptr,mask,val);
|
||||
} else {
|
||||
ret = -EPERM;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Get the current value of the given control. */
|
||||
int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return -EINVAL;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
ret = cptr->info->get_value(cptr,valptr);
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's type */
|
||||
enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
if (!cptr) return pvr2_ctl_int;
|
||||
return cptr->info->type;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's maximum value (int type) */
|
||||
int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_int) {
|
||||
ret = cptr->info->def.type_int.max_value;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's minimum value (int type) */
|
||||
int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_int) {
|
||||
ret = cptr->info->def.type_int.min_value;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's default value (any type) */
|
||||
int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_int) {
|
||||
ret = cptr->info->default_value;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's enumeration count (enum only) */
|
||||
int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_enum) {
|
||||
ret = cptr->info->def.type_enum.count;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve control's valid mask bits (bit mask only) */
|
||||
int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!cptr) return 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_bitmask) {
|
||||
ret = cptr->info->def.type_bitmask.valid_bits;
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve the control's name */
|
||||
const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
if (!cptr) return 0;
|
||||
return cptr->info->name;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve the control's desc */
|
||||
const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
if (!cptr) return 0;
|
||||
return cptr->info->desc;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve a control enumeration or bit mask value */
|
||||
int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
|
||||
char *bptr,unsigned int bmax,
|
||||
unsigned int *blen)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
if (!cptr) return 0;
|
||||
*blen = 0;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_enum) {
|
||||
const char **names;
|
||||
names = cptr->info->def.type_enum.value_names;
|
||||
if ((val >= 0) &&
|
||||
(val < cptr->info->def.type_enum.count)) {
|
||||
if (names[val]) {
|
||||
*blen = scnprintf(
|
||||
bptr,bmax,"%s",
|
||||
names[val]);
|
||||
} else {
|
||||
*blen = 0;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
||||
const char **names;
|
||||
unsigned int idx;
|
||||
int msk;
|
||||
names = cptr->info->def.type_bitmask.bit_names;
|
||||
val &= cptr->info->def.type_bitmask.valid_bits;
|
||||
for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
|
||||
if (val & msk) {
|
||||
*blen = scnprintf(bptr,bmax,"%s",
|
||||
names[idx]);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Return true if control is writable */
|
||||
int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
if (!cptr) return 0;
|
||||
return cptr->info->set_value != 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return true if control has custom symbolic representation */
|
||||
int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
|
||||
{
|
||||
if (!cptr) return 0;
|
||||
if (!cptr->info->val_to_sym) return 0;
|
||||
if (!cptr->info->sym_to_val) return 0;
|
||||
return !0;
|
||||
}
|
||||
|
||||
|
||||
/* Convert a given mask/val to a custom symbolic value */
|
||||
int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len)
|
||||
{
|
||||
if (!cptr) return -EINVAL;
|
||||
if (!cptr->info->val_to_sym) return -EINVAL;
|
||||
return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
|
||||
}
|
||||
|
||||
|
||||
/* Convert a symbolic value to a mask/value pair */
|
||||
int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
|
||||
const char *buf,unsigned int len,
|
||||
int *maskptr,int *valptr)
|
||||
{
|
||||
if (!cptr) return -EINVAL;
|
||||
if (!cptr->info->sym_to_val) return -EINVAL;
|
||||
return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int gen_bitmask_string(int msk,int val,int msk_only,
|
||||
const char **names,
|
||||
char *ptr,unsigned int len)
|
||||
{
|
||||
unsigned int idx;
|
||||
long sm,um;
|
||||
int spcFl;
|
||||
unsigned int uc,cnt;
|
||||
const char *idStr;
|
||||
|
||||
spcFl = 0;
|
||||
uc = 0;
|
||||
um = 0;
|
||||
for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
|
||||
if (sm & msk) {
|
||||
msk &= ~sm;
|
||||
idStr = names[idx];
|
||||
if (idStr) {
|
||||
cnt = scnprintf(ptr,len,"%s%s%s",
|
||||
(spcFl ? " " : ""),
|
||||
(msk_only ? "" :
|
||||
((val & sm) ? "+" : "-")),
|
||||
idStr);
|
||||
ptr += cnt; len -= cnt; uc += cnt;
|
||||
spcFl = !0;
|
||||
} else {
|
||||
um |= sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (um) {
|
||||
if (msk_only) {
|
||||
cnt = scnprintf(ptr,len,"%s0x%lx",
|
||||
(spcFl ? " " : ""),
|
||||
um);
|
||||
ptr += cnt; len -= cnt; uc += cnt;
|
||||
spcFl = !0;
|
||||
} else if (um & val) {
|
||||
cnt = scnprintf(ptr,len,"%s+0x%lx",
|
||||
(spcFl ? " " : ""),
|
||||
um & val);
|
||||
ptr += cnt; len -= cnt; uc += cnt;
|
||||
spcFl = !0;
|
||||
} else if (um & ~val) {
|
||||
cnt = scnprintf(ptr,len,"%s+0x%lx",
|
||||
(spcFl ? " " : ""),
|
||||
um & ~val);
|
||||
ptr += cnt; len -= cnt; uc += cnt;
|
||||
spcFl = !0;
|
||||
}
|
||||
}
|
||||
return uc;
|
||||
}
|
||||
|
||||
|
||||
static int parse_token(const char *ptr,unsigned int len,
|
||||
int *valptr,
|
||||
const char **names,unsigned int namecnt)
|
||||
{
|
||||
char buf[33];
|
||||
unsigned int slen;
|
||||
unsigned int idx;
|
||||
int negfl;
|
||||
char *p2;
|
||||
*valptr = 0;
|
||||
if (!names) namecnt = 0;
|
||||
for (idx = 0; idx < namecnt; idx++) {
|
||||
if (!names[idx]) continue;
|
||||
slen = strlen(names[idx]);
|
||||
if (slen != len) continue;
|
||||
if (memcmp(names[idx],ptr,slen)) continue;
|
||||
*valptr = idx;
|
||||
return 0;
|
||||
}
|
||||
negfl = 0;
|
||||
if ((*ptr == '-') || (*ptr == '+')) {
|
||||
negfl = (*ptr == '-');
|
||||
ptr++; len--;
|
||||
}
|
||||
if (len >= sizeof(buf)) return -EINVAL;
|
||||
memcpy(buf,ptr,len);
|
||||
buf[len] = 0;
|
||||
*valptr = simple_strtol(buf,&p2,0);
|
||||
if (negfl) *valptr = -(*valptr);
|
||||
if (*p2) return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_mtoken(const char *ptr,unsigned int len,
|
||||
int *valptr,
|
||||
const char **names,int valid_bits)
|
||||
{
|
||||
char buf[33];
|
||||
unsigned int slen;
|
||||
unsigned int idx;
|
||||
char *p2;
|
||||
int msk;
|
||||
*valptr = 0;
|
||||
for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
|
||||
if (!msk & valid_bits) continue;
|
||||
valid_bits &= ~msk;
|
||||
if (!names[idx]) continue;
|
||||
slen = strlen(names[idx]);
|
||||
if (slen != len) continue;
|
||||
if (memcmp(names[idx],ptr,slen)) continue;
|
||||
*valptr = msk;
|
||||
return 0;
|
||||
}
|
||||
if (len >= sizeof(buf)) return -EINVAL;
|
||||
memcpy(buf,ptr,len);
|
||||
buf[len] = 0;
|
||||
*valptr = simple_strtol(buf,&p2,0);
|
||||
if (*p2) return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_tlist(const char *ptr,unsigned int len,
|
||||
int *maskptr,int *valptr,
|
||||
const char **names,int valid_bits)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int mask,val,kv,mode,ret;
|
||||
mask = 0;
|
||||
val = 0;
|
||||
ret = 0;
|
||||
while (len) {
|
||||
cnt = 0;
|
||||
while ((cnt < len) &&
|
||||
((ptr[cnt] <= 32) ||
|
||||
(ptr[cnt] >= 127))) cnt++;
|
||||
ptr += cnt;
|
||||
len -= cnt;
|
||||
mode = 0;
|
||||
if ((*ptr == '-') || (*ptr == '+')) {
|
||||
mode = (*ptr == '-') ? -1 : 1;
|
||||
ptr++;
|
||||
len--;
|
||||
}
|
||||
cnt = 0;
|
||||
while (cnt < len) {
|
||||
if (ptr[cnt] <= 32) break;
|
||||
if (ptr[cnt] >= 127) break;
|
||||
cnt++;
|
||||
}
|
||||
if (!cnt) break;
|
||||
if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ptr += cnt;
|
||||
len -= cnt;
|
||||
switch (mode) {
|
||||
case 0:
|
||||
mask = valid_bits;
|
||||
val |= kv;
|
||||
break;
|
||||
case -1:
|
||||
mask |= kv;
|
||||
val &= ~kv;
|
||||
break;
|
||||
case 1:
|
||||
mask |= kv;
|
||||
val |= kv;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
*maskptr = mask;
|
||||
*valptr = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Convert a symbolic value to a mask/value pair */
|
||||
int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
|
||||
const char *ptr,unsigned int len,
|
||||
int *maskptr,int *valptr)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
unsigned int cnt;
|
||||
|
||||
*maskptr = 0;
|
||||
*valptr = 0;
|
||||
|
||||
cnt = 0;
|
||||
while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
|
||||
len -= cnt; ptr += cnt;
|
||||
cnt = 0;
|
||||
while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
|
||||
(ptr[len-(cnt+1)] >= 127))) cnt++;
|
||||
len -= cnt;
|
||||
|
||||
if (!len) return -EINVAL;
|
||||
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
if (cptr->info->type == pvr2_ctl_int) {
|
||||
ret = parse_token(ptr,len,valptr,0,0);
|
||||
if ((ret == 0) &&
|
||||
((*valptr < cptr->info->def.type_int.min_value) ||
|
||||
(*valptr > cptr->info->def.type_int.max_value))) {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (maskptr) *maskptr = ~0;
|
||||
} else if (cptr->info->type == pvr2_ctl_enum) {
|
||||
ret = parse_token(
|
||||
ptr,len,valptr,
|
||||
cptr->info->def.type_enum.value_names,
|
||||
cptr->info->def.type_enum.count);
|
||||
if ((ret == 0) &&
|
||||
((*valptr < 0) ||
|
||||
(*valptr >= cptr->info->def.type_enum.count))) {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (maskptr) *maskptr = ~0;
|
||||
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
||||
ret = parse_tlist(
|
||||
ptr,len,maskptr,valptr,
|
||||
cptr->info->def.type_bitmask.bit_names,
|
||||
cptr->info->def.type_bitmask.valid_bits);
|
||||
}
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Convert a given mask/val to a symbolic value */
|
||||
int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
*len = 0;
|
||||
if (cptr->info->type == pvr2_ctl_int) {
|
||||
*len = scnprintf(buf,maxlen,"%d",val);
|
||||
ret = 0;
|
||||
} else if (cptr->info->type == pvr2_ctl_enum) {
|
||||
const char **names;
|
||||
names = cptr->info->def.type_enum.value_names;
|
||||
if ((val >= 0) &&
|
||||
(val < cptr->info->def.type_enum.count)) {
|
||||
if (names[val]) {
|
||||
*len = scnprintf(
|
||||
buf,maxlen,"%s",
|
||||
names[val]);
|
||||
} else {
|
||||
*len = 0;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
||||
*len = gen_bitmask_string(
|
||||
val & mask & cptr->info->def.type_bitmask.valid_bits,
|
||||
~0,!0,
|
||||
cptr->info->def.type_bitmask.bit_names,
|
||||
buf,maxlen);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Convert a given mask/val to a symbolic value */
|
||||
int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len)
|
||||
{
|
||||
int ret;
|
||||
LOCK_TAKE(cptr->hdw->big_lock); do {
|
||||
ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
|
||||
buf,maxlen,len);
|
||||
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
||||
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: ***
|
||||
*/
|
115
drivers/media/video/pvrusb2/pvrusb2-ctrl.h
Normal file
115
drivers/media/video/pvrusb2/pvrusb2-ctrl.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_CTRL_H
|
||||
#define __PVRUSB2_CTRL_H
|
||||
|
||||
struct pvr2_ctrl;
|
||||
|
||||
enum pvr2_ctl_type {
|
||||
pvr2_ctl_int = 0,
|
||||
pvr2_ctl_enum = 1,
|
||||
pvr2_ctl_bitmask = 2,
|
||||
};
|
||||
|
||||
|
||||
/* Set the given control. */
|
||||
int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val);
|
||||
|
||||
/* Set/clear specific bits of the given control. */
|
||||
int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val);
|
||||
|
||||
/* Get the current value of the given control. */
|
||||
int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr);
|
||||
|
||||
/* Retrieve control's type */
|
||||
enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve control's maximum value (int type) */
|
||||
int pvr2_ctrl_get_max(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve control's minimum value (int type) */
|
||||
int pvr2_ctrl_get_min(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve control's default value (any type) */
|
||||
int pvr2_ctrl_get_def(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve control's enumeration count (enum only) */
|
||||
int pvr2_ctrl_get_cnt(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve control's valid mask bits (bit mask only) */
|
||||
int pvr2_ctrl_get_mask(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve the control's name */
|
||||
const char *pvr2_ctrl_get_name(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve the control's desc */
|
||||
const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *);
|
||||
|
||||
/* Retrieve a control enumeration or bit mask value */
|
||||
int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int,
|
||||
unsigned int *);
|
||||
|
||||
/* Return true if control is writable */
|
||||
int pvr2_ctrl_is_writable(struct pvr2_ctrl *);
|
||||
|
||||
/* Return true if control has custom symbolic representation */
|
||||
int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *);
|
||||
|
||||
/* Convert a given mask/val to a custom symbolic value */
|
||||
int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len);
|
||||
|
||||
/* Convert a symbolic value to a mask/value pair */
|
||||
int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *,
|
||||
const char *buf,unsigned int len,
|
||||
int *maskptr,int *valptr);
|
||||
|
||||
/* Convert a given mask/val to a symbolic value */
|
||||
int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len);
|
||||
|
||||
/* Convert a symbolic value to a mask/value pair */
|
||||
int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *,
|
||||
const char *buf,unsigned int len,
|
||||
int *maskptr,int *valptr);
|
||||
|
||||
/* Convert a given mask/val to a symbolic value - must already be
|
||||
inside of critical region. */
|
||||
int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *,
|
||||
int mask,int val,
|
||||
char *buf,unsigned int maxlen,
|
||||
unsigned int *len);
|
||||
|
||||
#endif /* __PVRUSB2_CTRL_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: ***
|
||||
*/
|
276
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
Normal file
276
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 source file is specifically designed to interface with the
|
||||
cx2584x, in kernels 2.6.16 or newer.
|
||||
|
||||
*/
|
||||
|
||||
#include "pvrusb2-cx2584x-v4l.h"
|
||||
#include "pvrusb2-video-v4l.h"
|
||||
#include "pvrusb2-i2c-cmd-v4l2.h"
|
||||
|
||||
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <media/cx25840.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct pvr2_v4l_cx2584x {
|
||||
struct pvr2_i2c_handler handler;
|
||||
struct pvr2_decoder_ctrl ctrl;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_hdw *hdw;
|
||||
unsigned long stale_mask;
|
||||
};
|
||||
|
||||
|
||||
static void set_input(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
struct v4l2_routing route;
|
||||
enum cx25840_video_input vid_input;
|
||||
enum cx25840_audio_input aud_input;
|
||||
|
||||
memset(&route,0,sizeof(route));
|
||||
|
||||
switch(hdw->input_val) {
|
||||
case PVR2_CVAL_INPUT_TV:
|
||||
vid_input = CX25840_COMPOSITE7;
|
||||
aud_input = CX25840_AUDIO8;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_COMPOSITE:
|
||||
vid_input = CX25840_COMPOSITE3;
|
||||
aud_input = CX25840_AUDIO_SERIAL;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_SVIDEO:
|
||||
vid_input = CX25840_SVIDEO1;
|
||||
aud_input = CX25840_AUDIO_SERIAL;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_RADIO:
|
||||
default:
|
||||
// Just set it to be composite input for now...
|
||||
vid_input = CX25840_COMPOSITE3;
|
||||
aud_input = CX25840_AUDIO_SERIAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x",
|
||||
vid_input,aud_input);
|
||||
route.input = (u32)vid_input;
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
|
||||
route.input = (u32)aud_input;
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
|
||||
}
|
||||
|
||||
|
||||
static int check_input(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return hdw->input_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
static void set_audio(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
u32 val;
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d",
|
||||
hdw->srate_val);
|
||||
switch (hdw->srate_val) {
|
||||
default:
|
||||
case PVR2_CVAL_SRATE_48:
|
||||
val = 48000;
|
||||
break;
|
||||
case PVR2_CVAL_SRATE_44_1:
|
||||
val = 44100;
|
||||
break;
|
||||
}
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
|
||||
}
|
||||
|
||||
|
||||
static int check_audio(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return hdw->srate_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
struct pvr2_v4l_cx2584x_ops {
|
||||
void (*update)(struct pvr2_v4l_cx2584x *);
|
||||
int (*check)(struct pvr2_v4l_cx2584x *);
|
||||
};
|
||||
|
||||
|
||||
static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
|
||||
{ .update = set_input, .check = check_input},
|
||||
{ .update = set_audio, .check = check_audio},
|
||||
};
|
||||
|
||||
|
||||
static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
ctxt->hdw->decoder_ctrl = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static int decoder_check(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (ctxt->stale_mask & msk) continue;
|
||||
if (decoder_ops[idx].check(ctxt)) {
|
||||
ctxt->stale_mask |= msk;
|
||||
}
|
||||
}
|
||||
return ctxt->stale_mask != 0;
|
||||
}
|
||||
|
||||
|
||||
static void decoder_update(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (!(ctxt->stale_mask & msk)) continue;
|
||||
ctxt->stale_mask &= ~msk;
|
||||
decoder_ops[idx].update(ctxt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl)
|
||||
{
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl);
|
||||
pvr2_v4l2_cmd_stream(ctxt->client,fl);
|
||||
}
|
||||
|
||||
|
||||
static int decoder_detect(struct pvr2_i2c_client *cp)
|
||||
{
|
||||
int ret;
|
||||
/* Attempt to query the decoder - let's see if it will answer */
|
||||
struct v4l2_queryctrl qc;
|
||||
|
||||
memset(&qc,0,sizeof(qc));
|
||||
|
||||
qc.id = V4L2_CID_BRIGHTNESS;
|
||||
|
||||
ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc);
|
||||
return ret == 0; /* Return true if it answered */
|
||||
}
|
||||
|
||||
|
||||
static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
struct v4l2_tuner vt;
|
||||
int ret;
|
||||
|
||||
memset(&vt,0,sizeof(vt));
|
||||
ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
|
||||
if (ret < 0) return -EINVAL;
|
||||
return vt.signal ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt,
|
||||
char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l");
|
||||
}
|
||||
|
||||
|
||||
static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt)
|
||||
{
|
||||
int ret;
|
||||
ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0);
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret);
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions hfuncs = {
|
||||
.detach = (void (*)(void *))decoder_detach,
|
||||
.check = (int (*)(void *))decoder_check,
|
||||
.update = (void (*)(void *))decoder_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
|
||||
struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_v4l_cx2584x *ctxt;
|
||||
|
||||
if (hdw->decoder_ctrl) return 0;
|
||||
if (cp->handler) return 0;
|
||||
if (!decoder_detect(cp)) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->handler.func_data = ctxt;
|
||||
ctxt->handler.func_table = &hfuncs;
|
||||
ctxt->ctrl.ctxt = ctxt;
|
||||
ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
|
||||
ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
|
||||
ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned;
|
||||
ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
ctxt->stale_mask = (1 << (sizeof(decoder_ops)/
|
||||
sizeof(decoder_ops[0]))) - 1;
|
||||
hdw->decoder_ctrl = &ctxt->ctrl;
|
||||
cp->handler = &ctxt->handler;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
53
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
Normal file
53
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_CX2584X_V4L_H
|
||||
#define __PVRUSB2_CX2584X_V4L_H
|
||||
|
||||
/*
|
||||
|
||||
This module connects the pvrusb2 driver to the I2C chip level
|
||||
driver which handles combined device audio & video processing.
|
||||
This interface is used internally by the driver; higher level code
|
||||
should only interact through the interface provided by
|
||||
pvrusb2-hdw.h.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
||||
#endif /* __PVRUSB2_CX2584X_V4L_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: ***
|
||||
*/
|
67
drivers/media/video/pvrusb2/pvrusb2-debug.h
Normal file
67
drivers/media/video/pvrusb2/pvrusb2-debug.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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 pvrusb2_debug;
|
||||
|
||||
#define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_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 (1 << 0) // Normal messages
|
||||
#define PVR2_TRACE_ERROR_LEGS (1 << 1) // error messages
|
||||
#define PVR2_TRACE_TOLERANCE (1 << 2) // track tolerance-affected errors
|
||||
#define PVR2_TRACE_TRAP (1 << 3) // Trap & report misbehavior from app
|
||||
#define PVR2_TRACE_INIT (1 << 4) // misc initialization steps
|
||||
#define PVR2_TRACE_START_STOP (1 << 5) // Streaming start / stop
|
||||
#define PVR2_TRACE_CTL (1 << 6) // commit of control changes
|
||||
#define PVR2_TRACE_DEBUG (1 << 7) // Temporary debug code
|
||||
#define PVR2_TRACE_EEPROM (1 << 8) // eeprom parsing / report
|
||||
#define PVR2_TRACE_STRUCT (1 << 9) // internal struct creation
|
||||
#define PVR2_TRACE_OPEN_CLOSE (1 << 10) // application open / close
|
||||
#define PVR2_TRACE_CREG (1 << 11) // Main critical region entry / exit
|
||||
#define PVR2_TRACE_SYSFS (1 << 12) // Sysfs driven I/O
|
||||
#define PVR2_TRACE_FIRMWARE (1 << 13) // firmware upload actions
|
||||
#define PVR2_TRACE_CHIPS (1 << 14) // chip broadcast operation
|
||||
#define PVR2_TRACE_I2C (1 << 15) // I2C related stuff
|
||||
#define PVR2_TRACE_I2C_CMD (1 << 16) // Software commands to I2C modules
|
||||
#define PVR2_TRACE_I2C_CORE (1 << 17) // I2C core debugging
|
||||
#define PVR2_TRACE_I2C_TRAF (1 << 18) // I2C traffic through the adapter
|
||||
#define PVR2_TRACE_V4LIOCTL (1 << 19) // v4l ioctl details
|
||||
#define PVR2_TRACE_ENCODER (1 << 20) // mpeg2 encoder operation
|
||||
#define PVR2_TRACE_BUF_POOL (1 << 21) // Track buffer pool management
|
||||
#define PVR2_TRACE_BUF_FLOW (1 << 22) // Track buffer flow in system
|
||||
#define PVR2_TRACE_DATA_FLOW (1 << 23) // Track data flow
|
||||
#define PVR2_TRACE_DEBUGIFC (1 << 24) // Debug interface actions
|
||||
#define PVR2_TRACE_GPIO (1 << 25) // 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: ***
|
||||
*/
|
478
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
Normal file
478
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
Normal file
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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"
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
struct debugifc_mask_item {
|
||||
const char *name;
|
||||
unsigned long msk;
|
||||
};
|
||||
|
||||
static struct debugifc_mask_item mask_items[] = {
|
||||
{"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)},
|
||||
{"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)},
|
||||
{"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)},
|
||||
{"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)},
|
||||
{"ENC_RUN",(1<<PVR2_SUBSYS_B_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;
|
||||
|
||||
ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
|
||||
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
|
||||
ccnt = pvr2_i2c_report(hdw,buf,acnt);
|
||||
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_powerup(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);
|
||||
} else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
|
||||
return pvr2_hdw_cmd_decoder_reset(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: ***
|
||||
*/
|
53
drivers/media/video/pvrusb2/pvrusb2-debugifc.h
Normal file
53
drivers/media/video/pvrusb2/pvrusb2-debugifc.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
126
drivers/media/video/pvrusb2/pvrusb2-demod.c
Normal file
126
drivers/media/video/pvrusb2/pvrusb2-demod.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-demod.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/tuner.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
|
||||
struct pvr2_demod_handler {
|
||||
struct pvr2_hdw *hdw;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_i2c_handler i2c_handler;
|
||||
int type_update_fl;
|
||||
};
|
||||
|
||||
|
||||
static void set_config(struct pvr2_demod_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
int cfg = 0;
|
||||
|
||||
switch (hdw->tuner_type) {
|
||||
case TUNER_PHILIPS_FM1216ME_MK3:
|
||||
case TUNER_PHILIPS_FM1236_MK3:
|
||||
cfg = TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c demod set_config(0x%x)",cfg);
|
||||
pvr2_i2c_client_cmd(ctxt->client,TDA9887_SET_CONFIG,&cfg);
|
||||
ctxt->type_update_fl = 0;
|
||||
}
|
||||
|
||||
|
||||
static int demod_check(struct pvr2_demod_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
if (hdw->tuner_updated) ctxt->type_update_fl = !0;
|
||||
return ctxt->type_update_fl != 0;
|
||||
}
|
||||
|
||||
|
||||
static void demod_update(struct pvr2_demod_handler *ctxt)
|
||||
{
|
||||
if (ctxt->type_update_fl) set_config(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static void demod_detach(struct pvr2_demod_handler *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int demod_describe(struct pvr2_demod_handler *ctxt,char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-demod");
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions tuner_funcs = {
|
||||
.detach = (void (*)(void *))demod_detach,
|
||||
.check = (int (*)(void *))demod_check,
|
||||
.update = (void (*)(void *))demod_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))demod_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_demod_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_demod_handler *ctxt;
|
||||
if (cp->handler) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->i2c_handler.func_data = ctxt;
|
||||
ctxt->i2c_handler.func_table = &tuner_funcs;
|
||||
ctxt->type_update_fl = !0;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
cp->handler = &ctxt->i2c_handler;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tda9887 V4L2 handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
38
drivers/media/video/pvrusb2/pvrusb2-demod.h
Normal file
38
drivers/media/video/pvrusb2/pvrusb2-demod.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_DEMOD_H
|
||||
#define __PVRUSB2_DEMOD_H
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_demod_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
#endif /* __PVRUSB2_DEMOD_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: ***
|
||||
*/
|
164
drivers/media/video/pvrusb2/pvrusb2-eeprom.c
Normal file
164
drivers/media/video/pvrusb2/pvrusb2-eeprom.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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"
|
||||
|
||||
#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include <media/tveeprom.h>
|
||||
|
||||
/* We seem to only be interested in the last 128 bytes of the EEPROM */
|
||||
#define EEPROM_SIZE 128
|
||||
|
||||
/* Grab EEPROM contents, needed for direct method. */
|
||||
static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
u8 *eeprom;
|
||||
u8 iadd[2];
|
||||
u8 addr;
|
||||
u16 eepromSize;
|
||||
unsigned int offs;
|
||||
int ret;
|
||||
int mode16 = 0;
|
||||
unsigned pcnt,tcnt;
|
||||
eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
|
||||
if (!eeprom) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"Failed to allocate memory"
|
||||
" required to read eeprom");
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_eeprom("Value for eeprom addr from controller was 0x%x",
|
||||
hdw->eeprom_addr);
|
||||
addr = hdw->eeprom_addr;
|
||||
/* Seems that if the high bit is set, then the *real* eeprom
|
||||
address is shifted right now bit position (noticed this in
|
||||
newer PVR USB2 hardware) */
|
||||
if (addr & 0x80) addr >>= 1;
|
||||
|
||||
/* FX2 documentation states that a 16bit-addressed eeprom is
|
||||
expected if the I2C address is an odd number (yeah, this is
|
||||
strange but it's what they do) */
|
||||
mode16 = (addr & 1);
|
||||
eepromSize = (mode16 ? 4096 : 256);
|
||||
trace_eeprom("Examining %d byte eeprom at location 0x%x"
|
||||
" using %d bit addressing",eepromSize,addr,
|
||||
mode16 ? 16 : 8);
|
||||
|
||||
msg[0].addr = addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = mode16 ? 2 : 1;
|
||||
msg[0].buf = iadd;
|
||||
msg[1].addr = 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 + (eepromSize - EEPROM_SIZE);
|
||||
if (mode16) {
|
||||
iadd[0] = offs >> 8;
|
||||
iadd[1] = offs;
|
||||
} else {
|
||||
iadd[0] = offs;
|
||||
}
|
||||
msg[1].len = pcnt;
|
||||
msg[1].buf = eeprom+tcnt;
|
||||
if ((ret = i2c_transfer(
|
||||
&hdw->i2c_adap,
|
||||
msg,sizeof(msg)/sizeof(msg[0]))) != 2) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"eeprom fetch set offs err=%d",ret);
|
||||
kfree(eeprom);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return eeprom;
|
||||
}
|
||||
|
||||
|
||||
/* Directly call eeprom analysis function within tveeprom. */
|
||||
int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
|
||||
{
|
||||
u8 *eeprom;
|
||||
struct tveeprom tvdata;
|
||||
|
||||
memset(&tvdata,0,sizeof(tvdata));
|
||||
|
||||
eeprom = pvr2_eeprom_fetch(hdw);
|
||||
if (!eeprom) return -EINVAL;
|
||||
|
||||
{
|
||||
struct i2c_client fake_client;
|
||||
/* Newer version expects a useless client interface */
|
||||
fake_client.addr = hdw->eeprom_addr;
|
||||
fake_client.adapter = &hdw->i2c_adap;
|
||||
tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom);
|
||||
}
|
||||
|
||||
trace_eeprom("eeprom assumed v4l tveeprom module");
|
||||
trace_eeprom("eeprom direct call results:");
|
||||
trace_eeprom("has_radio=%d",tvdata.has_radio);
|
||||
trace_eeprom("tuner_type=%d",tvdata.tuner_type);
|
||||
trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats);
|
||||
trace_eeprom("audio_processor=%d",tvdata.audio_processor);
|
||||
trace_eeprom("model=%d",tvdata.model);
|
||||
trace_eeprom("revision=%d",tvdata.revision);
|
||||
trace_eeprom("serial_number=%d",tvdata.serial_number);
|
||||
trace_eeprom("rev_str=%s",tvdata.rev_str);
|
||||
hdw->tuner_type = tvdata.tuner_type;
|
||||
hdw->serial_number = tvdata.serial_number;
|
||||
hdw->std_mask_eeprom = tvdata.tuner_formats;
|
||||
|
||||
kfree(eeprom);
|
||||
|
||||
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: ***
|
||||
*/
|
40
drivers/media/video/pvrusb2/pvrusb2-eeprom.h
Normal file
40
drivers/media/video/pvrusb2/pvrusb2-eeprom.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 *);
|
||||
|
||||
#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: ***
|
||||
*/
|
499
drivers/media/video/pvrusb2/pvrusb2-encoder.c
Normal file
499
drivers/media/video/pvrusb2/pvrusb2-encoder.c
Normal file
|
@ -0,0 +1,499 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/videodev2.h>
|
||||
#include <media/cx2341x.h>
|
||||
#include "pvrusb2-util.h"
|
||||
#include "pvrusb2-encoder.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 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;
|
||||
v4l2_std_id vd_std = hdw->std_mask_cur;
|
||||
int height = hdw->res_ver_val;
|
||||
int width = hdw->res_hor_val;
|
||||
int height_full = !hdw->interlace_val;
|
||||
|
||||
int is_30fps, is_ntsc;
|
||||
|
||||
if (vd_std & V4L2_STD_NTSC) {
|
||||
is_ntsc=1;
|
||||
is_30fps=1;
|
||||
} else if (vd_std & V4L2_STD_PAL_M) {
|
||||
is_ntsc=0;
|
||||
is_30fps=1;
|
||||
} else {
|
||||
is_ntsc=0;
|
||||
is_30fps=0;
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure");
|
||||
|
||||
/* set stream output port. Some notes here: The ivtv-derived
|
||||
encoder documentation says that this command only gets a
|
||||
single argument. However the Windows driver for the model
|
||||
29xxx series hardware has been sending 0x01 as a second
|
||||
argument, while the Windows driver for the model 24xxx
|
||||
series hardware has been sending 0x02 as a second argument.
|
||||
Confusing matters further are the observations that 0x01
|
||||
for that second argument simply won't work on the 24xxx
|
||||
hardware, while 0x02 will work on the 29xxx - except that
|
||||
when we use 0x02 then xawtv breaks due to a loss of
|
||||
synchronization with the mpeg packet headers. While xawtv
|
||||
should be fixed to let it resync better (I did try to
|
||||
contact Gerd about this but he has not answered), it has
|
||||
also been determined that sending 0x00 as this mystery
|
||||
second argument seems to work on both hardware models AND
|
||||
xawtv works again. So we're going to send 0x00. */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2,
|
||||
0x01, 0x00);
|
||||
|
||||
/* set the Program Index Information. We want I,P,B frames (max 400) */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2,
|
||||
0x07, 0x0190);
|
||||
|
||||
/* NOTE : windows driver sends these */
|
||||
/* Mike Isely <isely@pobox.com> 7-Mar-2006 The windows driver
|
||||
sends the following commands but if we do the same then
|
||||
many apps are no longer able to read the video stream.
|
||||
Leaving these out seems to do no harm at all, so they're
|
||||
commented out for that reason. */
|
||||
#ifdef notdef
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0);
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
|
||||
#endif
|
||||
|
||||
/* Strange compared to ivtv data. */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
|
||||
0xf0, 0xf0);
|
||||
|
||||
/* setup firmware to notify us about some events (don't know why...) */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
|
||||
0, 0, 0x10000000, 0xffffffff);
|
||||
|
||||
/* set fps to 25 or 30 (1 or 0)*/
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_RATE, 1,
|
||||
is_30fps ? 0 : 1);
|
||||
|
||||
/* set encoding resolution */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_SIZE, 2,
|
||||
(height_full ? height : (height / 2)),
|
||||
width);
|
||||
/* set encoding aspect ratio to 4:3 */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_ASPECT_RATIO, 1,
|
||||
0x02);
|
||||
|
||||
/* VBI */
|
||||
|
||||
if (hdw->config == pvr2_config_vbi) {
|
||||
int lines = 2 * (is_30fps ? 12 : 18);
|
||||
int size = (4*((lines*1443+3)/4)) / lines;
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_VBI_CONFIG, 7,
|
||||
0xbd05, 1, 4,
|
||||
0x25256262, 0x387f7f7f,
|
||||
lines , size);
|
||||
// 0x25256262, 0x13135454, lines , size);
|
||||
/* select vbi lines */
|
||||
#define line_used(l) (is_30fps ? (l >= 10 && l <= 21) : (l >= 6 && l <= 23))
|
||||
for (i = 2 ; i <= 24 ; i++){
|
||||
ret |= pvr2_write_encoder_vcmd(
|
||||
hdw,CX2341X_ENC_SET_VBI_LINE, 5,
|
||||
i-1,line_used(i), 0, 0, 0);
|
||||
ret |= pvr2_write_encoder_vcmd(
|
||||
hdw,CX2341X_ENC_SET_VBI_LINE, 5,
|
||||
(i-1) | (1 << 31),
|
||||
line_used(i), 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
ret |= pvr2_write_encoder_vcmd(
|
||||
hdw,CX2341X_ENC_SET_VBI_LINE, 5,
|
||||
0xffffffff,0,0,0,0);
|
||||
}
|
||||
|
||||
/* set stream type, depending on resolution. */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_STREAM_TYPE, 1,
|
||||
height_full ? 0x0a : 0x0b);
|
||||
/* set video bitrate */
|
||||
ret |= pvr2_write_encoder_vcmd(
|
||||
hdw, CX2341X_ENC_SET_BIT_RATE, 3,
|
||||
(hdw->vbr_val ? 1 : 0),
|
||||
hdw->videobitrate_val,
|
||||
hdw->videopeak_val / 400);
|
||||
/* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
|
||||
is_30fps ? 0x0f : 0x0c, 0x03);
|
||||
|
||||
/* enable 3:2 pulldown */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_3_2_PULLDOWN,1,0);
|
||||
|
||||
/* set GOP open/close property (open) */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_GOP_CLOSURE,1,0);
|
||||
|
||||
/* set audio stream properties 0x40b9? 0100 0000 1011 1001 */
|
||||
audio = (pvr_tbl_audiobitrate[hdw->audiobitrate_val] |
|
||||
pvr_tbl_srate[hdw->srate_val] |
|
||||
hdw->audiolayer_val << 2 |
|
||||
(hdw->audiocrc_val ? 1 << 14 : 0) |
|
||||
pvr_tbl_emphasis[hdw->audioemphasis_val]);
|
||||
|
||||
ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_AUDIO_PROPERTIES,1,
|
||||
audio);
|
||||
|
||||
/* set dynamic noise reduction filter to manual, Horiz/Vert */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
|
||||
0, 0x03);
|
||||
|
||||
/* dynamic noise reduction filter param */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2
|
||||
, 0, 0);
|
||||
|
||||
/* dynamic noise reduction median filter */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_CORING_LEVELS, 4,
|
||||
0, 0xff, 0, 0xff);
|
||||
|
||||
/* spacial prefiler parameter */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw,
|
||||
CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
|
||||
0x01, 0x01);
|
||||
|
||||
/* initialize video input */
|
||||
ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
|
||||
|
||||
if (!ret) {
|
||||
hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_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,CX2341X_ENC_START_CAPTURE,2,
|
||||
0x01,0x14);
|
||||
} else if (hdw->config == pvr2_config_mpeg) {
|
||||
status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
|
||||
0,0x13);
|
||||
} else {
|
||||
status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
|
||||
0,0x13);
|
||||
}
|
||||
if (!status) {
|
||||
hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_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,CX2341X_ENC_STOP_CAPTURE,3,
|
||||
0x01,0x01,0x14);
|
||||
} else if (hdw->config == pvr2_config_mpeg) {
|
||||
status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
|
||||
0x01,0,0x13);
|
||||
} else {
|
||||
status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_STOP_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 &= ~(1<<PVR2_SUBSYS_B_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: ***
|
||||
*/
|
42
drivers/media/video/pvrusb2/pvrusb2-encoder.h
Normal file
42
drivers/media/video/pvrusb2/pvrusb2-encoder.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
371
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
Normal file
371
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
Normal file
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/config.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "pvrusb2-hdw.h"
|
||||
#include "pvrusb2-io.h"
|
||||
|
||||
/* Legal values for the SRATE state variable */
|
||||
#define PVR2_CVAL_SRATE_48 0
|
||||
#define PVR2_CVAL_SRATE_44_1 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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_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 { mutex_lock(&x##_mutex); x##_held = !0; } while (0)
|
||||
#define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0)
|
||||
|
||||
struct pvr2_decoder;
|
||||
|
||||
typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *);
|
||||
typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *);
|
||||
typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *);
|
||||
typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val);
|
||||
typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val,
|
||||
char *,unsigned int,unsigned int *);
|
||||
typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *,
|
||||
const char *,unsigned int,
|
||||
int *mskp,int *valp);
|
||||
|
||||
/* This structure describes a specific control. A table of these is set up
|
||||
in pvrusb2-hdw.c. */
|
||||
struct pvr2_ctl_info {
|
||||
/* Control's name suitable for use as an identifier */
|
||||
const char *name;
|
||||
|
||||
/* Short description of control */
|
||||
const char *desc;
|
||||
|
||||
/* Control's implementation */
|
||||
pvr2_ctlf_get_value get_value; /* Get its value */
|
||||
pvr2_ctlf_set_value set_value; /* Set its value */
|
||||
pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */
|
||||
pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */
|
||||
pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */
|
||||
pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */
|
||||
|
||||
/* Control's type (int, enum, bitmask) */
|
||||
enum pvr2_ctl_type type;
|
||||
|
||||
/* Associated V4L control ID, if any */
|
||||
int v4l_id;
|
||||
|
||||
/* Associated driver internal ID, if any */
|
||||
int internal_id;
|
||||
|
||||
/* Don't implicitly initialize this control's value */
|
||||
int skip_init;
|
||||
|
||||
/* Starting value for this control */
|
||||
int default_value;
|
||||
|
||||
/* Type-specific control information */
|
||||
union {
|
||||
struct { /* Integer control */
|
||||
long min_value; /* lower limit */
|
||||
long max_value; /* upper limit */
|
||||
} type_int;
|
||||
struct { /* enumerated control */
|
||||
unsigned int count; /* enum value count */
|
||||
const char **value_names; /* symbol names */
|
||||
} type_enum;
|
||||
struct { /* bitmask control */
|
||||
unsigned int valid_bits; /* bits in use */
|
||||
const char **bit_names; /* symbol name/bit */
|
||||
} type_bitmask;
|
||||
} def;
|
||||
};
|
||||
|
||||
|
||||
struct pvr2_ctrl {
|
||||
const struct pvr2_ctl_info *info;
|
||||
struct pvr2_hdw *hdw;
|
||||
};
|
||||
|
||||
|
||||
struct pvr2_audio_stat {
|
||||
void *ctxt;
|
||||
void (*detach)(void *);
|
||||
int (*status)(void *);
|
||||
};
|
||||
|
||||
struct pvr2_decoder_ctrl {
|
||||
void *ctxt;
|
||||
void (*detach)(void *);
|
||||
void (*enable)(void *,int);
|
||||
int (*tuned)(void *);
|
||||
void (*force_reset)(void *);
|
||||
};
|
||||
|
||||
#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */
|
||||
#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */
|
||||
#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */
|
||||
#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */
|
||||
|
||||
#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\
|
||||
PVR2_I2C_PEND_CLIENT |\
|
||||
PVR2_I2C_PEND_REFRESH |\
|
||||
PVR2_I2C_PEND_STALE)
|
||||
|
||||
/* Disposition of firmware1 loading situation */
|
||||
#define FW1_STATE_UNKNOWN 0
|
||||
#define FW1_STATE_MISSING 1
|
||||
#define FW1_STATE_FAILED 2
|
||||
#define FW1_STATE_RELOAD 3
|
||||
#define FW1_STATE_OK 4
|
||||
|
||||
/* Known major hardware variants, keyed from device ID */
|
||||
#define PVR2_HDW_TYPE_29XXX 0
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
|
||||
#define PVR2_HDW_TYPE_24XXX 1
|
||||
#endif
|
||||
|
||||
typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
|
||||
#define PVR2_I2C_FUNC_CNT 128
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Device type, one of PVR2_HDW_TYPE_xxxxx */
|
||||
unsigned int hdw_type;
|
||||
|
||||
/* Video spigot */
|
||||
struct pvr2_stream *vid_stream;
|
||||
|
||||
/* Mutex for all hardware state control */
|
||||
struct mutex big_lock_mutex;
|
||||
int big_lock_held; /* For debugging */
|
||||
|
||||
void (*poll_trigger_func)(void *);
|
||||
void *poll_trigger_data;
|
||||
|
||||
char name[32];
|
||||
|
||||
/* I2C stuff */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_algorithm i2c_algo;
|
||||
pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT];
|
||||
int i2c_cx25840_hack_state;
|
||||
int i2c_linked;
|
||||
unsigned int i2c_pend_types; /* Which types of update are needed */
|
||||
unsigned long i2c_pend_mask; /* Change bits we need to scan */
|
||||
unsigned long i2c_stale_mask; /* Pending broadcast change bits */
|
||||
unsigned long i2c_active_mask; /* All change bits currently in use */
|
||||
struct list_head i2c_clients;
|
||||
struct mutex i2c_list_lock;
|
||||
|
||||
/* Frequency table */
|
||||
unsigned int freqTable[FREQTABLE_SIZE];
|
||||
unsigned int freqProgSlot;
|
||||
unsigned int freqSlot;
|
||||
|
||||
/* Stuff for handling low level control interaction with device */
|
||||
struct mutex ctl_lock_mutex;
|
||||
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
|
||||
int fw1_state; // current situation with fw1
|
||||
|
||||
int flag_decoder_is_tuned;
|
||||
|
||||
struct pvr2_decoder_ctrl *decoder_ctrl;
|
||||
|
||||
// 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;
|
||||
|
||||
// True if there is a request to trigger logging of state in each
|
||||
// module.
|
||||
int log_requested;
|
||||
|
||||
/* Tuner / frequency control stuff */
|
||||
unsigned int tuner_type;
|
||||
int tuner_updated;
|
||||
unsigned int freqVal;
|
||||
int freqDirty;
|
||||
|
||||
/* Video standard handling */
|
||||
v4l2_std_id std_mask_eeprom; // Hardware supported selections
|
||||
v4l2_std_id std_mask_avail; // Which standards we may select from
|
||||
v4l2_std_id std_mask_cur; // Currently selected standard(s)
|
||||
unsigned int std_enum_cnt; // # of enumerated standards
|
||||
int std_enum_cur; // selected standard enumeration value
|
||||
int std_dirty; // True if std_mask_cur has changed
|
||||
struct pvr2_ctl_info std_info_enum;
|
||||
struct pvr2_ctl_info std_info_avail;
|
||||
struct pvr2_ctl_info std_info_cur;
|
||||
struct v4l2_standard *std_defs;
|
||||
const char **std_enum_names;
|
||||
|
||||
// Generated string names, one per actual V4L2 standard
|
||||
const char *std_mask_ptrs[32];
|
||||
char std_mask_names[32][10];
|
||||
|
||||
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;
|
||||
|
||||
/* Location of eeprom or a negative number if none */
|
||||
int eeprom_addr;
|
||||
|
||||
enum pvr2_config config;
|
||||
|
||||
/* Information about what audio signal we're hearing */
|
||||
int flag_stereo;
|
||||
int flag_bilingual;
|
||||
struct pvr2_audio_stat *audio_stat;
|
||||
|
||||
/* Control state */
|
||||
#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty
|
||||
VCREATE_DATA(brightness);
|
||||
VCREATE_DATA(contrast);
|
||||
VCREATE_DATA(saturation);
|
||||
VCREATE_DATA(hue);
|
||||
VCREATE_DATA(volume);
|
||||
VCREATE_DATA(balance);
|
||||
VCREATE_DATA(bass);
|
||||
VCREATE_DATA(treble);
|
||||
VCREATE_DATA(mute);
|
||||
VCREATE_DATA(srate);
|
||||
VCREATE_DATA(audiobitrate);
|
||||
VCREATE_DATA(audiocrc);
|
||||
VCREATE_DATA(audioemphasis);
|
||||
VCREATE_DATA(vbr);
|
||||
VCREATE_DATA(videobitrate);
|
||||
VCREATE_DATA(videopeak);
|
||||
VCREATE_DATA(input);
|
||||
VCREATE_DATA(audiomode);
|
||||
VCREATE_DATA(res_hor);
|
||||
VCREATE_DATA(res_ver);
|
||||
VCREATE_DATA(interlace);
|
||||
VCREATE_DATA(audiolayer);
|
||||
#undef VCREATE_DATA
|
||||
|
||||
struct pvr2_ctrl *controls;
|
||||
};
|
||||
|
||||
int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
|
||||
|
||||
unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *);
|
||||
|
||||
void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
|
||||
unsigned long msk,unsigned long val);
|
||||
void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
|
||||
unsigned long msk,
|
||||
unsigned long val);
|
||||
|
||||
void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
|
||||
void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
|
||||
|
||||
int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr,
|
||||
u8 *wdata,u16 wlen,
|
||||
u8 *rdata,u16 rlen);
|
||||
|
||||
#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: ***
|
||||
*/
|
2949
drivers/media/video/pvrusb2/pvrusb2-hdw.c
Normal file
2949
drivers/media/video/pvrusb2/pvrusb2-hdw.c
Normal file
File diff suppressed because it is too large
Load Diff
342
drivers/media/video/pvrusb2/pvrusb2-hdw.h
Normal file
342
drivers/media/video/pvrusb2/pvrusb2-hdw.h
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/videodev2.h>
|
||||
#include "pvrusb2-io.h"
|
||||
#include "pvrusb2-ctrl.h"
|
||||
|
||||
/* Private V4L2-compatible controls available in this driver, look these up
|
||||
with pvr2_hdw_get_ctrl_v4l(). */
|
||||
#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)
|
||||
|
||||
/* Private internal control ids, look these up with
|
||||
pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */
|
||||
#define PVR2_CID_STDENUM 1
|
||||
#define PVR2_CID_STDCUR 2
|
||||
#define PVR2_CID_STDAVAIL 3
|
||||
#define PVR2_CID_INPUT 4
|
||||
#define PVR2_CID_AUDIOMODE 5
|
||||
#define PVR2_CID_FREQUENCY 6
|
||||
#define PVR2_CID_HRES 7
|
||||
#define PVR2_CID_VRES 8
|
||||
#define PVR2_CID_INTERLACE 9
|
||||
|
||||
/* 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
|
||||
|
||||
/* Values that pvr2_hdw_get_signal_status() returns */
|
||||
#define PVR2_SIGNAL_OK 0x0001
|
||||
#define PVR2_SIGNAL_STEREO 0x0002
|
||||
#define PVR2_SIGNAL_SAP 0x0004
|
||||
|
||||
|
||||
/* 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_B_ENC_FIRMWARE 0
|
||||
#define PVR2_SUBSYS_B_ENC_CFG 1
|
||||
#define PVR2_SUBSYS_B_DIGITIZER_RUN 2
|
||||
#define PVR2_SUBSYS_B_USBSTREAM_RUN 3
|
||||
#define PVR2_SUBSYS_B_ENC_RUN 4
|
||||
|
||||
#define PVR2_SUBSYS_CFG_ALL ( \
|
||||
(1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \
|
||||
(1 << PVR2_SUBSYS_B_ENC_CFG) )
|
||||
#define PVR2_SUBSYS_RUN_ALL ( \
|
||||
(1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \
|
||||
(1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \
|
||||
(1 << PVR2_SUBSYS_B_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,
|
||||
const struct usb_device_id *devid);
|
||||
|
||||
/* Poll for background activity (if any) */
|
||||
void pvr2_hdw_poll(struct pvr2_hdw *);
|
||||
|
||||
/* Trigger a poll to take place later at a convenient time */
|
||||
void pvr2_hdw_poll_trigger(struct pvr2_hdw *);
|
||||
void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *);
|
||||
|
||||
/* Register a callback used to trigger a future poll */
|
||||
void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *,
|
||||
void (*func)(void *),
|
||||
void *data);
|
||||
|
||||
/* 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 *);
|
||||
|
||||
/* Get the number of defined controls */
|
||||
unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *);
|
||||
|
||||
/* Retrieve a control handle given its index (0..count-1) */
|
||||
struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int);
|
||||
|
||||
/* Retrieve a control handle given its internal ID (if any) */
|
||||
struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int);
|
||||
|
||||
/* Retrieve a control handle given its V4L ID (if any) */
|
||||
struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id);
|
||||
|
||||
/* Commit all control changes made up to this point */
|
||||
int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
|
||||
|
||||
/* Return name for this driver instance */
|
||||
const char *pvr2_hdw_get_driver_name(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 *);
|
||||
|
||||
/* Emit a video standard struct */
|
||||
int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
|
||||
unsigned int idx);
|
||||
|
||||
/* 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 *write_ptr,unsigned int write_len,
|
||||
void *read_ptr,unsigned int read_len);
|
||||
|
||||
/* Issue a command and get a response from the device. This extended
|
||||
version includes a probe flag (which if set means that device errors
|
||||
should not be logged or treated as fatal) and a timeout in jiffies.
|
||||
This can be used to non-lethally probe the health of endpoint 1. */
|
||||
int pvr2_send_request_ex(struct pvr2_hdw *,unsigned int timeout,int probe_fl,
|
||||
void *write_ptr,unsigned int write_len,
|
||||
void *read_ptr,unsigned int read_len);
|
||||
|
||||
/* 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_powerup(struct pvr2_hdw *);
|
||||
|
||||
/* Order decoder to reset */
|
||||
int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *);
|
||||
|
||||
/* Stop / start video stream transport */
|
||||
int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
|
||||
|
||||
/* Find I2C address of eeprom */
|
||||
int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *);
|
||||
|
||||
/* 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 modules to log their state once */
|
||||
void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* List of device types that we can match */
|
||||
extern struct usb_device_id pvr2_device_table[];
|
||||
|
||||
#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: ***
|
||||
*/
|
115
drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
Normal file
115
drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-i2c-core.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include "pvrusb2-i2c-cmd-v4l2.h"
|
||||
#include "pvrusb2-audio.h"
|
||||
#include "pvrusb2-tuner.h"
|
||||
#include "pvrusb2-demod.h"
|
||||
#include "pvrusb2-video-v4l.h"
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
|
||||
#include "pvrusb2-cx2584x-v4l.h"
|
||||
#include "pvrusb2-wm8775.h"
|
||||
#endif
|
||||
|
||||
#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
|
||||
|
||||
#define OP_STANDARD 0
|
||||
#define OP_BCSH 1
|
||||
#define OP_VOLUME 2
|
||||
#define OP_FREQ 3
|
||||
#define OP_AUDIORATE 4
|
||||
#define OP_SIZE 5
|
||||
#define OP_LOG 6
|
||||
|
||||
static const struct pvr2_i2c_op * const ops[] = {
|
||||
[OP_STANDARD] = &pvr2_i2c_op_v4l2_standard,
|
||||
[OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh,
|
||||
[OP_VOLUME] = &pvr2_i2c_op_v4l2_volume,
|
||||
[OP_FREQ] = &pvr2_i2c_op_v4l2_frequency,
|
||||
[OP_SIZE] = &pvr2_i2c_op_v4l2_size,
|
||||
[OP_LOG] = &pvr2_i2c_op_v4l2_log,
|
||||
};
|
||||
|
||||
void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
|
||||
{
|
||||
int id;
|
||||
id = cp->client->driver->id;
|
||||
cp->ctl_mask = ((1 << OP_STANDARD) |
|
||||
(1 << OP_BCSH) |
|
||||
(1 << OP_VOLUME) |
|
||||
(1 << OP_FREQ) |
|
||||
(1 << OP_SIZE) |
|
||||
(1 << OP_LOG));
|
||||
|
||||
if (id == I2C_DRIVERID_MSP3400) {
|
||||
if (pvr2_i2c_msp3400_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (id == I2C_DRIVERID_TUNER) {
|
||||
if (pvr2_i2c_tuner_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
|
||||
if (id == I2C_DRIVERID_CX25840) {
|
||||
if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (id == I2C_DRIVERID_WM8775) {
|
||||
if (pvr2_i2c_wm8775_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (id == I2C_DRIVERID_SAA711X) {
|
||||
if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (id == I2C_DRIVERID_TDA9887) {
|
||||
if (pvr2_i2c_demod_setup(hdw,cp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx)
|
||||
{
|
||||
if (idx >= sizeof(ops)/sizeof(ops[0])) return 0;
|
||||
return ops[idx];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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: ***
|
||||
*/
|
232
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
Normal file
232
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-cmd-v4l2.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
|
||||
static void set_standard(struct pvr2_hdw *hdw)
|
||||
{
|
||||
v4l2_std_id vs;
|
||||
vs = hdw->std_mask_cur;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,
|
||||
"i2c v4l2 set_standard(0x%llx)",(__u64)vs);
|
||||
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
|
||||
}
|
||||
|
||||
|
||||
static int check_standard(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return hdw->std_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = {
|
||||
.check = check_standard,
|
||||
.update = set_standard,
|
||||
.name = "v4l2_standard",
|
||||
};
|
||||
|
||||
|
||||
static void set_bcsh(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct v4l2_control ctrl;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh"
|
||||
" b=%d c=%d s=%d h=%d",
|
||||
hdw->brightness_val,hdw->contrast_val,
|
||||
hdw->saturation_val,hdw->hue_val);
|
||||
memset(&ctrl,0,sizeof(ctrl));
|
||||
ctrl.id = V4L2_CID_BRIGHTNESS;
|
||||
ctrl.value = hdw->brightness_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_CONTRAST;
|
||||
ctrl.value = hdw->contrast_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_SATURATION;
|
||||
ctrl.value = hdw->saturation_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_HUE;
|
||||
ctrl.value = hdw->hue_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
}
|
||||
|
||||
|
||||
static int check_bcsh(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return (hdw->brightness_dirty ||
|
||||
hdw->contrast_dirty ||
|
||||
hdw->saturation_dirty ||
|
||||
hdw->hue_dirty);
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = {
|
||||
.check = check_bcsh,
|
||||
.update = set_bcsh,
|
||||
.name = "v4l2_bcsh",
|
||||
};
|
||||
|
||||
|
||||
static void set_volume(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct v4l2_control ctrl;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,
|
||||
"i2c v4l2 set_volume"
|
||||
"(vol=%d bal=%d bas=%d treb=%d mute=%d)",
|
||||
hdw->volume_val,
|
||||
hdw->balance_val,
|
||||
hdw->bass_val,
|
||||
hdw->treble_val,
|
||||
hdw->mute_val);
|
||||
memset(&ctrl,0,sizeof(ctrl));
|
||||
ctrl.id = V4L2_CID_AUDIO_MUTE;
|
||||
ctrl.value = hdw->mute_val ? 1 : 0;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_AUDIO_VOLUME;
|
||||
ctrl.value = hdw->volume_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_AUDIO_BALANCE;
|
||||
ctrl.value = hdw->balance_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_AUDIO_BASS;
|
||||
ctrl.value = hdw->bass_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
ctrl.id = V4L2_CID_AUDIO_TREBLE;
|
||||
ctrl.value = hdw->treble_val;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
|
||||
}
|
||||
|
||||
|
||||
static int check_volume(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return (hdw->volume_dirty ||
|
||||
hdw->balance_dirty ||
|
||||
hdw->bass_dirty ||
|
||||
hdw->treble_dirty ||
|
||||
hdw->mute_dirty);
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = {
|
||||
.check = check_volume,
|
||||
.update = set_volume,
|
||||
.name = "v4l2_volume",
|
||||
};
|
||||
|
||||
|
||||
static void set_frequency(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long fv;
|
||||
struct v4l2_frequency freq;
|
||||
fv = hdw->freqVal;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv);
|
||||
memset(&freq,0,sizeof(freq));
|
||||
freq.frequency = fv / 62500;
|
||||
freq.tuner = 0;
|
||||
freq.type = V4L2_TUNER_ANALOG_TV;
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq);
|
||||
}
|
||||
|
||||
|
||||
static int check_frequency(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return hdw->freqDirty != 0;
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = {
|
||||
.check = check_frequency,
|
||||
.update = set_frequency,
|
||||
.name = "v4l2_freq",
|
||||
};
|
||||
|
||||
|
||||
static void set_size(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct v4l2_format fmt;
|
||||
|
||||
memset(&fmt,0,sizeof(fmt));
|
||||
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = hdw->res_hor_val;
|
||||
fmt.fmt.pix.height = hdw->res_ver_val;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)",
|
||||
fmt.fmt.pix.width,fmt.fmt.pix.height);
|
||||
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt);
|
||||
}
|
||||
|
||||
|
||||
static int check_size(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return (hdw->res_hor_dirty || hdw->res_ver_dirty);
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = {
|
||||
.check = check_size,
|
||||
.update = set_size,
|
||||
.name = "v4l2_size",
|
||||
};
|
||||
|
||||
|
||||
static void do_log(struct pvr2_hdw *hdw)
|
||||
{
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()");
|
||||
pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int check_log(struct pvr2_hdw *hdw)
|
||||
{
|
||||
return hdw->log_requested != 0;
|
||||
}
|
||||
|
||||
|
||||
const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = {
|
||||
.check = check_log,
|
||||
.update = do_log,
|
||||
.name = "v4l2_log",
|
||||
};
|
||||
|
||||
|
||||
void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl)
|
||||
{
|
||||
pvr2_i2c_client_cmd(cp,
|
||||
(fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),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: ***
|
||||
*/
|
47
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
Normal file
47
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_CMD_V4L2_H
|
||||
#define __PVRUSB2_CMD_V4L2_H
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log;
|
||||
|
||||
void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int);
|
||||
|
||||
#endif /* __PVRUSB2_CMD_V4L2_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: ***
|
||||
*/
|
937
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
Normal file
937
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
Normal file
|
@ -0,0 +1,937 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-i2c-core.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
|
||||
#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
|
||||
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
if (!data) length = 0;
|
||||
if (length > (sizeof(hdw->cmd_buffer) - 3)) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"Killing an I2C write to %u that is too large"
|
||||
" (desired=%u limit=%u)",
|
||||
i2c_addr,
|
||||
length,(unsigned int)(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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
if (!data) dlen = 0;
|
||||
if (dlen > (sizeof(hdw->cmd_buffer) - 4)) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"Killing an I2C read to %u that has wlen too large"
|
||||
" (desired=%u limit=%u)",
|
||||
i2c_addr,
|
||||
dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4));
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"Killing an I2C read to %u that has rlen too large"
|
||||
" (desired=%u limit=%u)",
|
||||
i2c_addr,
|
||||
rlen,(unsigned int)(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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 the common low level entry point for doing I2C operations to the
|
||||
hardware. */
|
||||
int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
|
||||
u8 i2c_addr,
|
||||
u8 *wdata,
|
||||
u16 wlen,
|
||||
u8 *rdata,
|
||||
u16 rlen)
|
||||
{
|
||||
if (!rdata) rlen = 0;
|
||||
if (!wdata) wlen = 0;
|
||||
if (rlen || !wlen) {
|
||||
return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen);
|
||||
} else {
|
||||
return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
|
||||
|
||||
/* This is a special entry point that is entered if an I2C operation is
|
||||
attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this
|
||||
part doesn't work, but we know it is really there. So let's look for
|
||||
the autodetect attempt and just return success if we see that. */
|
||||
static int i2c_hack_wm8775(struct pvr2_hdw *hdw,
|
||||
u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
|
||||
{
|
||||
if (!(rlen || wlen)) {
|
||||
// This is a probe attempt. Just let it succeed.
|
||||
return 0;
|
||||
}
|
||||
return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
|
||||
}
|
||||
|
||||
/* This is a special entry point that is entered if an I2C operation is
|
||||
attempted to a cx25840 chip on model 24xxx hardware. This chip can
|
||||
sometimes wedge itself. Worse still, when this happens msp3400 can
|
||||
falsely detect this part and then the system gets hosed up after msp3400
|
||||
gets confused and dies. What we want to do here is try to keep msp3400
|
||||
away and also try to notice if the chip is wedged and send a warning to
|
||||
the system log. */
|
||||
static int i2c_hack_cx25840(struct pvr2_hdw *hdw,
|
||||
u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
|
||||
{
|
||||
int ret;
|
||||
unsigned int subaddr;
|
||||
u8 wbuf[2];
|
||||
int state = hdw->i2c_cx25840_hack_state;
|
||||
|
||||
if (!(rlen || wlen)) {
|
||||
// Probe attempt - always just succeed and don't bother the
|
||||
// hardware (this helps to make the state machine further
|
||||
// down somewhat easier).
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == 3) {
|
||||
return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
|
||||
}
|
||||
|
||||
/* We're looking for the exact pattern where the revision register
|
||||
is being read. The cx25840 module will always look at the
|
||||
revision register first. Any other pattern of access therefore
|
||||
has to be a probe attempt from somebody else so we'll reject it.
|
||||
Normally we could just let each client just probe the part
|
||||
anyway, but when the cx25840 is wedged, msp3400 will get a false
|
||||
positive and that just screws things up... */
|
||||
|
||||
if (wlen == 0) {
|
||||
switch (state) {
|
||||
case 1: subaddr = 0x0100; break;
|
||||
case 2: subaddr = 0x0101; break;
|
||||
default: goto fail;
|
||||
}
|
||||
} else if (wlen == 2) {
|
||||
subaddr = (wdata[0] << 8) | wdata[1];
|
||||
switch (subaddr) {
|
||||
case 0x0100: state = 1; break;
|
||||
case 0x0101: state = 2; break;
|
||||
default: goto fail;
|
||||
}
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
if (!rlen) goto success;
|
||||
state = 0;
|
||||
if (rlen != 1) goto fail;
|
||||
|
||||
/* If we get to here then we have a legitimate read for one of the
|
||||
two revision bytes, so pass it through. */
|
||||
wbuf[0] = subaddr >> 8;
|
||||
wbuf[1] = subaddr;
|
||||
ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen);
|
||||
|
||||
if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"WARNING: Detected a wedged cx25840 chip;"
|
||||
" the device will not work.");
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"WARNING: Try power cycling the pvrusb2 device.");
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"WARNING: Disabling further access to the device"
|
||||
" to prevent other foul-ups.");
|
||||
// This blocks all further communication with the part.
|
||||
hdw->i2c_func[0x44] = 0;
|
||||
pvr2_hdw_render_useless(hdw);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK.");
|
||||
state = 3;
|
||||
|
||||
success:
|
||||
hdw->i2c_cx25840_hack_state = state;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
hdw->i2c_cx25840_hack_state = state;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_24XXX */
|
||||
|
||||
/* 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;
|
||||
pvr2_i2c_func funcp = 0;
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
|
||||
|
||||
if (!num) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if ((msgs[0].flags & I2C_M_NOSTART)) {
|
||||
trace_i2c("i2c refusing I2C_M_NOSTART");
|
||||
goto done;
|
||||
}
|
||||
if (msgs[0].addr < PVR2_I2C_FUNC_CNT) {
|
||||
funcp = hdw->i2c_func[msgs[0].addr];
|
||||
}
|
||||
if (!funcp) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (num == 1) {
|
||||
if (msgs[0].flags & I2C_M_RD) {
|
||||
/* Simple read */
|
||||
u16 tcnt,bcnt,offs;
|
||||
if (!msgs[0].len) {
|
||||
/* Length == 0 read. This is a probe. */
|
||||
if (funcp(hdw,msgs[0].addr,0,0,0,0)) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
ret = 1;
|
||||
goto done;
|
||||
}
|
||||
/* 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 (funcp(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 (funcp(hdw,msgs[0].addr,
|
||||
msgs[0].buf,msgs[0].len,0,0)) {
|
||||
ret = -EIO;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
} else if (num == 2) {
|
||||
if (msgs[0].addr != msgs[1].addr) {
|
||||
trace_i2c("i2c refusing 2 phase transfer with"
|
||||
" conflicting target addresses");
|
||||
ret = -ENOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
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 (funcp(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:
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) {
|
||||
unsigned int idx,offs,cnt;
|
||||
for (idx = 0; idx < num; idx++) {
|
||||
cnt = msgs[idx].len;
|
||||
printk(KERN_INFO
|
||||
"pvrusb2 i2c xfer %u/%u:"
|
||||
" addr=0x%x len=%d %s%s",
|
||||
idx+1,num,
|
||||
msgs[idx].addr,
|
||||
cnt,
|
||||
(msgs[idx].flags & I2C_M_RD ?
|
||||
"read" : "write"),
|
||||
(msgs[idx].flags & I2C_M_NOSTART ?
|
||||
" nostart" : ""));
|
||||
if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {
|
||||
if (cnt > 8) cnt = 8;
|
||||
printk(" [");
|
||||
for (offs = 0; offs < (cnt>8?8:cnt); offs++) {
|
||||
if (offs) printk(" ");
|
||||
printk("%02x",msgs[idx].buf[offs]);
|
||||
}
|
||||
if (offs < cnt) printk(" ...");
|
||||
printk("]");
|
||||
}
|
||||
if (idx+1 == num) {
|
||||
printk(" result=%d",ret);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
if (!num) {
|
||||
printk(KERN_INFO
|
||||
"pvrusb2 i2c xfer null transfer result=%d\n",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
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 | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_core_singleton(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->driver.owner)) return -EAGAIN;
|
||||
stat = cp->driver->command(cp,cmd,arg);
|
||||
module_put(cp->driver->driver.owner);
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
|
||||
{
|
||||
int stat;
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND (code=%u 0x%x) to %.*s",
|
||||
cmd,cmd,cnt,buf);
|
||||
}
|
||||
stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
|
||||
{
|
||||
struct list_head *item,*nc;
|
||||
struct pvr2_i2c_client *cp;
|
||||
int stat = -EINVAL;
|
||||
|
||||
if (!hdw) return stat;
|
||||
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
list_for_each_safe(item,nc,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,list);
|
||||
if (!cp->recv_enable) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
stat = pvr2_i2c_client_cmd(cp,cmd,arg);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
static int handler_check(struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_i2c_handler *hp = cp->handler;
|
||||
if (!hp) return 0;
|
||||
if (!hp->func_table->check) return 0;
|
||||
return hp->func_table->check(hp->func_data) != 0;
|
||||
}
|
||||
|
||||
#define BUFSIZE 500
|
||||
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
struct list_head *item,*nc;
|
||||
struct pvr2_i2c_client *cp;
|
||||
|
||||
if (!hdw->i2c_linked) return;
|
||||
if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
|
||||
return;
|
||||
}
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
|
||||
/* One or more I2C clients have attached since we
|
||||
last synced. So scan the list and identify the
|
||||
new clients. */
|
||||
char *buf;
|
||||
unsigned int cnt;
|
||||
unsigned long amask = 0;
|
||||
buf = kmalloc(BUFSIZE,GFP_KERNEL);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
|
||||
list_for_each(item,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,
|
||||
list);
|
||||
if (!cp->detected_flag) {
|
||||
cp->ctl_mask = 0;
|
||||
pvr2_i2c_probe(hdw,cp);
|
||||
cp->detected_flag = !0;
|
||||
msk = cp->ctl_mask;
|
||||
cnt = 0;
|
||||
if (buf) {
|
||||
cnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
PVR2_I2C_DETAIL_ALL,
|
||||
buf,BUFSIZE);
|
||||
}
|
||||
trace_i2c("Probed: %.*s",cnt,buf);
|
||||
if (handler_check(cp)) {
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_CLIENT;
|
||||
}
|
||||
cp->pend_mask = msk;
|
||||
hdw->i2c_pend_mask |= msk;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
if (buf) kfree(buf);
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
|
||||
/* Need to do one or more global updates. Arrange
|
||||
for this to happen. */
|
||||
unsigned long m2;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: PEND_STALE (0x%lx)",
|
||||
hdw->i2c_stale_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
|
||||
list_for_each(item,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,
|
||||
list);
|
||||
m2 = hdw->i2c_stale_mask;
|
||||
m2 &= cp->ctl_mask;
|
||||
m2 &= ~cp->pend_mask;
|
||||
if (m2) {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p setting 0x%lx",
|
||||
cp,m2);
|
||||
cp->pend_mask |= m2;
|
||||
}
|
||||
}
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
|
||||
/* One or more client handlers are asking for an
|
||||
update. Run through the list of known clients
|
||||
and update each one. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
|
||||
list_for_each_safe(item,nc,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,
|
||||
list);
|
||||
if (!cp->handler) continue;
|
||||
if (!cp->handler->func_table->update) continue;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p update",cp);
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
cp->handler->func_table->update(
|
||||
cp->handler->func_data);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
/* If client's update function set some
|
||||
additional pending bits, account for that
|
||||
here. */
|
||||
if (cp->pend_mask & ~hdw->i2c_pend_mask) {
|
||||
hdw->i2c_pend_mask |= cp->pend_mask;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
|
||||
const struct pvr2_i2c_op *opf;
|
||||
unsigned long pm;
|
||||
/* Some actual updates are pending. Walk through
|
||||
each update type and perform it. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
|
||||
" (0x%lx)",hdw->i2c_pend_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
|
||||
pm = hdw->i2c_pend_mask;
|
||||
hdw->i2c_pend_mask = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(pm & msk)) continue;
|
||||
pm &= ~msk;
|
||||
list_for_each(item,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,
|
||||
struct pvr2_i2c_client,
|
||||
list);
|
||||
if (cp->pend_mask & msk) {
|
||||
cp->pend_mask &= ~msk;
|
||||
cp->recv_enable = !0;
|
||||
} else {
|
||||
cp->recv_enable = 0;
|
||||
}
|
||||
}
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!opf) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
opf->update(hdw);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
}
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk,sm,pm;
|
||||
unsigned int idx;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
struct list_head *item;
|
||||
struct pvr2_i2c_client *cp;
|
||||
unsigned int pt = 0;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
|
||||
|
||||
pm = hdw->i2c_active_mask;
|
||||
sm = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(msk & pm)) continue;
|
||||
pm &= ~msk;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!opf) continue;
|
||||
if (opf->check(hdw)) {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) pt |= PVR2_I2C_PEND_STALE;
|
||||
|
||||
list_for_each(item,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,list);
|
||||
if (!handler_check(cp)) continue;
|
||||
pt |= PVR2_I2C_PEND_CLIENT;
|
||||
}
|
||||
|
||||
if (pt) {
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->i2c_pend_types |= pt;
|
||||
hdw->i2c_stale_mask |= sm;
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: types=0x%x stale=0x%lx pend=0x%lx",
|
||||
hdw->i2c_pend_types,
|
||||
hdw->i2c_stale_mask,
|
||||
hdw->i2c_pend_mask);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
|
||||
|
||||
return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
|
||||
}
|
||||
|
||||
unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
|
||||
unsigned int detail,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
int spcfl = 0;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
|
||||
ccnt = 0;
|
||||
if (detail & PVR2_I2C_DETAIL_DEBUG) {
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"ctxt=%p ctl_mask=0x%lx",
|
||||
cp,cp->ctl_mask);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"%s%s @ 0x%x",
|
||||
(spcfl ? " " : ""),
|
||||
cp->client->name,
|
||||
cp->client->addr);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
|
||||
cp->handler && cp->handler->func_table->describe) {
|
||||
bcnt = scnprintf(buf,maxlen," (");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = cp->handler->func_table->describe(
|
||||
cp->handler->func_data,buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,")");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
|
||||
unsigned int idx;
|
||||
unsigned long msk,sm;
|
||||
int spcfl;
|
||||
bcnt = scnprintf(buf,maxlen," [");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
sm = 0;
|
||||
spcfl = 0;
|
||||
for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
|
||||
if (!(cp->ctl_mask & msk)) continue;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (opf) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%s",
|
||||
spcfl ? " " : "",
|
||||
opf->name);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
} else {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%lx",
|
||||
idx != 0 ? " " : "",sm);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,"]");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
struct list_head *item;
|
||||
struct pvr2_i2c_client *cp;
|
||||
ccnt = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
list_for_each(item,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,list);
|
||||
bcnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
(PVR2_I2C_DETAIL_HANDLER|
|
||||
PVR2_I2C_DETAIL_CTLMASK),
|
||||
buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,"\n");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_attach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp;
|
||||
int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
|
||||
cp = kmalloc(sizeof(*cp),GFP_KERNEL);
|
||||
trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (!cp) return -ENOMEM;
|
||||
memset(cp,0,sizeof(*cp));
|
||||
INIT_LIST_HEAD(&cp->list);
|
||||
cp->client = client;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
list_add_tail(&cp->list,&hdw->i2c_clients);
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_detach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp;
|
||||
struct list_head *item,*nc;
|
||||
unsigned long amask = 0;
|
||||
int foundfl = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
list_for_each_safe(item,nc,&hdw->i2c_clients) {
|
||||
cp = list_entry(item,struct pvr2_i2c_client,list);
|
||||
if (cp->client == client) {
|
||||
trace_i2c("pvr2_i2c_detach"
|
||||
" [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (cp->handler &&
|
||||
cp->handler->func_table->detach) {
|
||||
cp->handler->func_table->detach(
|
||||
cp->handler->func_data);
|
||||
}
|
||||
list_del(&cp->list);
|
||||
kfree(cp);
|
||||
foundfl = !0;
|
||||
continue;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (!foundfl) {
|
||||
trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
|
||||
client->name,
|
||||
client->addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm pvr2_i2c_algo_template = {
|
||||
.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,
|
||||
.id = I2C_HW_B_BT848,
|
||||
.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;
|
||||
printk("%s: i2c scan beginning\n",hdw->name);
|
||||
for (i = 0; i < 128; i++) {
|
||||
msg[0].addr = i;
|
||||
rc = i2c_transfer(&hdw->i2c_adap,msg,
|
||||
sizeof(msg)/sizeof(msg[0]));
|
||||
if (rc != 1) continue;
|
||||
printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i);
|
||||
}
|
||||
printk("%s: i2c scan done.\n",hdw->name);
|
||||
}
|
||||
|
||||
void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
// The default action for all possible I2C addresses is just to do
|
||||
// the transfer normally.
|
||||
for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
|
||||
hdw->i2c_func[idx] = pvr2_i2c_basic_op;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
|
||||
// If however we're dealing with new hardware, insert some hacks in
|
||||
// the I2C transfer stack to let things work better.
|
||||
if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
|
||||
hdw->i2c_func[0x1b] = i2c_hack_wm8775;
|
||||
hdw->i2c_func[0x44] = i2c_hack_cx25840;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure the adapter and set up everything else related to it.
|
||||
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));
|
||||
hdw->i2c_adap.algo = &hdw->i2c_algo;
|
||||
hdw->i2c_adap.algo_data = hdw;
|
||||
hdw->i2c_pend_mask = 0;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_active_mask = 0;
|
||||
INIT_LIST_HEAD(&hdw->i2c_clients);
|
||||
mutex_init(&hdw->i2c_list_lock);
|
||||
hdw->i2c_linked = !0;
|
||||
i2c_add_adapter(&hdw->i2c_adap);
|
||||
if (i2c_scan) do_i2c_scan(hdw);
|
||||
}
|
||||
|
||||
void pvr2_i2c_core_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: ***
|
||||
*/
|
96
drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
Normal file
96
drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_CORE_H
|
||||
#define __PVRUSB2_I2C_CORE_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
struct pvr2_hdw;
|
||||
struct pvr2_i2c_client;
|
||||
struct pvr2_i2c_handler;
|
||||
struct pvr2_i2c_handler_functions;
|
||||
struct pvr2_i2c_op;
|
||||
struct pvr2_i2c_op_functions;
|
||||
|
||||
struct pvr2_i2c_client {
|
||||
struct i2c_client *client;
|
||||
struct pvr2_i2c_handler *handler;
|
||||
struct list_head list;
|
||||
int detected_flag;
|
||||
int recv_enable;
|
||||
unsigned long pend_mask;
|
||||
unsigned long ctl_mask;
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler {
|
||||
void *func_data;
|
||||
const struct pvr2_i2c_handler_functions *func_table;
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler_functions {
|
||||
void (*detach)(void *);
|
||||
int (*check)(void *);
|
||||
void (*update)(void *);
|
||||
unsigned int (*describe)(void *,char *,unsigned int);
|
||||
};
|
||||
|
||||
struct pvr2_i2c_op {
|
||||
int (*check)(struct pvr2_hdw *);
|
||||
void (*update)(struct pvr2_hdw *);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
void pvr2_i2c_core_init(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_done(struct pvr2_hdw *);
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *);
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
|
||||
#define PVR2_I2C_DETAIL_DEBUG 0x0001
|
||||
#define PVR2_I2C_DETAIL_HANDLER 0x0002
|
||||
#define PVR2_I2C_DETAIL_CTLMASK 0x0004
|
||||
#define PVR2_I2C_DETAIL_ALL (\
|
||||
PVR2_I2C_DETAIL_DEBUG |\
|
||||
PVR2_I2C_DETAIL_HANDLER |\
|
||||
PVR2_I2C_DETAIL_CTLMASK)
|
||||
unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *,
|
||||
unsigned int detail_mask,
|
||||
char *buf,unsigned int maxlen);
|
||||
|
||||
void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
|
||||
|
||||
#endif /* __PVRUSB2_I2C_CORE_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: ***
|
||||
*/
|
695
drivers/media/video/pvrusb2/pvrusb2-io.c
Normal file
695
drivers/media/video/pvrusb2/pvrusb2-io.c
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/mutex.h>
|
||||
|
||||
#define BUFFER_SIG 0x47653271
|
||||
|
||||
// #define SANITY_CHECK_BUFFERS
|
||||
|
||||
|
||||
#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 mutex mutex;
|
||||
/* Tracking state for tolerating errors */
|
||||
unsigned int fail_count;
|
||||
unsigned int fail_tolerance;
|
||||
};
|
||||
|
||||
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);
|
||||
mutex_init(&sp->mutex);
|
||||
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)
|
||||
{
|
||||
mutex_lock(&sp->mutex); do {
|
||||
pvr2_stream_internal_flush(sp);
|
||||
pvr2_stream_buffer_count(sp,0);
|
||||
} while (0); mutex_unlock(&sp->mutex);
|
||||
}
|
||||
|
||||
static void buffer_complete(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct pvr2_buffer *bp = urb->context;
|
||||
struct pvr2_stream *sp;
|
||||
unsigned long irq_flags;
|
||||
BUFFER_CHECK(bp);
|
||||
sp = bp->stream;
|
||||
bp->used_count = 0;
|
||||
bp->status = 0;
|
||||
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
||||
"/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
|
||||
bp,urb->status,urb->actual_length);
|
||||
spin_lock_irqsave(&sp->list_lock,irq_flags);
|
||||
if ((!(urb->status)) ||
|
||||
(urb->status == -ENOENT) ||
|
||||
(urb->status == -ECONNRESET) ||
|
||||
(urb->status == -ESHUTDOWN)) {
|
||||
bp->used_count = urb->actual_length;
|
||||
if (sp->fail_count) {
|
||||
pvr2_trace(PVR2_TRACE_TOLERANCE,
|
||||
"stream %p transfer ok"
|
||||
" - fail count reset",sp);
|
||||
sp->fail_count = 0;
|
||||
}
|
||||
} else if (sp->fail_count < sp->fail_tolerance) {
|
||||
// We can tolerate this error, because we're below the
|
||||
// threshold...
|
||||
(sp->fail_count)++;
|
||||
pvr2_trace(PVR2_TRACE_TOLERANCE,
|
||||
"stream %p ignoring error %d"
|
||||
" - fail count increased to %u",
|
||||
sp,urb->status,sp->fail_count);
|
||||
} else {
|
||||
bp->status = urb->status;
|
||||
}
|
||||
spin_unlock_irqrestore(&sp->list_lock,irq_flags);
|
||||
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,
|
||||
unsigned int tolerance)
|
||||
{
|
||||
mutex_lock(&sp->mutex); do {
|
||||
pvr2_stream_internal_flush(sp);
|
||||
sp->dev = dev;
|
||||
sp->endpoint = endpoint;
|
||||
sp->fail_tolerance = tolerance;
|
||||
} while(0); mutex_unlock(&sp->mutex);
|
||||
}
|
||||
|
||||
void pvr2_stream_set_callback(struct pvr2_stream *sp,
|
||||
pvr2_stream_callback func,
|
||||
void *data)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
mutex_lock(&sp->mutex); 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); mutex_unlock(&sp->mutex);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
mutex_lock(&sp->mutex); do {
|
||||
sp->buffer_target_count = cnt;
|
||||
ret = pvr2_stream_achieve_buffer_count(sp);
|
||||
} while(0); mutex_unlock(&sp->mutex);
|
||||
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)
|
||||
{
|
||||
mutex_lock(&sp->mutex); do {
|
||||
pvr2_stream_internal_flush(sp);
|
||||
} while(0); mutex_unlock(&sp->mutex);
|
||||
}
|
||||
|
||||
void pvr2_stream_kill(struct pvr2_stream *sp)
|
||||
{
|
||||
struct pvr2_buffer *bp;
|
||||
mutex_lock(&sp->mutex); 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); mutex_unlock(&sp->mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
mutex_lock(&sp->mutex); 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); mutex_unlock(&sp->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pvr2_buffer_idle(struct pvr2_buffer *bp)
|
||||
{
|
||||
struct pvr2_stream *sp;
|
||||
if (!bp) return -EINVAL;
|
||||
sp = bp->stream;
|
||||
mutex_lock(&sp->mutex); 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); mutex_unlock(&sp->mutex);
|
||||
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;
|
||||
mutex_lock(&sp->mutex); 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); mutex_unlock(&sp->mutex);
|
||||
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: ***
|
||||
*/
|
102
drivers/media/video/pvrusb2/pvrusb2-io.h
Normal file
102
drivers/media/video/pvrusb2/pvrusb2-io.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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,
|
||||
unsigned int tolerance);
|
||||
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: ***
|
||||
*/
|
513
drivers/media/video/pvrusb2/pvrusb2-ioread.c
Normal file
513
drivers/media/video/pvrusb2/pvrusb2-ioread.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 <linux/mutex.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];
|
||||
char *sync_key_ptr;
|
||||
unsigned int sync_key_len;
|
||||
unsigned int sync_buf_offs;
|
||||
unsigned int sync_state;
|
||||
unsigned int sync_trashed_count;
|
||||
int enabled; // Streaming is on
|
||||
int spigot_open; // OK to pass data to client
|
||||
int stream_running; // Passing data to client now
|
||||
|
||||
/* State relevant to current buffer being read */
|
||||
struct pvr2_buffer *c_buf;
|
||||
char *c_data_ptr;
|
||||
unsigned int c_data_len;
|
||||
unsigned int c_data_offs;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
static int pvr2_ioread_init(struct pvr2_ioread *cp)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
cp->stream = 0;
|
||||
mutex_init(&cp->mutex);
|
||||
|
||||
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);
|
||||
if (cp->sync_key_ptr) {
|
||||
kfree(cp->sync_key_ptr);
|
||||
cp->sync_key_ptr = 0;
|
||||
}
|
||||
kfree(cp);
|
||||
}
|
||||
|
||||
void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp,
|
||||
const char *sync_key_ptr,
|
||||
unsigned int sync_key_len)
|
||||
{
|
||||
if (!cp) return;
|
||||
|
||||
if (!sync_key_ptr) sync_key_len = 0;
|
||||
if ((sync_key_len == cp->sync_key_len) &&
|
||||
((!sync_key_len) ||
|
||||
(!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return;
|
||||
|
||||
if (sync_key_len != cp->sync_key_len) {
|
||||
if (cp->sync_key_ptr) {
|
||||
kfree(cp->sync_key_ptr);
|
||||
cp->sync_key_ptr = 0;
|
||||
}
|
||||
cp->sync_key_len = 0;
|
||||
if (sync_key_len) {
|
||||
cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL);
|
||||
if (cp->sync_key_ptr) {
|
||||
cp->sync_key_len = sync_key_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cp->sync_key_len) return;
|
||||
memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len);
|
||||
}
|
||||
|
||||
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->c_buf = 0;
|
||||
cp->c_data_ptr = 0;
|
||||
cp->c_data_len = 0;
|
||||
cp->c_data_offs = 0;
|
||||
cp->enabled = 0;
|
||||
cp->stream_running = 0;
|
||||
cp->spigot_open = 0;
|
||||
if (cp->sync_state) {
|
||||
pvr2_trace(PVR2_TRACE_DATA_FLOW,
|
||||
"/*---TRACE_READ---*/ sync_state <== 0");
|
||||
cp->sync_state = 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);
|
||||
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->c_buf = 0;
|
||||
cp->c_data_ptr = 0;
|
||||
cp->c_data_len = 0;
|
||||
cp->c_data_offs = 0;
|
||||
cp->stream_running = 0;
|
||||
if (cp->sync_key_len) {
|
||||
pvr2_trace(PVR2_TRACE_DATA_FLOW,
|
||||
"/*---TRACE_READ---*/ sync_state <== 1");
|
||||
cp->sync_state = 1;
|
||||
cp->sync_trashed_count = 0;
|
||||
cp->sync_buf_offs = 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;
|
||||
|
||||
mutex_lock(&cp->mutex); 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); mutex_unlock(&cp->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
|
||||
{
|
||||
int ret = 0;
|
||||
if ((!fl) == (!(cp->enabled))) return ret;
|
||||
|
||||
mutex_lock(&cp->mutex); do {
|
||||
if (fl) {
|
||||
ret = pvr2_ioread_start(cp);
|
||||
} else {
|
||||
pvr2_ioread_stop(cp);
|
||||
}
|
||||
} while (0); mutex_unlock(&cp->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pvr2_ioread_get_enabled(struct pvr2_ioread *cp)
|
||||
{
|
||||
return cp->enabled != 0;
|
||||
}
|
||||
|
||||
int pvr2_ioread_get_buffer(struct pvr2_ioread *cp)
|
||||
{
|
||||
int stat;
|
||||
|
||||
while (cp->c_data_len <= cp->c_data_offs) {
|
||||
if (cp->c_buf) {
|
||||
// Flush out current buffer first.
|
||||
stat = pvr2_buffer_queue(cp->c_buf);
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
cp->c_buf = 0;
|
||||
cp->c_data_ptr = 0;
|
||||
cp->c_data_len = 0;
|
||||
cp->c_data_offs = 0;
|
||||
}
|
||||
// Now get a freshly filled buffer.
|
||||
cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream);
|
||||
if (!cp->c_buf) break; // Nothing ready; done.
|
||||
cp->c_data_len = pvr2_buffer_get_count(cp->c_buf);
|
||||
if (!cp->c_data_len) {
|
||||
// Nothing transferred. Was there an error?
|
||||
stat = pvr2_buffer_get_status(cp->c_buf);
|
||||
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);
|
||||
// Give up.
|
||||
return 0;
|
||||
}
|
||||
// Start over...
|
||||
continue;
|
||||
}
|
||||
cp->c_data_offs = 0;
|
||||
cp->c_data_ptr = cp->buffer_storage[
|
||||
pvr2_buffer_get_id(cp->c_buf)];
|
||||
}
|
||||
return !0;
|
||||
}
|
||||
|
||||
void pvr2_ioread_filter(struct pvr2_ioread *cp)
|
||||
{
|
||||
unsigned int idx;
|
||||
if (!cp->enabled) return;
|
||||
if (cp->sync_state != 1) return;
|
||||
|
||||
// Search the stream for our synchronization key. This is made
|
||||
// complicated by the fact that in order to be honest with
|
||||
// ourselves here we must search across buffer boundaries...
|
||||
mutex_lock(&cp->mutex); while (1) {
|
||||
// Ensure we have a buffer
|
||||
if (!pvr2_ioread_get_buffer(cp)) break;
|
||||
if (!cp->c_data_len) break;
|
||||
|
||||
// Now walk the buffer contents until we match the key or
|
||||
// run out of buffer data.
|
||||
for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) {
|
||||
if (cp->sync_buf_offs >= cp->sync_key_len) break;
|
||||
if (cp->c_data_ptr[idx] ==
|
||||
cp->sync_key_ptr[cp->sync_buf_offs]) {
|
||||
// Found the next key byte
|
||||
(cp->sync_buf_offs)++;
|
||||
} else {
|
||||
// Whoops, mismatched. Start key over...
|
||||
cp->sync_buf_offs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Consume what we've walked through
|
||||
cp->c_data_offs += idx;
|
||||
cp->sync_trashed_count += idx;
|
||||
|
||||
// If we've found the key, then update state and get out.
|
||||
if (cp->sync_buf_offs >= cp->sync_key_len) {
|
||||
cp->sync_trashed_count -= cp->sync_key_len;
|
||||
pvr2_trace(PVR2_TRACE_DATA_FLOW,
|
||||
"/*---TRACE_READ---*/"
|
||||
" sync_state <== 2 (skipped %u bytes)",
|
||||
cp->sync_trashed_count);
|
||||
cp->sync_state = 2;
|
||||
cp->sync_buf_offs = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cp->c_data_offs < cp->c_data_len) {
|
||||
// Sanity check - should NEVER get here
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"ERROR: pvr2_ioread filter sync problem"
|
||||
" len=%u offs=%u",
|
||||
cp->c_data_len,cp->c_data_offs);
|
||||
// Get out so we don't get stuck in an infinite
|
||||
// loop.
|
||||
break;
|
||||
}
|
||||
|
||||
continue; // (for clarity)
|
||||
} mutex_unlock(&cp->mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (cp->sync_state == 1) {
|
||||
pvr2_ioread_filter(cp);
|
||||
if (cp->sync_state == 1) return -EAGAIN;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
mutex_lock(&cp->mutex); do {
|
||||
|
||||
// Suck data out of the buffers and copy to the user
|
||||
copied_cnt = 0;
|
||||
if (!buf) cnt = 0;
|
||||
while (1) {
|
||||
if (!pvr2_ioread_get_buffer(cp)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cnt) break;
|
||||
|
||||
if (cp->sync_state == 2) {
|
||||
// We're repeating the sync key data into
|
||||
// the stream.
|
||||
src = cp->sync_key_ptr + cp->sync_buf_offs;
|
||||
bcnt = cp->sync_key_len - cp->sync_buf_offs;
|
||||
} else {
|
||||
// Normal buffer copy
|
||||
src = cp->c_data_ptr + cp->c_data_offs;
|
||||
bcnt = cp->c_data_len - cp->c_data_offs;
|
||||
}
|
||||
|
||||
if (!bcnt) break;
|
||||
|
||||
// Don't run past user's buffer
|
||||
if (bcnt > cnt) bcnt = cnt;
|
||||
|
||||
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->sync_state == 2) {
|
||||
// Update offset inside sync key that we're
|
||||
// repeating back out.
|
||||
cp->sync_buf_offs += bcnt;
|
||||
if (cp->sync_buf_offs >= cp->sync_key_len) {
|
||||
// Consumed entire key; switch mode
|
||||
// to normal.
|
||||
pvr2_trace(PVR2_TRACE_DATA_FLOW,
|
||||
"/*---TRACE_READ---*/"
|
||||
" sync_state <== 0");
|
||||
cp->sync_state = 0;
|
||||
}
|
||||
} else {
|
||||
// Update buffer offset.
|
||||
cp->c_data_offs += bcnt;
|
||||
}
|
||||
}
|
||||
|
||||
} while (0); mutex_unlock(&cp->mutex);
|
||||
|
||||
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: ***
|
||||
*/
|
50
drivers/media/video/pvrusb2/pvrusb2-ioread.h
Normal file
50
drivers/media/video/pvrusb2/pvrusb2-ioread.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 *);
|
||||
void pvr2_ioread_set_sync_key(struct pvr2_ioread *,
|
||||
const char *sync_key_ptr,
|
||||
unsigned int sync_key_len);
|
||||
int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl);
|
||||
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: ***
|
||||
*/
|
180
drivers/media/video/pvrusb2/pvrusb2-main.c
Normal file
180
drivers/media/video/pvrusb2/pvrusb2-main.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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/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/videodev2.h>
|
||||
|
||||
#include "pvrusb2-hdw.h"
|
||||
#include "pvrusb2-context.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include "pvrusb2-v4l2.h"
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
|
||||
#include "pvrusb2-sysfs.h"
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
|
||||
|
||||
#define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>"
|
||||
#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner"
|
||||
#define DRIVER_VERSION "V4L in-tree version"
|
||||
|
||||
#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \
|
||||
PVR2_TRACE_INFO| \
|
||||
PVR2_TRACE_TOLERANCE| \
|
||||
PVR2_TRACE_TRAP| \
|
||||
PVR2_TRACE_FIRMWARE| \
|
||||
PVR2_TRACE_EEPROM | \
|
||||
PVR2_TRACE_INIT | \
|
||||
PVR2_TRACE_I2C | \
|
||||
PVR2_TRACE_CHIPS | \
|
||||
PVR2_TRACE_START_STOP | \
|
||||
PVR2_TRACE_CTL | \
|
||||
PVR2_TRACE_DEBUGIFC | \
|
||||
0)
|
||||
|
||||
int pvrusb2_debug = DEFAULT_DEBUG_MASK;
|
||||
|
||||
module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug trace mask");
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
|
||||
static struct pvr2_sysfs_class *class_ptr = 0;
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
|
||||
|
||||
static void pvr_setup_attach(struct pvr2_context *pvr)
|
||||
{
|
||||
/* Create association with v4l layer */
|
||||
pvr2_v4l2_create(pvr);
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
|
||||
pvr2_sysfs_create(pvr,class_ptr);
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
|
||||
}
|
||||
|
||||
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,devid,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 = {
|
||||
name: "pvrusb2",
|
||||
id_table: pvr2_device_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");
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
|
||||
class_ptr = pvr2_sysfs_class_create();
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
|
||||
|
||||
ret = usb_register(&pvr_driver);
|
||||
|
||||
if (ret == 0)
|
||||
info(DRIVER_DESC " : " DRIVER_VERSION);
|
||||
if (pvrusb2_debug) info("Debug mask is %d (0x%x)",
|
||||
pvrusb2_debug,pvrusb2_debug);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit pvr_exit(void)
|
||||
{
|
||||
|
||||
pvr2_trace(PVR2_TRACE_INIT,"pvr_exit");
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
|
||||
pvr2_sysfs_class_destroy(class_ptr);
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
|
||||
|
||||
usb_deregister(&pvr_driver);
|
||||
}
|
||||
|
||||
module_init(pvr_init);
|
||||
module_exit(pvr_exit);
|
||||
|
||||
/* Mike Isely <mcisely@pobox.com> 11-Mar-2006: See pvrusb2-hdw.c for
|
||||
MODULE_DEVICE_TABLE(). We have to declare that attribute there
|
||||
because that's where the device table actually is now and it seems
|
||||
that certain gcc configurations get angry if MODULE_DEVICE_TABLE()
|
||||
is used on what ends up being an external symbol. */
|
||||
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: ***
|
||||
*/
|
406
drivers/media/video/pvrusb2/pvrusb2-std.c
Normal file
406
drivers/media/video/pvrusb2/pvrusb2-std.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-std.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
|
||||
struct std_name {
|
||||
const char *name;
|
||||
v4l2_std_id id;
|
||||
};
|
||||
|
||||
|
||||
#define CSTD_PAL \
|
||||
(V4L2_STD_PAL_B| \
|
||||
V4L2_STD_PAL_B1| \
|
||||
V4L2_STD_PAL_G| \
|
||||
V4L2_STD_PAL_H| \
|
||||
V4L2_STD_PAL_I| \
|
||||
V4L2_STD_PAL_D| \
|
||||
V4L2_STD_PAL_D1| \
|
||||
V4L2_STD_PAL_K| \
|
||||
V4L2_STD_PAL_M| \
|
||||
V4L2_STD_PAL_N| \
|
||||
V4L2_STD_PAL_Nc| \
|
||||
V4L2_STD_PAL_60)
|
||||
|
||||
#define CSTD_NTSC \
|
||||
(V4L2_STD_NTSC_M| \
|
||||
V4L2_STD_NTSC_M_JP| \
|
||||
V4L2_STD_NTSC_M_KR| \
|
||||
V4L2_STD_NTSC_443)
|
||||
|
||||
#define CSTD_SECAM \
|
||||
(V4L2_STD_SECAM_B| \
|
||||
V4L2_STD_SECAM_D| \
|
||||
V4L2_STD_SECAM_G| \
|
||||
V4L2_STD_SECAM_H| \
|
||||
V4L2_STD_SECAM_K| \
|
||||
V4L2_STD_SECAM_K1| \
|
||||
V4L2_STD_SECAM_L| \
|
||||
V4L2_STD_SECAM_LC)
|
||||
|
||||
#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B)
|
||||
#define TSTD_B1 (V4L2_STD_PAL_B1)
|
||||
#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D)
|
||||
#define TSTD_D1 (V4L2_STD_PAL_D1)
|
||||
#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G)
|
||||
#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H)
|
||||
#define TSTD_I (V4L2_STD_PAL_I)
|
||||
#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K)
|
||||
#define TSTD_K1 (V4L2_STD_SECAM_K1)
|
||||
#define TSTD_L (V4L2_STD_SECAM_L)
|
||||
#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M)
|
||||
#define TSTD_N (V4L2_STD_PAL_N)
|
||||
#define TSTD_Nc (V4L2_STD_PAL_Nc)
|
||||
#define TSTD_60 (V4L2_STD_PAL_60)
|
||||
|
||||
#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM)
|
||||
|
||||
/* Mapping of standard bits to color system */
|
||||
const static struct std_name std_groups[] = {
|
||||
{"PAL",CSTD_PAL},
|
||||
{"NTSC",CSTD_NTSC},
|
||||
{"SECAM",CSTD_SECAM},
|
||||
};
|
||||
|
||||
/* Mapping of standard bits to modulation system */
|
||||
const static struct std_name std_items[] = {
|
||||
{"B",TSTD_B},
|
||||
{"B1",TSTD_B1},
|
||||
{"D",TSTD_D},
|
||||
{"D1",TSTD_D1},
|
||||
{"G",TSTD_G},
|
||||
{"H",TSTD_H},
|
||||
{"I",TSTD_I},
|
||||
{"K",TSTD_K},
|
||||
{"K1",TSTD_K1},
|
||||
{"L",TSTD_L},
|
||||
{"LC",V4L2_STD_SECAM_LC},
|
||||
{"M",TSTD_M},
|
||||
{"Mj",V4L2_STD_NTSC_M_JP},
|
||||
{"443",V4L2_STD_NTSC_443},
|
||||
{"Mk",V4L2_STD_NTSC_M_KR},
|
||||
{"N",TSTD_N},
|
||||
{"Nc",TSTD_Nc},
|
||||
{"60",TSTD_60},
|
||||
};
|
||||
|
||||
|
||||
// Search an array of std_name structures and return a pointer to the
|
||||
// element with the matching name.
|
||||
static const struct std_name *find_std_name(const struct std_name *arrPtr,
|
||||
unsigned int arrSize,
|
||||
const char *bufPtr,
|
||||
unsigned int bufSize)
|
||||
{
|
||||
unsigned int idx;
|
||||
const struct std_name *p;
|
||||
for (idx = 0; idx < arrSize; idx++) {
|
||||
p = arrPtr + idx;
|
||||
if (strlen(p->name) != bufSize) continue;
|
||||
if (!memcmp(bufPtr,p->name,bufSize)) return p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
|
||||
unsigned int bufSize)
|
||||
{
|
||||
v4l2_std_id id = 0;
|
||||
v4l2_std_id cmsk = 0;
|
||||
v4l2_std_id t;
|
||||
int mMode = 0;
|
||||
unsigned int cnt;
|
||||
char ch;
|
||||
const struct std_name *sp;
|
||||
|
||||
while (bufSize) {
|
||||
if (!mMode) {
|
||||
cnt = 0;
|
||||
while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++;
|
||||
if (cnt >= bufSize) return 0; // No more characters
|
||||
sp = find_std_name(
|
||||
std_groups,
|
||||
sizeof(std_groups)/sizeof(std_groups[0]),
|
||||
bufPtr,cnt);
|
||||
if (!sp) return 0; // Illegal color system name
|
||||
cnt++;
|
||||
bufPtr += cnt;
|
||||
bufSize -= cnt;
|
||||
mMode = !0;
|
||||
cmsk = sp->id;
|
||||
continue;
|
||||
}
|
||||
cnt = 0;
|
||||
while (cnt < bufSize) {
|
||||
ch = bufPtr[cnt];
|
||||
if (ch == ';') {
|
||||
mMode = 0;
|
||||
break;
|
||||
}
|
||||
if (ch == '/') break;
|
||||
cnt++;
|
||||
}
|
||||
sp = find_std_name(std_items,
|
||||
sizeof(std_items)/sizeof(std_items[0]),
|
||||
bufPtr,cnt);
|
||||
if (!sp) return 0; // Illegal modulation system ID
|
||||
t = sp->id & cmsk;
|
||||
if (!t) return 0; // Specific color + modulation system illegal
|
||||
id |= t;
|
||||
if (cnt < bufSize) cnt++;
|
||||
bufPtr += cnt;
|
||||
bufSize -= cnt;
|
||||
}
|
||||
|
||||
if (idPtr) *idPtr = id;
|
||||
return !0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
|
||||
v4l2_std_id id)
|
||||
{
|
||||
unsigned int idx1,idx2;
|
||||
const struct std_name *ip,*gp;
|
||||
int gfl,cfl;
|
||||
unsigned int c1,c2;
|
||||
cfl = 0;
|
||||
c1 = 0;
|
||||
for (idx1 = 0;
|
||||
idx1 < sizeof(std_groups)/sizeof(std_groups[0]);
|
||||
idx1++) {
|
||||
gp = std_groups + idx1;
|
||||
gfl = 0;
|
||||
for (idx2 = 0;
|
||||
idx2 < sizeof(std_items)/sizeof(std_items[0]);
|
||||
idx2++) {
|
||||
ip = std_items + idx2;
|
||||
if (!(gp->id & ip->id & id)) continue;
|
||||
if (!gfl) {
|
||||
if (cfl) {
|
||||
c2 = scnprintf(bufPtr,bufSize,";");
|
||||
c1 += c2;
|
||||
bufSize -= c2;
|
||||
bufPtr += c2;
|
||||
}
|
||||
cfl = !0;
|
||||
c2 = scnprintf(bufPtr,bufSize,
|
||||
"%s-",gp->name);
|
||||
gfl = !0;
|
||||
} else {
|
||||
c2 = scnprintf(bufPtr,bufSize,"/");
|
||||
}
|
||||
c1 += c2;
|
||||
bufSize -= c2;
|
||||
bufPtr += c2;
|
||||
c2 = scnprintf(bufPtr,bufSize,
|
||||
ip->name);
|
||||
c1 += c2;
|
||||
bufSize -= c2;
|
||||
bufPtr += c2;
|
||||
}
|
||||
}
|
||||
return c1;
|
||||
}
|
||||
|
||||
|
||||
// Template data for possible enumerated video standards. Here we group
|
||||
// standards which share common frame rates and resolution.
|
||||
static struct v4l2_standard generic_standards[] = {
|
||||
{
|
||||
.id = (TSTD_B|TSTD_B1|
|
||||
TSTD_D|TSTD_D1|
|
||||
TSTD_G|
|
||||
TSTD_H|
|
||||
TSTD_I|
|
||||
TSTD_K|TSTD_K1|
|
||||
TSTD_L|
|
||||
V4L2_STD_SECAM_LC |
|
||||
TSTD_N|TSTD_Nc),
|
||||
.frameperiod =
|
||||
{
|
||||
.numerator = 1,
|
||||
.denominator= 25
|
||||
},
|
||||
.framelines = 625,
|
||||
.reserved = {0,0,0,0}
|
||||
}, {
|
||||
.id = (TSTD_M|
|
||||
V4L2_STD_NTSC_M_JP|
|
||||
V4L2_STD_NTSC_M_KR),
|
||||
.frameperiod =
|
||||
{
|
||||
.numerator = 1001,
|
||||
.denominator= 30000
|
||||
},
|
||||
.framelines = 525,
|
||||
.reserved = {0,0,0,0}
|
||||
}, { // This is a total wild guess
|
||||
.id = (TSTD_60),
|
||||
.frameperiod =
|
||||
{
|
||||
.numerator = 1001,
|
||||
.denominator= 30000
|
||||
},
|
||||
.framelines = 525,
|
||||
.reserved = {0,0,0,0}
|
||||
}, { // This is total wild guess
|
||||
.id = V4L2_STD_NTSC_443,
|
||||
.frameperiod =
|
||||
{
|
||||
.numerator = 1001,
|
||||
.denominator= 30000
|
||||
},
|
||||
.framelines = 525,
|
||||
.reserved = {0,0,0,0}
|
||||
}
|
||||
};
|
||||
|
||||
#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0]))
|
||||
|
||||
static struct v4l2_standard *match_std(v4l2_std_id id)
|
||||
{
|
||||
unsigned int idx;
|
||||
for (idx = 0; idx < generic_standards_cnt; idx++) {
|
||||
if (generic_standards[idx].id & id) {
|
||||
return generic_standards + idx;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id)
|
||||
{
|
||||
struct v4l2_standard *template;
|
||||
int idx;
|
||||
unsigned int bcnt;
|
||||
template = match_std(id);
|
||||
if (!template) return 0;
|
||||
idx = std->index;
|
||||
memcpy(std,template,sizeof(*template));
|
||||
std->index = idx;
|
||||
std->id = id;
|
||||
bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id);
|
||||
std->name[bcnt] = 0;
|
||||
pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s",
|
||||
std->index,std->name);
|
||||
return !0;
|
||||
}
|
||||
|
||||
/* These are special cases of combined standards that we should enumerate
|
||||
separately if the component pieces are present. */
|
||||
static v4l2_std_id std_mixes[] = {
|
||||
V4L2_STD_PAL_B | V4L2_STD_PAL_G,
|
||||
V4L2_STD_PAL_D | V4L2_STD_PAL_K,
|
||||
V4L2_STD_SECAM_B | V4L2_STD_SECAM_G,
|
||||
V4L2_STD_SECAM_D | V4L2_STD_SECAM_K,
|
||||
};
|
||||
|
||||
struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
|
||||
v4l2_std_id id)
|
||||
{
|
||||
unsigned int std_cnt = 0;
|
||||
unsigned int idx,bcnt,idx2;
|
||||
v4l2_std_id idmsk,cmsk,fmsk;
|
||||
struct v4l2_standard *stddefs;
|
||||
|
||||
if (pvrusb2_debug & PVR2_TRACE_INIT) {
|
||||
char buf[50];
|
||||
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
|
||||
pvr2_trace(
|
||||
PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)",
|
||||
(int)id,bcnt,buf);
|
||||
}
|
||||
|
||||
*countptr = 0;
|
||||
std_cnt = 0;
|
||||
fmsk = 0;
|
||||
for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) {
|
||||
if (!(idmsk & cmsk)) continue;
|
||||
cmsk &= ~idmsk;
|
||||
if (match_std(idmsk)) {
|
||||
std_cnt++;
|
||||
continue;
|
||||
}
|
||||
fmsk |= idmsk;
|
||||
}
|
||||
|
||||
for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) {
|
||||
if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
|
||||
}
|
||||
|
||||
if (fmsk) {
|
||||
char buf[50];
|
||||
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk);
|
||||
pvr2_trace(
|
||||
PVR2_TRACE_ERROR_LEGS,
|
||||
"WARNING:"
|
||||
" Failed to classify the following standard(s): %.*s",
|
||||
bcnt,buf);
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)",
|
||||
std_cnt);
|
||||
if (!std_cnt) return 0; // paranoia
|
||||
|
||||
stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt,
|
||||
GFP_KERNEL);
|
||||
memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt);
|
||||
for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx;
|
||||
|
||||
idx = 0;
|
||||
|
||||
/* Enumerate potential special cases */
|
||||
for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) &&
|
||||
(idx < std_cnt)); idx2++) {
|
||||
if (!(id & std_mixes[idx2])) continue;
|
||||
if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++;
|
||||
}
|
||||
/* Now enumerate individual pieces */
|
||||
for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) {
|
||||
if (!(idmsk & cmsk)) continue;
|
||||
cmsk &= ~idmsk;
|
||||
if (!pvr2_std_fill(stddefs+idx,idmsk)) continue;
|
||||
idx++;
|
||||
}
|
||||
|
||||
*countptr = std_cnt;
|
||||
return stddefs;
|
||||
}
|
||||
|
||||
v4l2_std_id pvr2_std_get_usable(void)
|
||||
{
|
||||
return CSTD_ALL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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: ***
|
||||
*/
|
60
drivers/media/video/pvrusb2/pvrusb2-std.h
Normal file
60
drivers/media/video/pvrusb2/pvrusb2-std.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_STD_H
|
||||
#define __PVRUSB2_STD_H
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
// Convert string describing one or more video standards into a mask of V4L
|
||||
// standard bits. Return true if conversion succeeds otherwise return
|
||||
// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and
|
||||
// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are
|
||||
// modulation schemes (e.g. "M", "B", "G", etc).
|
||||
int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
|
||||
unsigned int bufSize);
|
||||
|
||||
// Convert any arbitrary set of video standard bits into an unambiguous
|
||||
// readable string. Return value is the number of bytes consumed in the
|
||||
// buffer. The formatted string is of a form that can be parsed by our
|
||||
// sibling std_std_to_id() function.
|
||||
unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
|
||||
v4l2_std_id id);
|
||||
|
||||
// Create an array of suitable v4l2_standard structures given a bit mask of
|
||||
// video standards to support. The array is allocated from the heap, and
|
||||
// the number of elements is returned in the first argument.
|
||||
struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
|
||||
v4l2_std_id id);
|
||||
|
||||
// Return mask of which video standard bits are valid
|
||||
v4l2_std_id pvr2_std_get_usable(void);
|
||||
|
||||
#endif /* __PVRUSB2_STD_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: ***
|
||||
*/
|
775
drivers/media/video/pvrusb2/pvrusb2-sysfs.c
Normal file
775
drivers/media/video/pvrusb2/pvrusb2-sysfs.c
Normal file
|
@ -0,0 +1,775 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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/config.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include "pvrusb2-sysfs.h"
|
||||
#include "pvrusb2-hdw.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
#include "pvrusb2-debugifc.h"
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
|
||||
#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__)
|
||||
|
||||
struct pvr2_sysfs {
|
||||
struct pvr2_channel channel;
|
||||
struct class_device *class_dev;
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
struct pvr2_sysfs_debugifc *debugifc;
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_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;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
struct pvr2_sysfs_debugifc {
|
||||
struct class_device_attribute attr_debugcmd;
|
||||
struct class_device_attribute attr_debuginfo;
|
||||
};
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
|
||||
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_bits;
|
||||
struct class_device_attribute attr_val;
|
||||
struct class_device_attribute attr_custom;
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *chptr;
|
||||
struct pvr2_sysfs_ctl_item *item_next;
|
||||
struct attribute *attr_gen[6];
|
||||
struct attribute_group grp;
|
||||
char name[80];
|
||||
};
|
||||
|
||||
struct pvr2_sysfs_class {
|
||||
struct class class;
|
||||
};
|
||||
|
||||
static ssize_t show_name(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
const char *name;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
|
||||
name = pvr2_ctrl_get_desc(cptr);
|
||||
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_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
long val;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
val = pvr2_ctrl_get_min(cptr);
|
||||
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val);
|
||||
|
||||
return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
|
||||
}
|
||||
|
||||
static ssize_t show_max(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
long val;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
val = pvr2_ctrl_get_max(cptr);
|
||||
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val);
|
||||
|
||||
return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
|
||||
}
|
||||
|
||||
static ssize_t show_val_norm(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
int val,ret;
|
||||
unsigned int cnt = 0;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
|
||||
ret = pvr2_ctrl_get_value(cptr,&val);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = pvr2_ctrl_value_to_sym(cptr,~0,val,
|
||||
buf,PAGE_SIZE-1,&cnt);
|
||||
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
|
||||
sfp,id,cnt,buf,val);
|
||||
buf[cnt] = '\n';
|
||||
return cnt+1;
|
||||
}
|
||||
|
||||
static ssize_t show_val_custom(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
int val,ret;
|
||||
unsigned int cnt = 0;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
|
||||
ret = pvr2_ctrl_get_value(cptr,&val);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val,
|
||||
buf,PAGE_SIZE-1,&cnt);
|
||||
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
|
||||
sfp,id,cnt,buf,val);
|
||||
buf[cnt] = '\n';
|
||||
return cnt+1;
|
||||
}
|
||||
|
||||
static ssize_t show_enum(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
long val;
|
||||
unsigned int bcnt,ccnt,ecnt;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
ecnt = pvr2_ctrl_get_cnt(cptr);
|
||||
bcnt = 0;
|
||||
for (val = 0; val < ecnt; val++) {
|
||||
pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
|
||||
bcnt += ccnt;
|
||||
if (bcnt >= PAGE_SIZE) break;
|
||||
buf[bcnt] = '\n';
|
||||
bcnt++;
|
||||
}
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id);
|
||||
return bcnt;
|
||||
}
|
||||
|
||||
static ssize_t show_bits(int id,struct class_device *class_dev,char *buf)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
struct pvr2_sysfs *sfp;
|
||||
int valid_bits,msk;
|
||||
unsigned int bcnt,ccnt;
|
||||
|
||||
sfp = (struct pvr2_sysfs *)class_dev->class_data;
|
||||
if (!sfp) return -EINVAL;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (!cptr) return -EINVAL;
|
||||
valid_bits = pvr2_ctrl_get_mask(cptr);
|
||||
bcnt = 0;
|
||||
for (msk = 1; valid_bits; msk <<= 1) {
|
||||
if (!(msk & valid_bits)) continue;
|
||||
valid_bits &= ~msk;
|
||||
pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
|
||||
bcnt += ccnt;
|
||||
if (bcnt >= PAGE_SIZE) break;
|
||||
buf[bcnt] = '\n';
|
||||
bcnt++;
|
||||
}
|
||||
pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id);
|
||||
return bcnt;
|
||||
}
|
||||
|
||||
static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp,
|
||||
const char *buf,unsigned int count)
|
||||
{
|
||||
struct pvr2_ctrl *cptr;
|
||||
int ret;
|
||||
int mask,val;
|
||||
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
|
||||
if (customfl) {
|
||||
ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val);
|
||||
} else {
|
||||
ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val);
|
||||
}
|
||||
if (ret < 0) return ret;
|
||||
ret = pvr2_ctrl_set_mask_value(cptr,mask,val);
|
||||
pvr2_hdw_commit_ctl(sfp->channel.hdw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t store_val_norm(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_any(id,0,sfp,buf,count);
|
||||
if (!ret) ret = count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t store_val_custom(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_any(id,1,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_norm,ctl_id) \
|
||||
CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \
|
||||
CREATE_SHOW_INSTANCE(show_enum,ctl_id) \
|
||||
CREATE_SHOW_INSTANCE(show_bits,ctl_id) \
|
||||
CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \
|
||||
CREATE_STORE_INSTANCE(store_val_custom,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)
|
||||
CREATE_BATCH(32)
|
||||
CREATE_BATCH(33)
|
||||
|
||||
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_bits)(struct class_device *,char *);
|
||||
ssize_t (*show_val_norm)(struct class_device *,char *);
|
||||
ssize_t (*store_val_norm)(struct class_device *,
|
||||
const char *,size_t);
|
||||
ssize_t (*show_val_custom)(struct class_device *,char *);
|
||||
ssize_t (*store_val_custom)(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_bits = show_bits_##ctl_id, \
|
||||
.show_val_norm = show_val_norm_##ctl_id, \
|
||||
.store_val_norm = store_val_norm_##ctl_id, \
|
||||
.show_val_custom = show_val_custom_##ctl_id, \
|
||||
.store_val_custom = store_val_custom_##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),
|
||||
INIT_BATCH(32),
|
||||
INIT_BATCH(33),
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
struct pvr2_ctrl *cptr;
|
||||
unsigned int cnt,acnt;
|
||||
|
||||
if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) {
|
||||
return;
|
||||
}
|
||||
|
||||
fp = funcs + ctl_id;
|
||||
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
|
||||
if (!cptr) return;
|
||||
|
||||
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->cptr = cptr;
|
||||
|
||||
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_custom.attr.owner = THIS_MODULE;
|
||||
cip->attr_custom.attr.name = "custom_val";
|
||||
cip->attr_custom.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;
|
||||
|
||||
cip->attr_bits.attr.owner = THIS_MODULE;
|
||||
cip->attr_bits.attr.name = "bit_val";
|
||||
cip->attr_bits.attr.mode = S_IRUGO;
|
||||
cip->attr_bits.show = fp->show_bits;
|
||||
|
||||
if (pvr2_ctrl_is_writable(cptr)) {
|
||||
cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
|
||||
cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP;
|
||||
}
|
||||
|
||||
acnt = 0;
|
||||
cip->attr_gen[acnt++] = &cip->attr_name.attr;
|
||||
cip->attr_gen[acnt++] = &cip->attr_val.attr;
|
||||
cip->attr_val.show = fp->show_val_norm;
|
||||
cip->attr_val.store = fp->store_val_norm;
|
||||
if (pvr2_ctrl_has_custom_symbols(cptr)) {
|
||||
cip->attr_gen[acnt++] = &cip->attr_custom.attr;
|
||||
cip->attr_custom.show = fp->show_val_custom;
|
||||
cip->attr_custom.store = fp->store_val_custom;
|
||||
}
|
||||
switch (pvr2_ctrl_get_type(cptr)) {
|
||||
case pvr2_ctl_enum:
|
||||
// Control is an enumeration
|
||||
cip->attr_gen[acnt++] = &cip->attr_enum.attr;
|
||||
break;
|
||||
case pvr2_ctl_int:
|
||||
// Control is an integer
|
||||
cip->attr_gen[acnt++] = &cip->attr_min.attr;
|
||||
cip->attr_gen[acnt++] = &cip->attr_max.attr;
|
||||
break;
|
||||
case pvr2_ctl_bitmask:
|
||||
// Control is an bitmask
|
||||
cip->attr_gen[acnt++] = &cip->attr_bits.attr;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s",
|
||||
pvr2_ctrl_get_name(cptr));
|
||||
cip->name[cnt] = 0;
|
||||
cip->grp.name = cip->name;
|
||||
cip->grp.attrs = cip->attr_gen;
|
||||
|
||||
sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
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;
|
||||
}
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
|
||||
|
||||
static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp)
|
||||
{
|
||||
unsigned int idx,cnt;
|
||||
cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw);
|
||||
for (idx = 0; idx < cnt; idx++) {
|
||||
pvr2_sysfs_add_control(sfp,idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
pvr2_sysfs_tear_down_debugifc(sfp);
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
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);
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
pvr2_sysfs_add_debugifc(sfp);
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
}
|
||||
|
||||
|
||||
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.uevent = 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);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
|
||||
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;
|
||||
pvr2_hdw_trigger_module_log(sfp->channel.hdw);
|
||||
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;
|
||||
}
|
||||
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
|
||||
|
||||
|
||||
/*
|
||||
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: ***
|
||||
*/
|
47
drivers/media/video/pvrusb2/pvrusb2-sysfs.h
Normal file
47
drivers/media/video/pvrusb2/pvrusb2-sysfs.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
122
drivers/media/video/pvrusb2/pvrusb2-tuner.c
Normal file
122
drivers/media/video/pvrusb2/pvrusb2-tuner.c
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/tuner.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
struct pvr2_tuner_handler {
|
||||
struct pvr2_hdw *hdw;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_i2c_handler i2c_handler;
|
||||
int type_update_fl;
|
||||
};
|
||||
|
||||
|
||||
static void set_type(struct pvr2_tuner_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
struct tuner_setup setup;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type);
|
||||
if (((int)(hdw->tuner_type)) < 0) return;
|
||||
|
||||
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 */
|
||||
pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup);
|
||||
ctxt->type_update_fl = 0;
|
||||
}
|
||||
|
||||
|
||||
static int tuner_check(struct pvr2_tuner_handler *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
if (hdw->tuner_updated) ctxt->type_update_fl = !0;
|
||||
return ctxt->type_update_fl != 0;
|
||||
}
|
||||
|
||||
|
||||
static void tuner_update(struct pvr2_tuner_handler *ctxt)
|
||||
{
|
||||
if (ctxt->type_update_fl) set_type(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-tuner");
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions tuner_funcs = {
|
||||
.detach = (void (*)(void *))pvr2_tuner_detach,
|
||||
.check = (int (*)(void *))tuner_check,
|
||||
.update = (void (*)(void *))tuner_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_tuner_handler *ctxt;
|
||||
if (cp->handler) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->i2c_handler.func_data = ctxt;
|
||||
ctxt->i2c_handler.func_table = &tuner_funcs;
|
||||
ctxt->type_update_fl = !0;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
cp->handler = &ctxt->i2c_handler;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
38
drivers/media/video/pvrusb2/pvrusb2-tuner.h
Normal file
38
drivers/media/video/pvrusb2/pvrusb2-tuner.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
#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: ***
|
||||
*/
|
63
drivers/media/video/pvrusb2/pvrusb2-util.h
Normal file
63
drivers/media/video/pvrusb2/pvrusb2-util.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
1056
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
Normal file
1056
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
Normal file
File diff suppressed because it is too large
Load Diff
40
drivers/media/video/pvrusb2/pvrusb2-v4l2.h
Normal file
40
drivers/media/video/pvrusb2/pvrusb2-v4l2.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
250
drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
Normal file
250
drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 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-video-v4l.h"
|
||||
#include "pvrusb2-i2c-cmd-v4l2.h"
|
||||
|
||||
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/saa7115.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct pvr2_v4l_decoder {
|
||||
struct pvr2_i2c_handler handler;
|
||||
struct pvr2_decoder_ctrl ctrl;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_hdw *hdw;
|
||||
unsigned long stale_mask;
|
||||
};
|
||||
|
||||
|
||||
static void set_input(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
struct v4l2_routing route;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
|
||||
switch(hdw->input_val) {
|
||||
case PVR2_CVAL_INPUT_TV:
|
||||
route.input = SAA7115_COMPOSITE4;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_COMPOSITE:
|
||||
route.input = SAA7115_COMPOSITE5;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_SVIDEO:
|
||||
route.input = SAA7115_SVIDEO2;
|
||||
break;
|
||||
case PVR2_CVAL_INPUT_RADIO:
|
||||
// ????? No idea yet what to do here
|
||||
default:
|
||||
return;
|
||||
}
|
||||
route.output = 0;
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
|
||||
}
|
||||
|
||||
|
||||
static int check_input(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return hdw->input_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
static void set_audio(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
u32 val;
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d",
|
||||
hdw->srate_val);
|
||||
switch (hdw->srate_val) {
|
||||
default:
|
||||
case PVR2_CVAL_SRATE_48:
|
||||
val = 48000;
|
||||
break;
|
||||
case PVR2_CVAL_SRATE_44_1:
|
||||
val = 44100;
|
||||
break;
|
||||
}
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
|
||||
}
|
||||
|
||||
|
||||
static int check_audio(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return hdw->srate_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
struct pvr2_v4l_decoder_ops {
|
||||
void (*update)(struct pvr2_v4l_decoder *);
|
||||
int (*check)(struct pvr2_v4l_decoder *);
|
||||
};
|
||||
|
||||
|
||||
static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
|
||||
{ .update = set_input, .check = check_input},
|
||||
{ .update = set_audio, .check = check_audio},
|
||||
};
|
||||
|
||||
|
||||
static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
ctxt->hdw->decoder_ctrl = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static int decoder_check(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (ctxt->stale_mask & msk) continue;
|
||||
if (decoder_ops[idx].check(ctxt)) {
|
||||
ctxt->stale_mask |= msk;
|
||||
}
|
||||
}
|
||||
return ctxt->stale_mask != 0;
|
||||
}
|
||||
|
||||
|
||||
static void decoder_update(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (!(ctxt->stale_mask & msk)) continue;
|
||||
ctxt->stale_mask &= ~msk;
|
||||
decoder_ops[idx].update(ctxt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int decoder_detect(struct pvr2_i2c_client *cp)
|
||||
{
|
||||
/* Attempt to query the decoder - let's see if it will answer */
|
||||
struct v4l2_tuner vt;
|
||||
int ret;
|
||||
|
||||
memset(&vt,0,sizeof(vt));
|
||||
ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt);
|
||||
return ret == 0; /* Return true if it answered */
|
||||
}
|
||||
|
||||
|
||||
static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl)
|
||||
{
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl);
|
||||
pvr2_v4l2_cmd_stream(ctxt->client,fl);
|
||||
}
|
||||
|
||||
|
||||
static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt)
|
||||
{
|
||||
struct v4l2_tuner vt;
|
||||
int ret;
|
||||
|
||||
memset(&vt,0,sizeof(vt));
|
||||
ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
|
||||
if (ret < 0) return -EINVAL;
|
||||
return vt.signal ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l");
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions hfuncs = {
|
||||
.detach = (void (*)(void *))decoder_detach,
|
||||
.check = (int (*)(void *))decoder_check,
|
||||
.update = (void (*)(void *))decoder_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
|
||||
struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_v4l_decoder *ctxt;
|
||||
|
||||
if (hdw->decoder_ctrl) return 0;
|
||||
if (cp->handler) return 0;
|
||||
if (!decoder_detect(cp)) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->handler.func_data = ctxt;
|
||||
ctxt->handler.func_table = &hfuncs;
|
||||
ctxt->ctrl.ctxt = ctxt;
|
||||
ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
|
||||
ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
|
||||
ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
ctxt->stale_mask = (1 << (sizeof(decoder_ops)/
|
||||
sizeof(decoder_ops[0]))) - 1;
|
||||
hdw->decoder_ctrl = &ctxt->ctrl;
|
||||
cp->handler = &ctxt->handler;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
52
drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
Normal file
52
drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_V4L_H
|
||||
#define __PVRUSB2_VIDEO_V4L_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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
||||
#endif /* __PVRUSB2_VIDEO_V4L_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: ***
|
||||
*/
|
170
drivers/media/video/pvrusb2/pvrusb2-wm8775.c
Normal file
170
drivers/media/video/pvrusb2/pvrusb2-wm8775.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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 source file is specifically designed to interface with the
|
||||
wm8775.
|
||||
|
||||
*/
|
||||
|
||||
#include "pvrusb2-wm8775.h"
|
||||
#include "pvrusb2-i2c-cmd-v4l2.h"
|
||||
|
||||
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct pvr2_v4l_wm8775 {
|
||||
struct pvr2_i2c_handler handler;
|
||||
struct pvr2_i2c_client *client;
|
||||
struct pvr2_hdw *hdw;
|
||||
unsigned long stale_mask;
|
||||
};
|
||||
|
||||
|
||||
static void set_input(struct pvr2_v4l_wm8775 *ctxt)
|
||||
{
|
||||
struct v4l2_routing route;
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
int msk = 0;
|
||||
|
||||
memset(&route,0,sizeof(route));
|
||||
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)",
|
||||
hdw->input_val,msk);
|
||||
|
||||
// Always point to input #1 no matter what
|
||||
route.input = 2;
|
||||
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
|
||||
}
|
||||
|
||||
static int check_input(struct pvr2_v4l_wm8775 *ctxt)
|
||||
{
|
||||
struct pvr2_hdw *hdw = ctxt->hdw;
|
||||
return hdw->input_dirty != 0;
|
||||
}
|
||||
|
||||
|
||||
struct pvr2_v4l_wm8775_ops {
|
||||
void (*update)(struct pvr2_v4l_wm8775 *);
|
||||
int (*check)(struct pvr2_v4l_wm8775 *);
|
||||
};
|
||||
|
||||
|
||||
static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = {
|
||||
{ .update = set_input, .check = check_input},
|
||||
};
|
||||
|
||||
|
||||
static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt,
|
||||
char *buf,unsigned int cnt)
|
||||
{
|
||||
return scnprintf(buf,cnt,"handler: pvrusb2-wm8775");
|
||||
}
|
||||
|
||||
|
||||
static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt)
|
||||
{
|
||||
ctxt->client->handler = 0;
|
||||
kfree(ctxt);
|
||||
}
|
||||
|
||||
|
||||
static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (ctxt->stale_mask & msk) continue;
|
||||
if (wm8775_ops[idx].check(ctxt)) {
|
||||
ctxt->stale_mask |= msk;
|
||||
}
|
||||
}
|
||||
return ctxt->stale_mask != 0;
|
||||
}
|
||||
|
||||
|
||||
static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]);
|
||||
idx++) {
|
||||
msk = 1 << idx;
|
||||
if (!(ctxt->stale_mask & msk)) continue;
|
||||
ctxt->stale_mask &= ~msk;
|
||||
wm8775_ops[idx].update(ctxt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const static struct pvr2_i2c_handler_functions hfuncs = {
|
||||
.detach = (void (*)(void *))wm8775_detach,
|
||||
.check = (int (*)(void *))wm8775_check,
|
||||
.update = (void (*)(void *))wm8775_update,
|
||||
.describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe,
|
||||
};
|
||||
|
||||
|
||||
int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_v4l_wm8775 *ctxt;
|
||||
|
||||
if (cp->handler) return 0;
|
||||
|
||||
ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
|
||||
if (!ctxt) return 0;
|
||||
memset(ctxt,0,sizeof(*ctxt));
|
||||
|
||||
ctxt->handler.func_data = ctxt;
|
||||
ctxt->handler.func_table = &hfuncs;
|
||||
ctxt->client = cp;
|
||||
ctxt->hdw = hdw;
|
||||
ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/
|
||||
sizeof(wm8775_ops[0]))) - 1;
|
||||
cp->handler = &ctxt->handler;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up",
|
||||
cp->client->addr);
|
||||
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: ***
|
||||
*/
|
53
drivers/media/video/pvrusb2/pvrusb2-wm8775.h
Normal file
53
drivers/media/video/pvrusb2/pvrusb2-wm8775.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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_WM8775_H
|
||||
#define __PVRUSB2_WM8775_H
|
||||
|
||||
/*
|
||||
|
||||
This module connects the pvrusb2 driver to the I2C chip level
|
||||
driver which performs analog -> digital audio conversion for
|
||||
external audio inputs. This interface is used internally by the
|
||||
driver; higher level code should only interact through the
|
||||
interface provided by pvrusb2-hdw.h.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
|
||||
int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
||||
#endif /* __PVRUSB2_WM8775_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: ***
|
||||
*/
|
43
drivers/media/video/pvrusb2/pvrusb2.h
Normal file
43
drivers/media/video/pvrusb2/pvrusb2.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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: ***
|
||||
*/
|
Loading…
Reference in New Issue
Block a user