1
0
Fork 0

Merge master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb

* master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (26 commits)
  V4L/DVB (4263): Fix warning when compiling on 64 bit machines
  V4L/DVB (4261): Included required header for in-kernel compilation
  V4L/DVB (4260): Stradis.c: make 2 functions static
  V4L/DVB (4259): Pass an explicit log prefix to cx2341x_log_status
  V4L/DVB (4257): Fix 64-bit compile warnings.
  V4L/DVB (4255): Tda9887 default TOP value is 0x10
  V4L/DVB (4254): Remove obsoleted tuner_debug option.
  V4L/DVB (4253): IVTV VBI format description too long.
  V4L/DVB (4252): Remove duplicate 'tda9887' in info messages.
  V4L/DVB (4245): Reduce the amount of pvrusb2-sourced noise going into the system log
  V4L/DVB (4244): Implement use of cx2341x module in pvrusb2 driver
  V4L/DVB (4243): Exploit new V4L control features in pvrusb2
  V4L/DVB (4242): Don't suspend encoder when changing its attributes (in pvrusb2)
  V4L/DVB (4241): Fix faulty encoder error recovery in pvrusb2
  V4L/DVB (4240): Various V4L control enhancements in pvrusb2
  V4L/DVB (4239): Handle boolean controls in pvrusb2
  V4L/DVB (4238): Make sure flags field is initialized when quering a control in pvrusb2
  V4L/DVB (4237): Move LOG_STATUS bracketing to a different part of the pvrusb2 driver
  V4L/DVB (4236): Rearrange things in pvrusb2 driver in preparation for using cx2341x module
  V4L/DVB (4235): Increase the maximum number of controls that pvrusb2-sysfs.c can handle.
  ...
hifive-unleashed-5.1
Linus Torvalds 2006-06-27 18:22:13 -07:00
commit 12e56b601f
57 changed files with 13528 additions and 51 deletions

View 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

View File

@ -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

View File

@ -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

View File

@ -601,7 +601,7 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
}
int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params,
struct v4l2_ext_controls *ctrls, int cmd)
struct v4l2_ext_controls *ctrls, unsigned int cmd)
{
int err = 0;
int i;
@ -847,22 +847,22 @@ invalid:
return "<invalid>";
}
void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
{
int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
/* Stream */
printk(KERN_INFO "cx2341x-%d: Stream: %s\n",
card_id,
printk(KERN_INFO "%s: Stream: %s\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
/* Video */
printk(KERN_INFO "cx2341x-%d: Video: %dx%d, %d fps\n",
card_id,
printk(KERN_INFO "%s: Video: %dx%d, %d fps\n",
prefix,
p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
p->is_50hz ? 25 : 30);
printk(KERN_INFO "cx2341x-%d: Video: %s, %s, %s, %d",
card_id,
printk(KERN_INFO "%s: Video: %s, %s, %s, %d",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
@ -871,19 +871,19 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
printk(", Peak %d", p->video_bitrate_peak);
}
printk("\n");
printk(KERN_INFO "cx2341x-%d: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n",
card_id,
printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n",
prefix,
p->video_gop_size, p->video_b_frames,
p->video_gop_closure ? "" : "No ",
p->video_pulldown ? "" : "No ");
if (p->video_temporal_decimation) {
printk(KERN_INFO "cx2341x-%d: Video: Temporal Decimation %d\n",
card_id, p->video_temporal_decimation);
printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
prefix, p->video_temporal_decimation);
}
/* Audio */
printk(KERN_INFO "cx2341x-%d: Audio: %s, %s, %s, %s",
card_id,
printk(KERN_INFO "%s: Audio: %s, %s, %s, %s",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
@ -897,18 +897,18 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
/* Encoding filters */
printk(KERN_INFO "cx2341x-%d: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
card_id,
printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
p->video_spatial_filter);
printk(KERN_INFO "cx2341x-%d: Temporal Filter: %s, %d\n",
card_id,
printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
p->video_temporal_filter);
printk(KERN_INFO "cx2341x-%d: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
card_id,
printk(KERN_INFO "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
p->video_luma_median_filter_bottom,
p->video_luma_median_filter_top,

View File

@ -853,6 +853,19 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file,
fh->mpegq.field);
return 0;
}
case VIDIOC_LOG_STATUS:
{
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", core->name);
printk("%s/2: ============ START LOG STATUS ============\n",
core->name);
cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, 0);
cx2341x_log_status(&dev->params, name);
printk("%s/2: ============= END LOG STATUS =============\n",
core->name);
return 0;
}
default:
return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);

View 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.

View 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

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View File

@ -0,0 +1,593 @@
/*
*
* $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;
}
} else if (cptr->info->type != pvr2_ctl_bool) {
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 V4L ID for this control or zero if none */
int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
{
if (!cptr) return 0;
return cptr->info->v4l_id;
}
unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
{
unsigned int flags = 0;
if (cptr->info->get_v4lflags) {
flags = cptr->info->get_v4lflags(cptr);
}
if (cptr->info->set_value) {
flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
} else {
flags |= V4L2_CTRL_FLAG_READ_ONLY;
}
return flags;
}
/* 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 const char *boolNames[] = {
"false",
"true",
"no",
"yes",
};
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 1;
}
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 = -ERANGE;
}
if (maskptr) *maskptr = ~0;
} else if (cptr->info->type == pvr2_ctl_bool) {
ret = parse_token(
ptr,len,valptr,boolNames,
sizeof(boolNames)/sizeof(boolNames[0]));
if (ret == 1) {
*valptr = *valptr ? !0 : 0;
} else if (ret == 0) {
*valptr = (*valptr & 1) ? !0 : 0;
}
if (maskptr) *maskptr = 1;
} 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 = -ERANGE;
}
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_bool) {
*len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
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: ***
*/

View File

@ -0,0 +1,123 @@
/*
*
* $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,
pvr2_ctl_bool = 3,
};
/* 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 V4L flags value for control (or zero if there is no v4l control
actually under this control) */
unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *);
/* Return V4L ID for this control or zero if none */
int pvr2_ctrl_get_v4lid(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: ***
*/

View File

@ -0,0 +1,279 @@
/*
*
* $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 V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
val = 48000;
break;
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
val = 44100;
break;
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
val = 32000;
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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View File

@ -0,0 +1,418 @@
/*
*
* $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 "pvrusb2-util.h"
#include "pvrusb2-encoder.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
/* 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_encoder_write_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_encoder_read_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;
}
/* This prototype is set up to be compatible with the
cx2341x_mbox_func prototype in cx2341x.h, which should be in
kernels 2.6.18 or later. We do this so that we can enable
cx2341x.ko to write to our encoder (by handing it a pointer to this
function). For earlier kernels this doesn't really matter. */
static int pvr2_encoder_cmd(void *ctxt,
int cmd,
int arg_cnt_send,
int arg_cnt_recv,
u32 *argp)
{
unsigned int poll_count;
int ret = 0;
unsigned int idx;
/* These sizes look to be limited by the FX2 firmware implementation */
u32 wrData[16];
u32 rdData[16];
struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
/*
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").
*/
if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many input arguments"
" (was given %u limit %u)",
arg_cnt_send,
(unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4);
return -EINVAL;
}
if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many return arguments"
" (was given %u limit %u)",
arg_cnt_recv,
(unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4);
return -EINVAL;
}
LOCK_TAKE(hdw->ctl_lock); do {
wrData[0] = 0;
wrData[1] = cmd;
wrData[2] = 0;
wrData[3] = 0x00060000;
for (idx = 0; idx < arg_cnt_send; idx++) {
wrData[idx+4] = argp[idx];
}
for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) {
wrData[idx+4] = 0;
}
ret = pvr2_encoder_write_words(hdw,wrData,idx);
if (ret) break;
wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
ret = pvr2_encoder_write_words(hdw,wrData,1);
if (ret) break;
poll_count = 0;
while (1) {
if (poll_count < 10000000) poll_count++;
ret = pvr2_encoder_read_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 < arg_cnt_send; 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_encoder_read_words(
hdw,0,rdData,
sizeof(rdData)/sizeof(rdData[0]));
if (ret) break;
for (idx = 0; idx < arg_cnt_recv; idx++) {
argp[idx] = rdData[idx+4];
}
wrData[0] = 0x0;
ret = pvr2_encoder_write_words(hdw,wrData,1);
if (ret) break;
} while(0); LOCK_GIVE(hdw->ctl_lock);
return ret;
}
static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
int args, ...)
{
va_list vl;
unsigned int idx;
u32 data[12];
if (args > sizeof(data)/sizeof(data[0])) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many arguments"
" (was given %u limit %u)",
args,(unsigned int)(sizeof(data)/sizeof(data[0])));
return -EINVAL;
}
va_start(vl, args);
for (idx = 0; idx < args; idx++) {
data[idx] = va_arg(vl, u32);
}
va_end(vl);
return pvr2_encoder_cmd(hdw,cmd,args,0,data);
}
int pvr2_encoder_configure(struct pvr2_hdw *hdw)
{
int ret;
pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
" (cx2341x module)");
hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
hdw->enc_ctl_state.width = hdw->res_hor_val;
hdw->enc_ctl_state.height = hdw->res_ver_val;
hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
(V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
0 : 1);
ret = 0;
if (!ret) ret = pvr2_encoder_vcmd(
hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
0xf0, 0xf0);
/* setup firmware to notify us about some events (don't know why...) */
if (!ret) ret = pvr2_encoder_vcmd(
hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
0, 0, 0x10000000, 0xffffffff);
if (!ret) ret = pvr2_encoder_vcmd(
hdw,CX2341X_ENC_SET_VBI_LINE, 5,
0xffffffff,0,0,0,0);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Failed to configure cx32416");
return ret;
}
ret = cx2341x_update(hdw,pvr2_encoder_cmd,
(hdw->enc_cur_valid ? &hdw->enc_cur_state : 0),
&hdw->enc_ctl_state);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Error from cx2341x module code=%d",ret);
return ret;
}
ret = 0;
if (!ret) ret = pvr2_encoder_vcmd(
hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Failed to initialize cx32416 video input");
return ret;
}
hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
sizeof(struct cx2341x_mpeg_params));
hdw->enc_cur_valid = !0;
return 0;
}
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_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0x01,0x14);
} else if (hdw->config == pvr2_config_mpeg) {
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0,0x13);
} else {
status = pvr2_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_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0x01,0x14);
} else if (hdw->config == pvr2_config_mpeg) {
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0,0x13);
} else {
status = pvr2_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: ***
*/

View 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: ***
*/

View File

@ -0,0 +1,384 @@
/*
*
* $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"
#include <media/cx2341x.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);
typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *);
/* 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 */
pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */
/* 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;
};
/* Same as pvr2_ctl_info, but includes storage for the control description */
#define PVR2_CTLD_INFO_DESC_SIZE 32
struct pvr2_ctld_info {
struct pvr2_ctl_info info;
char desc[PVR2_CTLD_INFO_DESC_SIZE];
};
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 needed for cx2341x module */
struct cx2341x_mpeg_params enc_cur_state;
struct cx2341x_mpeg_params enc_ctl_state;
/* True if an encoder attribute has changed */
int enc_stale;
/* True if enc_cur_state is valid */
int enc_cur_valid;
/* 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(input);
VCREATE_DATA(audiomode);
VCREATE_DATA(res_hor);
VCREATE_DATA(res_ver);
VCREATE_DATA(srate);
#undef VCREATE_DATA
struct pvr2_ctld_info *mpeg_ctrl_info;
struct pvr2_ctrl *controls;
unsigned int control_cnt;
};
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: ***
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,335 @@
/*
*
* $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 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
/* 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);
/* Retrieve a control handle given its immediate predecessor V4L ID (if any) */
struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View File

@ -0,0 +1,172 @@
/*
*
* $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| \
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: ***
*/

View File

@ -0,0 +1,408 @@
/*
*
* $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"
#include <asm/string.h>
#include <linux/slab.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: ***
*/

View 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: ***
*/

View File

@ -0,0 +1,865 @@
/*
*
* $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_type;
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[7];
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_type(int id,struct class_device *class_dev,char *buf)
{
struct pvr2_ctrl *cptr;
struct pvr2_sysfs *sfp;
const char *name;
enum pvr2_ctl_type tp;
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;
tp = pvr2_ctrl_get_type(cptr);
switch (tp) {
case pvr2_ctl_int: name = "integer"; break;
case pvr2_ctl_enum: name = "enum"; break;
case pvr2_ctl_bitmask: name = "bitmask"; break;
case pvr2_ctl_bool: name = "boolean"; break;
default: name = "?"; break;
}
pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(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);
if (!ccnt) continue;
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_type,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)
CREATE_BATCH(34)
CREATE_BATCH(35)
CREATE_BATCH(36)
CREATE_BATCH(37)
CREATE_BATCH(38)
CREATE_BATCH(39)
CREATE_BATCH(40)
CREATE_BATCH(41)
CREATE_BATCH(42)
CREATE_BATCH(43)
CREATE_BATCH(44)
CREATE_BATCH(45)
CREATE_BATCH(46)
CREATE_BATCH(47)
CREATE_BATCH(48)
CREATE_BATCH(49)
CREATE_BATCH(50)
CREATE_BATCH(51)
CREATE_BATCH(52)
CREATE_BATCH(53)
CREATE_BATCH(54)
CREATE_BATCH(55)
CREATE_BATCH(56)
CREATE_BATCH(57)
CREATE_BATCH(58)
CREATE_BATCH(59)
struct pvr2_sysfs_func_set {
ssize_t (*show_name)(struct class_device *,char *);
ssize_t (*show_type)(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_type = show_type_##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),
INIT_BATCH(34),
INIT_BATCH(35),
INIT_BATCH(36),
INIT_BATCH(37),
INIT_BATCH(38),
INIT_BATCH(39),
INIT_BATCH(40),
INIT_BATCH(41),
INIT_BATCH(42),
INIT_BATCH(43),
INIT_BATCH(44),
INIT_BATCH(45),
INIT_BATCH(46),
INIT_BATCH(47),
INIT_BATCH(48),
INIT_BATCH(49),
INIT_BATCH(50),
INIT_BATCH(51),
INIT_BATCH(52),
INIT_BATCH(53),
INIT_BATCH(54),
INIT_BATCH(55),
INIT_BATCH(56),
INIT_BATCH(57),
INIT_BATCH(58),
INIT_BATCH(59),
};
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_type.attr.owner = THIS_MODULE;
cip->attr_type.attr.name = "type";
cip->attr_type.attr.mode = S_IRUGO;
cip->attr_type.show = fp->show_type;
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_type.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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

File diff suppressed because it is too large Load Diff

View 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: ***
*/

View File

@ -0,0 +1,253 @@
/*
*
* $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 V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
val = 48000;
break;
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
val = 44100;
break;
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
val = 32000;
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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View 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: ***
*/

View File

@ -432,10 +432,10 @@ static void saa6752hs_old_set_params(struct i2c_client* client,
}
static int handle_ctrl(struct saa6752hs_mpeg_params *params,
struct v4l2_ext_control *ctrl, int cmd)
struct v4l2_ext_control *ctrl, unsigned int cmd)
{
int old = 0, new;
int set = cmd == VIDIOC_S_EXT_CTRLS;
int set = (cmd == VIDIOC_S_EXT_CTRLS);
new = ctrl->value;
switch (ctrl->id) {

View File

@ -2190,7 +2190,7 @@ static struct pci_driver stradis_driver = {
.remove = __devexit_p(stradis_remove)
};
int __init stradis_init(void)
static int __init stradis_init(void)
{
int retval;
@ -2203,7 +2203,7 @@ int __init stradis_init(void)
return retval;
}
void __exit stradis_exit(void)
static void __exit stradis_exit(void)
{
pci_unregister_driver(&stradis_driver);
printk(KERN_INFO "stradis: module cleanup complete\n");

View File

@ -22,11 +22,11 @@
*/
#define tda9887_info(fmt, arg...) do {\
printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \
printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
#define tda9887_dbg(fmt, arg...) do {\
if (tuner_debug) \
printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \
printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
@ -84,8 +84,7 @@ struct tvnorm {
#define cAudioGain6 0x80 // bit c7
#define cTopMask 0x1f // bit c0:4
#define cTopPalSecamDefault 0x14 // bit c0:4
#define cTopNtscRadioDefault 0x10 // bit c0:4
#define cTopDefault 0x10 // bit c0:4
//// third reg (e)
#define cAudioIF_4_5 0x00 // bit e0:1
@ -123,7 +122,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
cTopPalSecamDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_5_5 |
cVideoIF_38_90 ),
@ -134,7 +133,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
cTopPalSecamDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_0 |
cVideoIF_38_90 ),
@ -145,7 +144,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
cTopPalSecamDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@ -156,7 +155,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
cTopNtscRadioDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ),
@ -165,7 +164,7 @@ static struct tvnorm tvnorms[] = {
.name = "SECAM-BGH",
.b = ( cPositiveAmTV |
cQSS ),
.c = ( cTopPalSecamDefault),
.c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_5_5 |
cVideoIF_38_90 ),
@ -174,7 +173,7 @@ static struct tvnorm tvnorms[] = {
.name = "SECAM-L",
.b = ( cPositiveAmTV |
cQSS ),
.c = ( cTopPalSecamDefault),
.c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@ -184,7 +183,7 @@ static struct tvnorm tvnorms[] = {
.b = ( cOutputPort2Inactive |
cPositiveAmTV |
cQSS ),
.c = ( cTopPalSecamDefault),
.c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_33_90 ),
@ -195,7 +194,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
cTopPalSecamDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@ -206,7 +205,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
cTopNtscRadioDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ),
@ -217,7 +216,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
cTopNtscRadioDefault),
cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_58_75 ),
@ -230,7 +229,7 @@ static struct tvnorm radio_stereo = {
cQSS ),
.c = ( cDeemphasisOFF |
cAudioGain6 |
cTopNtscRadioDefault),
cTopDefault),
.e = ( cTunerGainLow |
cAudioIF_5_5 |
cRadioIF_38_90 ),
@ -242,7 +241,7 @@ static struct tvnorm radio_mono = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
cTopNtscRadioDefault),
cTopDefault),
.e = ( cTunerGainLow |
cAudioIF_5_5 |
cRadioIF_38_90 ),

View File

@ -40,7 +40,6 @@ static unsigned int no_autodetect = 0;
static unsigned int show_i2c = 0;
/* insmod options used at runtime => read/write */
static unsigned int tuner_debug_old = 0;
int tuner_debug = 0;
static unsigned int tv_range[2] = { 44, 958 };
@ -54,8 +53,6 @@ static char ntsc[] = "-";
module_param(addr, int, 0444);
module_param(no_autodetect, int, 0444);
module_param(show_i2c, int, 0444);
/* Note: tuner_debug is deprecated and will be removed in 2.6.17 */
module_param_named(tuner_debug,tuner_debug_old, int, 0444);
module_param_named(debug,tuner_debug, int, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
@ -442,11 +439,6 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
t->audmode = V4L2_TUNER_MODE_STEREO;
t->mode_mask = T_UNINITIALIZED;
t->tuner_status = tuner_status;
if (tuner_debug_old) {
tuner_debug = tuner_debug_old;
printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n");
printk(KERN_ERR "tuner: use the debug option instead.\n");
}
if (show_i2c) {
unsigned char buffer[16];

View File

@ -1103,7 +1103,7 @@ const char **v4l2_ctrl_get_menu(u32 id)
};
static const char *mpeg_stream_vbi_fmt[] = {
"No VBI",
"VBI in private packets, IVTV format",
"Private packet, IVTV format",
NULL
};

View File

@ -89,9 +89,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
struct v4l2_queryctrl *qctrl);
const char **cx2341x_ctrl_get_menu(u32 id);
int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params,
struct v4l2_ext_controls *ctrls, int cmd);
struct v4l2_ext_controls *ctrls, unsigned int cmd);
void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p);
void cx2341x_log_status(struct cx2341x_mpeg_params *p, int cardid);
void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix);
/* Firmware names */
#define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw"