[media] rename most media/video usb drivers to media/usb

Rename all USB drivers with their own directory under
drivers/media/video into drivers/media/usb and update the
building system.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
此提交包含在:
Mauro Carvalho Chehab
2012-08-14 00:13:22 -03:00
父節點 84cfe9e79b
當前提交 0c0d06cac6
共有 288 個檔案被更改,包括 68 行新增80 行删除

查看文件

@@ -0,0 +1,65 @@
config VIDEO_PVRUSB2
tristate "Hauppauge WinTV-PVR USB2 support"
depends on VIDEO_V4L2 && I2C
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
select VIDEO_SAA711X
select VIDEO_CX25840
select VIDEO_MSP3400
select VIDEO_WM8775
select VIDEO_CS53L32A
---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_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_DVB
bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
default y
depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_TDA10048 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
---help---
This option enables a DVB interface for the pvrusb2 driver.
If your device does not support digital television, this
feature will have no affect on the driver's operation.
If you are in doubt, say Y.
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.
If you are in doubt, say N.

查看文件

@@ -0,0 +1,22 @@
obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
pvrusb2-objs := pvrusb2-i2c-core.o \
pvrusb2-audio.o \
pvrusb2-encoder.o pvrusb2-video-v4l.o \
pvrusb2-eeprom.o \
pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
pvrusb2-cs53l32a.o \
$(obj-pvrusb2-dvb-y) \
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
ccflags-y += -Idrivers/media/video
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends

查看文件

@@ -0,0 +1,96 @@
/*
*
*
* 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 routing_scheme {
const int *def;
unsigned int cnt;
};
static const int routing_scheme0[] = {
[PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT,
[PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2,
MSP_IN_TUNER1,
MSP_DSP_IN_SCART,
MSP_DSP_IN_SCART),
[PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1,
MSP_IN_TUNER1,
MSP_DSP_IN_SCART,
MSP_DSP_IN_SCART),
[PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1,
MSP_IN_TUNER1,
MSP_DSP_IN_SCART,
MSP_DSP_IN_SCART),
};
static const struct routing_scheme routing_def0 = {
.def = routing_scheme0,
.cnt = ARRAY_SIZE(routing_scheme0),
};
static const struct routing_scheme *routing_schemes[] = {
[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
};
void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
{
if (hdw->input_dirty || hdw->force_dirty) {
const struct routing_scheme *sp;
unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
u32 input;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo");
sp = (sid < ARRAY_SIZE(routing_schemes)) ?
routing_schemes[sid] : NULL;
if ((sp != NULL) &&
(hdw->input_val >= 0) &&
(hdw->input_val < sp->cnt)) {
input = sp->def[hdw->input_val];
} else {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"*** WARNING *** subdev msp3400 set_input:"
" Invalid routing scheme (%u)"
" and/or input (%d)",
sid, hdw->input_val);
return;
}
sd->ops->audio->s_routing(sd, input,
MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 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: ***
*/

查看文件

@@ -0,0 +1,37 @@
/*
*
*
* 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-hdw-internal.h"
void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
#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: ***
*/

查看文件

@@ -0,0 +1,431 @@
/*
*
* 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/wait.h>
#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
static struct pvr2_context *pvr2_context_exist_first;
static struct pvr2_context *pvr2_context_exist_last;
static struct pvr2_context *pvr2_context_notify_first;
static struct pvr2_context *pvr2_context_notify_last;
static DEFINE_MUTEX(pvr2_context_mutex);
static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
static int pvr2_context_cleanup_flag;
static int pvr2_context_cleaned_flag;
static struct task_struct *pvr2_context_thread_ptr;
static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
{
int signal_flag = 0;
mutex_lock(&pvr2_context_mutex);
if (fl) {
if (!mp->notify_flag) {
signal_flag = (pvr2_context_notify_first == NULL);
mp->notify_prev = pvr2_context_notify_last;
mp->notify_next = NULL;
pvr2_context_notify_last = mp;
if (mp->notify_prev) {
mp->notify_prev->notify_next = mp;
} else {
pvr2_context_notify_first = mp;
}
mp->notify_flag = !0;
}
} else {
if (mp->notify_flag) {
mp->notify_flag = 0;
if (mp->notify_next) {
mp->notify_next->notify_prev = mp->notify_prev;
} else {
pvr2_context_notify_last = mp->notify_prev;
}
if (mp->notify_prev) {
mp->notify_prev->notify_next = mp->notify_next;
} else {
pvr2_context_notify_first = mp->notify_next;
}
}
}
mutex_unlock(&pvr2_context_mutex);
if (signal_flag) wake_up(&pvr2_context_sync_data);
}
static void pvr2_context_destroy(struct pvr2_context *mp)
{
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
pvr2_context_set_notify(mp, 0);
mutex_lock(&pvr2_context_mutex);
if (mp->exist_next) {
mp->exist_next->exist_prev = mp->exist_prev;
} else {
pvr2_context_exist_last = mp->exist_prev;
}
if (mp->exist_prev) {
mp->exist_prev->exist_next = mp->exist_next;
} else {
pvr2_context_exist_first = mp->exist_next;
}
if (!pvr2_context_exist_first) {
/* Trigger wakeup on control thread in case it is waiting
for an exit condition. */
wake_up(&pvr2_context_sync_data);
}
mutex_unlock(&pvr2_context_mutex);
kfree(mp);
}
static void pvr2_context_notify(struct pvr2_context *mp)
{
pvr2_context_set_notify(mp,!0);
}
static void pvr2_context_check(struct pvr2_context *mp)
{
struct pvr2_channel *ch1, *ch2;
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (notify)", mp);
if (!mp->initialized_flag && !mp->disconnect_flag) {
mp->initialized_flag = !0;
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (initialize)", mp);
/* Finish hardware initialization */
if (pvr2_hdw_initialize(mp->hdw,
(void (*)(void *))pvr2_context_notify,
mp)) {
mp->video_stream.stream =
pvr2_hdw_get_video_stream(mp->hdw);
/* Trigger interface initialization. By doing this
here initialization runs in our own safe and
cozy thread context. */
if (mp->setup_func) mp->setup_func(mp);
} else {
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (thread skipping setup)",
mp);
/* Even though initialization did not succeed,
we're still going to continue anyway. We need
to do this in order to await the expected
disconnect (which we will detect in the normal
course of operation). */
}
}
for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
ch2 = ch1->mc_next;
if (ch1->check_func) ch1->check_func(ch1);
}
if (mp->disconnect_flag && !mp->mc_first) {
/* Go away... */
pvr2_context_destroy(mp);
return;
}
}
static int pvr2_context_shutok(void)
{
return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
}
static int pvr2_context_thread_func(void *foo)
{
struct pvr2_context *mp;
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
do {
while ((mp = pvr2_context_notify_first) != NULL) {
pvr2_context_set_notify(mp, 0);
pvr2_context_check(mp);
}
wait_event_interruptible(
pvr2_context_sync_data,
((pvr2_context_notify_first != NULL) ||
pvr2_context_shutok()));
} while (!pvr2_context_shutok());
pvr2_context_cleaned_flag = !0;
wake_up(&pvr2_context_cleanup_data);
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
wait_event_interruptible(
pvr2_context_sync_data,
kthread_should_stop());
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
return 0;
}
int pvr2_context_global_init(void)
{
pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
NULL,
"pvrusb2-context");
return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
}
void pvr2_context_global_done(void)
{
pvr2_context_cleanup_flag = !0;
wake_up(&pvr2_context_sync_data);
wait_event_interruptible(
pvr2_context_cleanup_data,
pvr2_context_cleaned_flag);
kthread_stop(pvr2_context_thread_ptr);
}
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 = NULL;
mp = kzalloc(sizeof(*mp),GFP_KERNEL);
if (!mp) goto done;
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
mp->setup_func = setup_func;
mutex_init(&mp->mutex);
mutex_lock(&pvr2_context_mutex);
mp->exist_prev = pvr2_context_exist_last;
mp->exist_next = NULL;
pvr2_context_exist_last = mp;
if (mp->exist_prev) {
mp->exist_prev->exist_next = mp;
} else {
pvr2_context_exist_first = mp;
}
mutex_unlock(&pvr2_context_mutex);
mp->hdw = pvr2_hdw_create(intf,devid);
if (!mp->hdw) {
pvr2_context_destroy(mp);
mp = NULL;
goto done;
}
pvr2_context_set_notify(mp, !0);
done:
return mp;
}
static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
{
unsigned int tmsk,mmsk;
struct pvr2_channel *cp;
struct pvr2_hdw *hdw = mp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
tmsk = mmsk;
for (cp = mp->mc_first; cp; cp = cp->mc_next) {
if (!cp->input_mask) continue;
tmsk &= cp->input_mask;
}
pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
pvr2_hdw_commit_ctl(hdw);
}
static void pvr2_context_enter(struct pvr2_context *mp)
{
mutex_lock(&mp->mutex);
}
static void pvr2_context_exit(struct pvr2_context *mp)
{
int destroy_flag = 0;
if (!(mp->mc_first || !mp->disconnect_flag)) {
destroy_flag = !0;
}
mutex_unlock(&mp->mutex);
if (destroy_flag) pvr2_context_notify(mp);
}
void pvr2_context_disconnect(struct pvr2_context *mp)
{
pvr2_hdw_disconnect(mp->hdw);
mp->disconnect_flag = !0;
pvr2_context_notify(mp);
}
void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
{
pvr2_context_enter(mp);
cp->hdw = mp->hdw;
cp->mc_head = mp;
cp->mc_next = NULL;
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;
pvr2_context_exit(mp);
}
static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
{
if (!cp->stream) return;
pvr2_stream_kill(cp->stream->stream);
cp->stream->user = NULL;
cp->stream = NULL;
}
void pvr2_channel_done(struct pvr2_channel *cp)
{
struct pvr2_context *mp = cp->mc_head;
pvr2_context_enter(mp);
cp->input_mask = 0;
pvr2_channel_disclaim_stream(cp);
pvr2_context_reset_input_limits(mp);
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 = NULL;
pvr2_context_exit(mp);
}
int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
{
unsigned int tmsk,mmsk;
int ret = 0;
struct pvr2_channel *p2;
struct pvr2_hdw *hdw = cp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
cmsk &= mmsk;
if (cmsk == cp->input_mask) {
/* No change; nothing to do */
return 0;
}
pvr2_context_enter(cp->mc_head);
do {
if (!cmsk) {
cp->input_mask = 0;
pvr2_context_reset_input_limits(cp->mc_head);
break;
}
tmsk = mmsk;
for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
if (p2 == cp) continue;
if (!p2->input_mask) continue;
tmsk &= p2->input_mask;
}
if (!(tmsk & cmsk)) {
ret = -EPERM;
break;
}
tmsk &= cmsk;
if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
/* Internal failure changing allowed list; probably
should not happen, but react if it does. */
break;
}
cp->input_mask = cmsk;
pvr2_hdw_commit_ctl(hdw);
} while (0);
pvr2_context_exit(cp->mc_head);
return ret;
}
unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
{
return cp->input_mask;
}
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 && 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 NULL;
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: ***
*/

查看文件

@@ -0,0 +1,94 @@
/*
*
* 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_CONTEXT_H
#define __PVRUSB2_CONTEXT_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_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_context *exist_next;
struct pvr2_context *exist_prev;
struct pvr2_context *notify_next;
struct pvr2_context *notify_prev;
struct pvr2_hdw *hdw;
struct pvr2_context_stream video_stream;
struct mutex mutex;
int notify_flag;
int initialized_flag;
int disconnect_flag;
/* Called after pvr2_context initialization is complete */
void (*setup_func)(struct pvr2_context *);
};
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;
unsigned int input_mask;
void (*check_func)(struct pvr2_channel *);
};
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_limit_inputs(struct pvr2_channel *,unsigned int);
unsigned int pvr2_channel_get_limited_inputs(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 *);
int pvr2_context_global_init(void);
void pvr2_context_global_done(void);
#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: ***
*/

查看文件

@@ -0,0 +1,95 @@
/*
*
*
* 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
v4l-dvb cs53l32a module.
*/
#include "pvrusb2-cs53l32a.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/errno.h>
struct routing_scheme {
const int *def;
unsigned int cnt;
};
static const int routing_scheme1[] = {
[PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */
[PVR2_CVAL_INPUT_RADIO] = 2,
[PVR2_CVAL_INPUT_COMPOSITE] = 0,
[PVR2_CVAL_INPUT_SVIDEO] = 0,
};
static const struct routing_scheme routing_def1 = {
.def = routing_scheme1,
.cnt = ARRAY_SIZE(routing_scheme1),
};
static const struct routing_scheme *routing_schemes[] = {
[PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1,
};
void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
{
if (hdw->input_dirty || hdw->force_dirty) {
const struct routing_scheme *sp;
unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
u32 input;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
hdw->input_val);
sp = (sid < ARRAY_SIZE(routing_schemes)) ?
routing_schemes[sid] : NULL;
if ((sp == NULL) ||
(hdw->input_val < 0) ||
(hdw->input_val >= sp->cnt)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"*** WARNING *** subdev v4l2 set_input:"
" Invalid routing scheme (%u)"
" and/or input (%d)",
sid, hdw->input_val);
return;
}
input = sp->def[hdw->input_val];
sd->ops->audio->s_routing(sd, input, 0, 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: ***
*/

查看文件

@@ -0,0 +1,48 @@
/*
*
*
* 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_CS53L32A_H
#define __PVRUSB2_CS53L32A_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-hdw-internal.h"
void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
#endif /* __PVRUSB2_AUDIO_CS53L32A_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: ***
*/

查看文件

@@ -0,0 +1,609 @@
/*
*
*
* 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>
static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val)
{
if (cptr->info->check_value) {
if (!cptr->info->check_value(cptr,val)) return -ERANGE;
} else if (cptr->info->type == pvr2_ctl_enum) {
if (val < 0) return -ERANGE;
if (val >= cptr->info->def.type_enum.count) return -ERANGE;
} else {
int lim;
lim = cptr->info->def.type_int.min_value;
if (cptr->info->get_min_value) {
cptr->info->get_min_value(cptr,&lim);
}
if (val < lim) return -ERANGE;
lim = cptr->info->def.type_int.max_value;
if (cptr->info->get_max_value) {
cptr->info->get_max_value(cptr,&lim);
}
if (val > lim) return -ERANGE;
}
return 0;
}
/* 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) {
if (cptr->info->type == pvr2_ctl_bitmask) {
mask &= cptr->info->def.type_bitmask.valid_bits;
} else if ((cptr->info->type == pvr2_ctl_int)||
(cptr->info->type == pvr2_ctl_enum)) {
ret = pvr2_ctrl_range_check(cptr,val);
if (ret < 0) 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->get_max_value) {
cptr->info->get_max_value(cptr,&ret);
} else 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->get_min_value) {
cptr->info->get_min_value(cptr,&ret);
} else 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 *valptr)
{
int ret = 0;
if (!cptr) return -EINVAL;
LOCK_TAKE(cptr->hdw->big_lock); do {
if (cptr->info->get_def_value) {
ret = cptr->info->get_def_value(cptr, valptr);
} else {
*valptr = 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 NULL;
return cptr->info->name;
}
/* Retrieve the control's desc */
const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
{
if (!cptr) return NULL;
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 * const *names;
names = cptr->info->def.type_enum.value_names;
if (pvr2_ctrl_range_check(cptr,val) == 0) {
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 != NULL;
}
/* 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 * const *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,NULL,0);
if (ret >= 0) {
ret = pvr2_ctrl_range_check(cptr,*valptr);
}
*maskptr = ~0;
} else if (cptr->info->type == pvr2_ctl_bool) {
ret = parse_token(ptr,len,valptr,boolNames,
ARRAY_SIZE(boolNames));
if (ret == 1) {
*valptr = *valptr ? !0 : 0;
} else if (ret == 0) {
*valptr = (*valptr & 1) ? !0 : 0;
}
*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) {
ret = pvr2_ctrl_range_check(cptr,*valptr);
}
*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 * const *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: ***
*/

查看文件

@@ -0,0 +1,122 @@
/*
*
*
* 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 *, int *valptr);
/* 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: ***
*/

查看文件

@@ -0,0 +1,166 @@
/*
*
*
* 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-hdw-internal.h"
#include "pvrusb2-debug.h"
#include <media/cx25840.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/errno.h>
struct routing_scheme_item {
int vid;
int aud;
};
struct routing_scheme {
const struct routing_scheme_item *def;
unsigned int cnt;
};
static const struct routing_scheme_item routing_scheme0[] = {
[PVR2_CVAL_INPUT_TV] = {
.vid = CX25840_COMPOSITE7,
.aud = CX25840_AUDIO8,
},
[PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
.vid = CX25840_COMPOSITE3,
.aud = CX25840_AUDIO_SERIAL,
},
[PVR2_CVAL_INPUT_COMPOSITE] = {
.vid = CX25840_COMPOSITE3,
.aud = CX25840_AUDIO_SERIAL,
},
[PVR2_CVAL_INPUT_SVIDEO] = {
.vid = CX25840_SVIDEO1,
.aud = CX25840_AUDIO_SERIAL,
},
};
static const struct routing_scheme routing_def0 = {
.def = routing_scheme0,
.cnt = ARRAY_SIZE(routing_scheme0),
};
/* Specific to gotview device */
static const struct routing_scheme_item routing_schemegv[] = {
[PVR2_CVAL_INPUT_TV] = {
.vid = CX25840_COMPOSITE2,
.aud = CX25840_AUDIO5,
},
[PVR2_CVAL_INPUT_RADIO] = {
/* line-in is used for radio and composite. A GPIO is
used to switch between the two choices. */
.vid = CX25840_COMPOSITE1,
.aud = CX25840_AUDIO_SERIAL,
},
[PVR2_CVAL_INPUT_COMPOSITE] = {
.vid = CX25840_COMPOSITE1,
.aud = CX25840_AUDIO_SERIAL,
},
[PVR2_CVAL_INPUT_SVIDEO] = {
.vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4),
.aud = CX25840_AUDIO_SERIAL,
},
};
static const struct routing_scheme routing_defgv = {
.def = routing_schemegv,
.cnt = ARRAY_SIZE(routing_schemegv),
};
/* Specific to grabster av400 device */
static const struct routing_scheme_item routing_schemeav400[] = {
[PVR2_CVAL_INPUT_COMPOSITE] = {
.vid = CX25840_COMPOSITE1,
.aud = CX25840_AUDIO_SERIAL,
},
[PVR2_CVAL_INPUT_SVIDEO] = {
.vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4),
.aud = CX25840_AUDIO_SERIAL,
},
};
static const struct routing_scheme routing_defav400 = {
.def = routing_schemeav400,
.cnt = ARRAY_SIZE(routing_schemeav400),
};
static const struct routing_scheme *routing_schemes[] = {
[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
[PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv,
[PVR2_ROUTING_SCHEME_AV400] = &routing_defav400,
};
void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
{
pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update...");
if (hdw->input_dirty || hdw->force_dirty) {
enum cx25840_video_input vid_input;
enum cx25840_audio_input aud_input;
const struct routing_scheme *sp;
unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
sp = (sid < ARRAY_SIZE(routing_schemes)) ?
routing_schemes[sid] : NULL;
if ((sp == NULL) ||
(hdw->input_val < 0) ||
(hdw->input_val >= sp->cnt)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"*** WARNING *** subdev cx2584x set_input:"
" Invalid routing scheme (%u)"
" and/or input (%d)",
sid, hdw->input_val);
return;
}
vid_input = sp->def[hdw->input_val].vid;
aud_input = sp->def[hdw->input_val].aud;
pvr2_trace(PVR2_TRACE_CHIPS,
"subdev cx2584x set_input vid=0x%x aud=0x%x",
vid_input, aud_input);
sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0);
sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 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: ***
*/

查看文件

@@ -0,0 +1,52 @@
/*
*
*
* 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-hdw-internal.h"
void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
#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: ***
*/

查看文件

@@ -0,0 +1,69 @@
/*
*
* 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 app misbehavior */
#define PVR2_TRACE_STD (1 << 4) /* Log video standard stuff */
#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */
#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */
#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */
#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */
#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */
#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */
#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */
#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */
#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */
#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */
#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */
#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */
#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */
#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */
#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */
#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */
#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */
#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */
#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: ***
*/

查看文件

@@ -0,0 +1,335 @@
/*
*
*
* 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 "pvrusb2-debugifc.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-debug.h"
struct debugifc_mask_item {
const char *name;
unsigned long msk;
};
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 = NULL;
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;
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--) {
int val = hex_to_bin(*buf++);
if (val < 0 || 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);
}
int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
{
int bcnt = 0;
int ccnt;
ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n",
pvr2_hdw_get_desc(hdw));
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = scnprintf(buf,acnt,"Driver state info:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = pvr2_hdw_state_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;
int ret;
u32 gpio_dir,gpio_in,gpio_out;
struct pvr2_stream_stats stats;
struct pvr2_stream *sp;
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;
sp = pvr2_hdw_get_video_stream(hdw);
if (sp) {
pvr2_stream_get_stats(sp, &stats, 0);
ccnt = scnprintf(
buf,acnt,
"Bytes streamed=%u"
" URBs: queued=%u idle=%u ready=%u"
" processed=%u failed=%u\n",
stats.bytes_processed,
stats.buffers_in_queue,
stats.buffers_in_idle,
stats.buffers_in_ready,
stats.buffers_processed,
stats.buffers_failed);
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
}
return bcnt;
}
static 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);
} else if (debugifc_match_keyword(wptr,wlen,"worker")) {
return pvr2_hdw_untrip(hdw);
} else if (debugifc_match_keyword(wptr,wlen,"usbstats")) {
pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw),
NULL, !0);
return 0;
}
return -EINVAL;
} 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")) {
scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
if (scnt && wptr) {
count -= scnt; buf += scnt;
if (debugifc_match_keyword(wptr, wlen,
"prom")) {
pvr2_hdw_cpufw_set_enabled(hdw, 2, !0);
} else if (debugifc_match_keyword(wptr, wlen,
"ram8k")) {
pvr2_hdw_cpufw_set_enabled(hdw, 0, !0);
} else if (debugifc_match_keyword(wptr, wlen,
"ram16k")) {
pvr2_hdw_cpufw_set_enabled(hdw, 1, !0);
} else {
return -EINVAL;
}
}
pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
return 0;
} else if (debugifc_match_keyword(wptr,wlen,"done")) {
pvr2_hdw_cpufw_set_enabled(hdw,0,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: ***
*/

查看文件

@@ -0,0 +1,52 @@
/*
*
*
* 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;
/* 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_info(struct pvr2_hdw *,
char *buf_ptr, unsigned int buf_size);
/* 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_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: ***
*/

查看文件

@@ -0,0 +1,576 @@
/*
*
*
* Copyright (C) 2007 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
*
*/
/*
This source file should encompass ALL per-device type information for the
driver. To define a new device, add elements to the pvr2_device_table and
pvr2_device_desc structures.
*/
#include "pvrusb2-devattr.h"
#include <linux/usb.h>
#include <linux/module.h>
/* This is needed in order to pull in tuner type ids... */
#include <linux/i2c.h>
#include <media/tuner.h>
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
#include "pvrusb2-hdw-internal.h"
#include "lgdt330x.h"
#include "s5h1409.h"
#include "s5h1411.h"
#include "tda10048.h"
#include "tda18271.h"
#include "tda8290.h"
#include "tuner-simple.h"
#endif
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 29xxx */
static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = {
{ .module_id = PVR2_CLIENT_ID_SAA7115 },
{ .module_id = PVR2_CLIENT_ID_MSP3400 },
{ .module_id = PVR2_CLIENT_ID_TUNER },
{ .module_id = PVR2_CLIENT_ID_DEMOD },
};
#define PVR2_FIRMWARE_29xxx "v4l-pvrusb2-29xxx-01.fw"
static const char *pvr2_fw1_names_29xxx[] = {
PVR2_FIRMWARE_29xxx,
};
static const struct pvr2_device_desc pvr2_device_29xxx = {
.description = "WinTV PVR USB2 Model 29xxx",
.shortname = "29xxx",
.client_table.lst = pvr2_cli_29xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
.fx2_firmware.lst = pvr2_fw1_names_29xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
.flag_has_hauppauge_rom = !0,
.flag_has_analogtuner = !0,
.flag_has_fmradio = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
.ir_scheme = PVR2_IR_SCHEME_29XXX,
};
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 24xxx */
static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = {
{ .module_id = PVR2_CLIENT_ID_CX25840 },
{ .module_id = PVR2_CLIENT_ID_TUNER },
{ .module_id = PVR2_CLIENT_ID_WM8775 },
{ .module_id = PVR2_CLIENT_ID_DEMOD },
};
#define PVR2_FIRMWARE_24xxx "v4l-pvrusb2-24xxx-01.fw"
static const char *pvr2_fw1_names_24xxx[] = {
PVR2_FIRMWARE_24xxx,
};
static const struct pvr2_device_desc pvr2_device_24xxx = {
.description = "WinTV PVR USB2 Model 24xxx",
.shortname = "24xxx",
.client_table.lst = pvr2_cli_24xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
.fx2_firmware.lst = pvr2_fw1_names_24xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
.flag_has_cx25840 = !0,
.flag_has_wm8775 = !0,
.flag_has_hauppauge_rom = !0,
.flag_has_analogtuner = !0,
.flag_has_fmradio = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
.ir_scheme = PVR2_IR_SCHEME_24XXX,
};
/*------------------------------------------------------------------------*/
/* GOTVIEW USB2.0 DVD2 */
static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = {
{ .module_id = PVR2_CLIENT_ID_CX25840 },
{ .module_id = PVR2_CLIENT_ID_TUNER },
{ .module_id = PVR2_CLIENT_ID_DEMOD },
};
static const struct pvr2_device_desc pvr2_device_gotview_2 = {
.description = "Gotview USB 2.0 DVD 2",
.shortname = "gv2",
.client_table.lst = pvr2_cli_gotview_2,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
.flag_has_cx25840 = !0,
.default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.flag_has_analogtuner = !0,
.flag_has_fmradio = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
};
/*------------------------------------------------------------------------*/
/* GOTVIEW USB2.0 DVD Deluxe */
/* (same module list as gotview_2) */
static const struct pvr2_device_desc pvr2_device_gotview_2d = {
.description = "Gotview USB 2.0 DVD Deluxe",
.shortname = "gv2d",
.client_table.lst = pvr2_cli_gotview_2,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
.flag_has_cx25840 = !0,
.default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
};
/*------------------------------------------------------------------------*/
/* Terratec Grabster AV400 */
static const struct pvr2_device_client_desc pvr2_cli_av400[] = {
{ .module_id = PVR2_CLIENT_ID_CX25840 },
};
static const struct pvr2_device_desc pvr2_device_av400 = {
.description = "Terratec Grabster AV400",
.shortname = "av400",
.flag_is_experimental = 1,
.client_table.lst = pvr2_cli_av400,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_av400),
.flag_has_cx25840 = !0,
.flag_has_analogtuner = 0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400,
};
/*------------------------------------------------------------------------*/
/* OnAir Creator */
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct lgdt330x_config pvr2_lgdt3303_config = {
.demod_address = 0x0e,
.demod_chip = LGDT3303,
.clock_polarity_flip = 1,
};
static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
return -EIO;
}
static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
{
dvb_attach(simple_tuner_attach, adap->fe,
&adap->channel.hdw->i2c_adap, 0x61,
TUNER_LG_TDVS_H06XF);
return 0;
}
static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
.frontend_attach = pvr2_lgdt3303_attach,
.tuner_attach = pvr2_lgh06xf_attach,
};
#endif
static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = {
{ .module_id = PVR2_CLIENT_ID_SAA7115 },
{ .module_id = PVR2_CLIENT_ID_CS53L32A },
{ .module_id = PVR2_CLIENT_ID_TUNER },
};
static const struct pvr2_device_desc pvr2_device_onair_creator = {
.description = "OnAir Creator Hybrid USB tuner",
.shortname = "oac",
.client_table.lst = pvr2_cli_onair_creator,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator),
.default_tuner_type = TUNER_LG_TDVS_H06XF,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.flag_digital_requires_cx23416 = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
.default_std_mask = V4L2_STD_NTSC_M,
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
.dvb_props = &pvr2_onair_creator_fe_props,
#endif
};
/*------------------------------------------------------------------------*/
/* OnAir USB 2.0 */
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct lgdt330x_config pvr2_lgdt3302_config = {
.demod_address = 0x0e,
.demod_chip = LGDT3302,
};
static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
return -EIO;
}
static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
{
dvb_attach(simple_tuner_attach, adap->fe,
&adap->channel.hdw->i2c_adap, 0x61,
TUNER_PHILIPS_FCV1236D);
return 0;
}
static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
.frontend_attach = pvr2_lgdt3302_attach,
.tuner_attach = pvr2_fcv1236d_attach,
};
#endif
static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = {
{ .module_id = PVR2_CLIENT_ID_SAA7115 },
{ .module_id = PVR2_CLIENT_ID_CS53L32A },
{ .module_id = PVR2_CLIENT_ID_TUNER },
};
static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
.description = "OnAir USB2 Hybrid USB tuner",
.shortname = "oa2",
.client_table.lst = pvr2_cli_onair_usb2,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2),
.default_tuner_type = TUNER_PHILIPS_FCV1236D,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.flag_digital_requires_cx23416 = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
.default_std_mask = V4L2_STD_NTSC_M,
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
.dvb_props = &pvr2_onair_usb2_fe_props,
#endif
};
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 73xxx */
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct tda10048_config hauppauge_tda10048_config = {
.demod_address = 0x10 >> 1,
.output_mode = TDA10048_PARALLEL_OUTPUT,
.fwbulkwritelen = TDA10048_BULKWRITE_50,
.inversion = TDA10048_INVERSION_ON,
.dtv6_if_freq_khz = TDA10048_IF_3300,
.dtv7_if_freq_khz = TDA10048_IF_3800,
.dtv8_if_freq_khz = TDA10048_IF_4300,
.clk_freq_khz = TDA10048_CLK_16000,
.disable_gate_access = 1,
};
static struct tda829x_config tda829x_no_probe = {
.probe_tuner = TDA829X_DONT_PROBE,
};
static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = {
.dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
.if_lvl = 1, .rfagc_top = 0x37, },
.dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
.if_lvl = 1, .rfagc_top = 0x37, },
.dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
.if_lvl = 1, .rfagc_top = 0x37, },
};
static struct tda18271_config hauppauge_tda18271_dvb_config = {
.std_map = &hauppauge_tda18271_dvbt_std_map,
.gate = TDA18271_GATE_ANALOG,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
return -EIO;
}
static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
{
dvb_attach(tda829x_attach, adap->fe,
&adap->channel.hdw->i2c_adap, 0x42,
&tda829x_no_probe);
dvb_attach(tda18271_attach, adap->fe, 0x60,
&adap->channel.hdw->i2c_adap,
&hauppauge_tda18271_dvb_config);
return 0;
}
static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
.frontend_attach = pvr2_tda10048_attach,
.tuner_attach = pvr2_73xxx_tda18271_8295_attach,
};
#endif
static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = {
{ .module_id = PVR2_CLIENT_ID_CX25840 },
{ .module_id = PVR2_CLIENT_ID_TUNER,
.i2c_address_list = "\x42"},
};
#define PVR2_FIRMWARE_73xxx "v4l-pvrusb2-73xxx-01.fw"
static const char *pvr2_fw1_names_73xxx[] = {
PVR2_FIRMWARE_73xxx,
};
static const struct pvr2_device_desc pvr2_device_73xxx = {
.description = "WinTV HVR-1900 Model 73xxx",
.shortname = "73xxx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
.fx2_firmware.lst = pvr2_fw1_names_73xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
.flag_has_cx25840 = !0,
.flag_has_hauppauge_rom = !0,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
.ir_scheme = PVR2_IR_SCHEME_ZILOG,
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
.dvb_props = &pvr2_73xxx_dvb_props,
#endif
};
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 75xxx */
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct s5h1409_config pvr2_s5h1409_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_PARALLEL_OUTPUT,
.gpio = S5H1409_GPIO_OFF,
.qam_if = 4000,
.inversion = S5H1409_INVERSION_ON,
.status_mode = S5H1409_DEMODLOCKING,
};
static struct s5h1411_config pvr2_s5h1411_config = {
.output_mode = S5H1411_PARALLEL_OUTPUT,
.gpio = S5H1411_GPIO_OFF,
.vsb_if = S5H1411_IF_44000,
.qam_if = S5H1411_IF_4000,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
};
static struct tda18271_std_map hauppauge_tda18271_std_map = {
.atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
.if_lvl = 6, .rfagc_top = 0x37, },
.qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
.if_lvl = 6, .rfagc_top = 0x37, },
};
static struct tda18271_config hauppauge_tda18271_config = {
.std_map = &hauppauge_tda18271_std_map,
.gate = TDA18271_GATE_ANALOG,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
return -EIO;
}
static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
return -EIO;
}
static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
{
dvb_attach(tda829x_attach, adap->fe,
&adap->channel.hdw->i2c_adap, 0x42,
&tda829x_no_probe);
dvb_attach(tda18271_attach, adap->fe, 0x60,
&adap->channel.hdw->i2c_adap,
&hauppauge_tda18271_config);
return 0;
}
static const struct pvr2_dvb_props pvr2_750xx_dvb_props = {
.frontend_attach = pvr2_s5h1409_attach,
.tuner_attach = pvr2_tda18271_8295_attach,
};
static const struct pvr2_dvb_props pvr2_751xx_dvb_props = {
.frontend_attach = pvr2_s5h1411_attach,
.tuner_attach = pvr2_tda18271_8295_attach,
};
#endif
#define PVR2_FIRMWARE_75xxx "v4l-pvrusb2-73xxx-01.fw"
static const char *pvr2_fw1_names_75xxx[] = {
PVR2_FIRMWARE_75xxx,
};
static const struct pvr2_device_desc pvr2_device_750xx = {
.description = "WinTV HVR-1950 Model 750xx",
.shortname = "750xx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
.fx2_firmware.lst = pvr2_fw1_names_75xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
.flag_has_cx25840 = !0,
.flag_has_hauppauge_rom = !0,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.default_std_mask = V4L2_STD_NTSC_M,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
.ir_scheme = PVR2_IR_SCHEME_ZILOG,
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
.dvb_props = &pvr2_750xx_dvb_props,
#endif
};
static const struct pvr2_device_desc pvr2_device_751xx = {
.description = "WinTV HVR-1950 Model 751xx",
.shortname = "751xx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
.fx2_firmware.lst = pvr2_fw1_names_75xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
.flag_has_cx25840 = !0,
.flag_has_hauppauge_rom = !0,
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.default_std_mask = V4L2_STD_NTSC_M,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
.ir_scheme = PVR2_IR_SCHEME_ZILOG,
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
.dvb_props = &pvr2_751xx_dvb_props,
#endif
};
/*------------------------------------------------------------------------*/
struct usb_device_id pvr2_device_table[] = {
{ USB_DEVICE(0x2040, 0x2900),
.driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
{ USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */
.driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
{ USB_DEVICE(0x2040, 0x2400),
.driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
{ USB_DEVICE(0x1164, 0x0622),
.driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
{ USB_DEVICE(0x1164, 0x0602),
.driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
{ USB_DEVICE(0x11ba, 0x1003),
.driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
{ USB_DEVICE(0x11ba, 0x1001),
.driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
{ USB_DEVICE(0x2040, 0x7300),
.driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
{ USB_DEVICE(0x2040, 0x7500),
.driver_info = (kernel_ulong_t)&pvr2_device_750xx},
{ USB_DEVICE(0x2040, 0x7501),
.driver_info = (kernel_ulong_t)&pvr2_device_751xx},
{ USB_DEVICE(0x0ccd, 0x0039),
.driver_info = (kernel_ulong_t)&pvr2_device_av400},
{ }
};
MODULE_DEVICE_TABLE(usb, pvr2_device_table);
MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx);
MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx);
MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx);
MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx);
/*
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: ***
*/

查看文件

@@ -0,0 +1,199 @@
/*
*
*
* 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_DEVATTR_H
#define __PVRUSB2_DEVATTR_H
#include <linux/mod_devicetable.h>
#include <linux/videodev2.h>
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
#include "pvrusb2-dvb.h"
#endif
/*
This header defines structures used to describe attributes of a device.
*/
#define PVR2_CLIENT_ID_NULL 0
#define PVR2_CLIENT_ID_MSP3400 1
#define PVR2_CLIENT_ID_CX25840 2
#define PVR2_CLIENT_ID_SAA7115 3
#define PVR2_CLIENT_ID_TUNER 4
#define PVR2_CLIENT_ID_CS53L32A 5
#define PVR2_CLIENT_ID_WM8775 6
#define PVR2_CLIENT_ID_DEMOD 7
struct pvr2_device_client_desc {
/* One ovr PVR2_CLIENT_ID_xxxx */
unsigned char module_id;
/* Null-terminated array of I2C addresses to try in order
initialize the module. It's safe to make this null terminated
since we're never going to encounter an i2c device with an
address of zero. If this is a null pointer or zero-length,
then no I2C addresses have been specified, in which case we'll
try some compiled in defaults for now. */
unsigned char *i2c_address_list;
};
struct pvr2_device_client_table {
const struct pvr2_device_client_desc *lst;
unsigned char cnt;
};
struct pvr2_string_table {
const char **lst;
unsigned int cnt;
};
#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
#define PVR2_ROUTING_SCHEME_GOTVIEW 1
#define PVR2_ROUTING_SCHEME_ONAIR 2
#define PVR2_ROUTING_SCHEME_AV400 3
#define PVR2_DIGITAL_SCHEME_NONE 0
#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1
#define PVR2_DIGITAL_SCHEME_ONAIR 2
#define PVR2_LED_SCHEME_NONE 0
#define PVR2_LED_SCHEME_HAUPPAUGE 1
#define PVR2_IR_SCHEME_NONE 0
#define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */
#define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */
#define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */
#define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */
/* This describes a particular hardware type (except for the USB device ID
which must live in a separate structure due to environmental
constraints). See the top of pvrusb2-hdw.c for where this is
instantiated. */
struct pvr2_device_desc {
/* Single line text description of hardware */
const char *description;
/* Single token identifier for hardware */
const char *shortname;
/* List of additional client modules we need to load */
struct pvr2_string_table client_modules;
/* List of defined client modules we need to load */
struct pvr2_device_client_table client_table;
/* List of FX2 firmware file names we should search; if empty then
FX2 firmware check / load is skipped and we assume the device
was initialized from internal ROM. */
struct pvr2_string_table fx2_firmware;
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
/* callback functions to handle attachment of digital tuner & demod */
const struct pvr2_dvb_props *dvb_props;
#endif
/* Initial standard bits to use for this device, if not zero.
Anything set here is also implied as an available standard.
Note: This is ignored if overridden on the module load line via
the video_std module option. */
v4l2_std_id default_std_mask;
/* V4L tuner type ID to use with this device (only used if the
driver could not discover the type any other way). */
int default_tuner_type;
/* Signal routing scheme used by device, contains one of
PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we
encounter them. This is an arbitrary integer scheme id; its
meaning is contained entirely within the driver and is
interpreted by logic which must send commands to the chip-level
drivers (search for things which touch this field). */
unsigned char signal_routing_scheme;
/* Indicates scheme for controlling device's LED (if any). The
driver will turn on the LED when streaming is underway. This
contains one of PVR2_LED_SCHEME_XXX. */
unsigned char led_scheme;
/* Control scheme to use if there is a digital tuner. This
contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary
integer scheme id; its meaning is contained entirely within the
driver and is interpreted by logic which must control the
streaming pathway (search for things which touch this field). */
unsigned char digital_control_scheme;
/* If set, we don't bother trying to load cx23416 firmware. */
unsigned int flag_skip_cx23416_firmware:1;
/* If set, the encoder must be healthy in order for digital mode to
work (otherwise we assume that digital streaming will work even
if we fail to locate firmware for the encoder). If the device
doesn't support digital streaming then this flag has no
effect. */
unsigned int flag_digital_requires_cx23416:1;
/* Device has a hauppauge eeprom which we can interrogate. */
unsigned int flag_has_hauppauge_rom:1;
/* Device does not require a powerup command to be issued. */
unsigned int flag_no_powerup:1;
/* Device has a cx25840 - this enables special additional logic to
handle it. */
unsigned int flag_has_cx25840:1;
/* Device has a wm8775 - this enables special additional logic to
ensure that it is found. */
unsigned int flag_has_wm8775:1;
/* Indicate IR scheme of hardware. If not set, then it is assumed
that IR can work without any help from the driver. */
unsigned int ir_scheme:3;
/* These bits define which kinds of sources the device can handle.
Note: Digital tuner presence is inferred by the
digital_control_scheme enumeration. */
unsigned int flag_has_fmradio:1; /* Has FM radio receiver */
unsigned int flag_has_analogtuner:1; /* Has analog tuner */
unsigned int flag_has_composite:1; /* Has composite input */
unsigned int flag_has_svideo:1; /* Has s-video input */
unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */
/* If this driver is considered experimental, i.e. not all aspects
are working correctly and/or it is untested, mark that fact
with this flag. */
unsigned int flag_is_experimental:1;
};
extern struct usb_device_id pvr2_device_table[];
#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: ***
*/

查看文件

@@ -0,0 +1,435 @@
/*
* pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver.
*
* Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
*
* 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/kthread.h>
#include <linux/freezer.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include "dvbdev.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
#include "pvrusb2-dvb.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap)
{
int ret;
unsigned int count;
struct pvr2_buffer *bp;
struct pvr2_stream *stream;
pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started");
set_freezable();
stream = adap->channel.stream->stream;
for (;;) {
if (kthread_should_stop()) break;
/* Not sure about this... */
try_to_freeze();
bp = pvr2_stream_get_ready_buffer(stream);
if (bp != NULL) {
count = pvr2_buffer_get_count(bp);
if (count) {
dvb_dmx_swfilter(
&adap->demux,
adap->buffer_storage[
pvr2_buffer_get_id(bp)],
count);
} else {
ret = pvr2_buffer_get_status(bp);
if (ret < 0) break;
}
ret = pvr2_buffer_queue(bp);
if (ret < 0) break;
/* Since we know we did something to a buffer,
just go back and try again. No point in
blocking unless we really ran out of
buffers to process. */
continue;
}
/* Wait until more buffers become available or we're
told not to wait any longer. */
ret = wait_event_interruptible(
adap->buffer_wait_data,
(pvr2_stream_get_ready_count(stream) > 0) ||
kthread_should_stop());
if (ret < 0) break;
}
/* If we get here and ret is < 0, then an error has occurred.
Probably would be a good idea to communicate that to DVB core... */
pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped");
return 0;
}
static int pvr2_dvb_feed_thread(void *data)
{
int stat = pvr2_dvb_feed_func(data);
/* from videobuf-dvb.c: */
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
return stat;
}
static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap)
{
wake_up(&adap->buffer_wait_data);
}
static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap)
{
unsigned int idx;
struct pvr2_stream *stream;
if (adap->thread) {
kthread_stop(adap->thread);
adap->thread = NULL;
}
if (adap->channel.stream) {
stream = adap->channel.stream->stream;
} else {
stream = NULL;
}
if (stream) {
pvr2_hdw_set_streaming(adap->channel.hdw, 0);
pvr2_stream_set_callback(stream, NULL, NULL);
pvr2_stream_kill(stream);
pvr2_stream_set_buffer_count(stream, 0);
pvr2_channel_claim_stream(&adap->channel, NULL);
}
if (adap->stream_run) {
for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
if (!(adap->buffer_storage[idx])) continue;
kfree(adap->buffer_storage[idx]);
adap->buffer_storage[idx] = NULL;
}
adap->stream_run = 0;
}
}
static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap)
{
struct pvr2_context *pvr = adap->channel.mc_head;
unsigned int idx;
int ret;
struct pvr2_buffer *bp;
struct pvr2_stream *stream = NULL;
if (adap->stream_run) return -EIO;
ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream);
/* somebody else already has the stream */
if (ret < 0) return ret;
stream = adap->channel.stream->stream;
for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE,
GFP_KERNEL);
if (!(adap->buffer_storage[idx])) return -ENOMEM;
}
pvr2_stream_set_callback(pvr->video_stream.stream,
(pvr2_stream_callback) pvr2_dvb_notify, adap);
ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT);
if (ret < 0) return ret;
for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
bp = pvr2_stream_get_buffer(stream, idx);
pvr2_buffer_set_buffer(bp,
adap->buffer_storage[idx],
PVR2_DVB_BUFFER_SIZE);
}
ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1);
if (ret < 0) return ret;
while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) {
ret = pvr2_buffer_queue(bp);
if (ret < 0) return ret;
}
adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb");
if (IS_ERR(adap->thread)) {
ret = PTR_ERR(adap->thread);
adap->thread = NULL;
return ret;
}
adap->stream_run = !0;
return 0;
}
static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap)
{
int ret = pvr2_dvb_stream_do_start(adap);
if (ret < 0) pvr2_dvb_stream_end(adap);
return ret;
}
static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv;
int ret = 0;
if (adap == NULL) return -ENODEV;
mutex_lock(&adap->lock);
do {
if (onoff) {
if (!adap->feedcount) {
pvr2_trace(PVR2_TRACE_DVB_FEED,
"start feeding demux");
ret = pvr2_dvb_stream_start(adap);
if (ret < 0) break;
}
(adap->feedcount)++;
} else if (adap->feedcount > 0) {
(adap->feedcount)--;
if (!adap->feedcount) {
pvr2_trace(PVR2_TRACE_DVB_FEED,
"stop feeding demux");
pvr2_dvb_stream_end(adap);
}
}
} while (0);
mutex_unlock(&adap->lock);
return ret;
}
static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid);
return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1);
}
static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid);
return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0);
}
static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
struct pvr2_dvb_adapter *adap = fe->dvb->priv;
return pvr2_channel_limit_inputs(
&adap->channel,
(acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0));
}
static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
{
int ret;
ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb",
THIS_MODULE/*&hdw->usb_dev->owner*/,
&adap->channel.hdw->usb_dev->dev,
adapter_nr);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"dvb_register_adapter failed: error %d", ret);
goto err;
}
adap->dvb_adap.priv = adap;
adap->demux.dmx.capabilities = DMX_TS_FILTERING |
DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
adap->demux.priv = adap;
adap->demux.filternum = 256;
adap->demux.feednum = 256;
adap->demux.start_feed = pvr2_dvb_start_feed;
adap->demux.stop_feed = pvr2_dvb_stop_feed;
adap->demux.write_to_decoder = NULL;
ret = dvb_dmx_init(&adap->demux);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"dvb_dmx_init failed: error %d", ret);
goto err_dmx;
}
adap->dmxdev.filternum = adap->demux.filternum;
adap->dmxdev.demux = &adap->demux.dmx;
adap->dmxdev.capabilities = 0;
ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"dvb_dmxdev_init failed: error %d", ret);
goto err_dmx_dev;
}
dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx);
return 0;
err_dmx_dev:
dvb_dmx_release(&adap->demux);
err_dmx:
dvb_unregister_adapter(&adap->dvb_adap);
err:
return ret;
}
static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
{
pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices");
dvb_net_release(&adap->dvb_net);
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
dvb_dmx_release(&adap->demux);
dvb_unregister_adapter(&adap->dvb_adap);
return 0;
}
static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
{
struct pvr2_hdw *hdw = adap->channel.hdw;
const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
int ret = 0;
if (dvb_props == NULL) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!");
return -EINVAL;
}
ret = pvr2_channel_limit_inputs(
&adap->channel,
(1 << PVR2_CVAL_INPUT_DTV));
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"failed to grab control of dtv input (code=%d)",
ret);
return ret;
}
if (dvb_props->frontend_attach == NULL) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"frontend_attach not defined!");
ret = -EINVAL;
goto done;
}
if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"frontend registration failed!");
dvb_frontend_detach(adap->fe);
adap->fe = NULL;
ret = -ENODEV;
goto done;
}
if (dvb_props->tuner_attach)
dvb_props->tuner_attach(adap);
if (adap->fe->ops.analog_ops.standby)
adap->fe->ops.analog_ops.standby(adap->fe);
/* Ensure all frontends negotiate bus access */
adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl;
} else {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"no frontend was attached!");
ret = -ENODEV;
return ret;
}
done:
pvr2_channel_limit_inputs(&adap->channel, 0);
return ret;
}
static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap)
{
if (adap->fe != NULL) {
dvb_unregister_frontend(adap->fe);
dvb_frontend_detach(adap->fe);
}
return 0;
}
static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap)
{
pvr2_dvb_stream_end(adap);
pvr2_dvb_frontend_exit(adap);
pvr2_dvb_adapter_exit(adap);
pvr2_channel_done(&adap->channel);
kfree(adap);
}
static void pvr2_dvb_internal_check(struct pvr2_channel *chp)
{
struct pvr2_dvb_adapter *adap;
adap = container_of(chp, struct pvr2_dvb_adapter, channel);
if (!adap->channel.mc_head->disconnect_flag) return;
pvr2_dvb_destroy(adap);
}
struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr)
{
int ret = 0;
struct pvr2_dvb_adapter *adap;
if (!pvr->hdw->hdw_desc->dvb_props) {
/* Device lacks a digital interface so don't set up
the DVB side of the driver either. For now. */
return NULL;
}
adap = kzalloc(sizeof(*adap), GFP_KERNEL);
if (!adap) return adap;
pvr2_channel_init(&adap->channel, pvr);
adap->channel.check_func = pvr2_dvb_internal_check;
init_waitqueue_head(&adap->buffer_wait_data);
mutex_init(&adap->lock);
ret = pvr2_dvb_adapter_init(adap);
if (ret < 0) goto fail1;
ret = pvr2_dvb_frontend_init(adap);
if (ret < 0) goto fail2;
return adap;
fail2:
pvr2_dvb_adapter_exit(adap);
fail1:
pvr2_channel_done(&adap->channel);
return NULL;
}

查看文件

@@ -0,0 +1,41 @@
#ifndef __PVRUSB2_DVB_H__
#define __PVRUSB2_DVB_H__
#include "dvb_frontend.h"
#include "dvb_demux.h"
#include "dvb_net.h"
#include "dmxdev.h"
#include "pvrusb2-context.h"
#define PVR2_DVB_BUFFER_COUNT 32
#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000)
struct pvr2_dvb_adapter {
struct pvr2_channel channel;
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
struct dvb_frontend *fe;
int feedcount;
int max_feed_count;
struct task_struct *thread;
struct mutex lock;
unsigned int stream_run:1;
wait_queue_head_t buffer_wait_data;
char *buffer_storage[PVR2_DVB_BUFFER_COUNT];
};
struct pvr2_dvb_props {
int (*frontend_attach) (struct pvr2_dvb_adapter *);
int (*tuner_attach) (struct pvr2_dvb_adapter *);
};
struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr);
#endif /* __PVRUSB2_DVB_H__ */

查看文件

@@ -0,0 +1,164 @@
/*
*
*
* 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/slab.h>
#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 resemblance 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 NULL;
}
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,ARRAY_SIZE(msg))) != 2) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"eeprom fetch set offs err=%d",ret);
kfree(eeprom);
return NULL;
}
}
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->tuner_updated = !0;
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: ***
*/

查看文件

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

查看文件

@@ -0,0 +1,552 @@
/*
*
*
* 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"
#include "pvrusb2-fx2-cmd.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
#define MBOX_BASE 0x44
static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
unsigned int offs,
const u32 *data, unsigned int dlen)
{
unsigned int idx,addr;
unsigned int bAddr;
int ret;
unsigned int chunkCnt;
/*
Format: First byte must be 0x01. Remaining 32 bit words are
spread out into chunks of 7 bytes each, with the first 4 bytes
being the data word (little endian), and the next 3 bytes
being the address where that data word is to be written (big
endian). 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));
bAddr = 0;
hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
for (idx = 0; idx < chunkCnt; idx++) {
addr = idx + offs;
hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
bAddr += 7;
}
ret = pvr2_send_request(hdw,
hdw->cmd_buffer,1+(chunkCnt*7),
NULL,0);
if (ret) return ret;
data += chunkCnt;
dlen -= chunkCnt;
offs += chunkCnt;
}
return 0;
}
static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
unsigned int offs,
u32 *data, unsigned int dlen)
{
unsigned int idx;
int ret;
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 MBOX_BASE+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;
if (chunkCnt < 16) chunkCnt = 1;
hdw->cmd_buffer[0] =
((chunkCnt == 1) ?
FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
hdw->cmd_buffer[1] = 0;
hdw->cmd_buffer[2] = 0;
hdw->cmd_buffer[3] = 0;
hdw->cmd_buffer[4] = 0;
hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
hdw->cmd_buffer[7] = (offs & 0xffu);
ret = pvr2_send_request(hdw,
hdw->cmd_buffer,8,
hdw->cmd_buffer,
(chunkCnt == 1 ? 4 : 16 * 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,
u32 cmd,
int arg_cnt_send,
int arg_cnt_recv,
u32 *argp)
{
unsigned int poll_count;
unsigned int try_count = 0;
int retry_flag;
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 at MBOX_BASE 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 the return count words. 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 until it is set.
Finally, write out just the first word again, but set it to
0x0 this time (which probably means "idle").
*/
if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many input arguments"
" (was given %u limit %lu)",
arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
return -EINVAL;
}
if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many return arguments"
" (was given %u limit %lu)",
arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
return -EINVAL;
}
LOCK_TAKE(hdw->ctl_lock); do {
if (!hdw->state_encoder_ok) {
ret = -EIO;
break;
}
retry_flag = 0;
try_count++;
ret = 0;
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 < ARRAY_SIZE(wrData) - 4; idx++) {
wrData[idx+4] = 0;
}
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
if (ret) break;
wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
if (ret) break;
poll_count = 0;
while (1) {
poll_count++;
ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
arg_cnt_recv+4);
if (ret) {
break;
}
if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
break;
}
if (rdData[0] && (poll_count < 1000)) continue;
if (!rdData[0]) {
retry_flag = !0;
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Encoder timed out waiting for us"
"; arranging to retry");
} else {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"***WARNING*** device's encoder"
" appears to be stuck"
" (status=0x%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]);
}
ret = -EBUSY;
break;
}
if (retry_flag) {
if (try_count < 20) continue;
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Too many retries...");
ret = -EBUSY;
}
if (ret) {
del_timer_sync(&hdw->encoder_run_timer);
hdw->state_encoder_ok = 0;
pvr2_trace(PVR2_TRACE_STBITS,
"State bit %s <-- %s",
"state_encoder_ok",
(hdw->state_encoder_ok ? "true" : "false"));
if (hdw->state_encoder_runok) {
hdw->state_encoder_runok = 0;
pvr2_trace(PVR2_TRACE_STBITS,
"State bit %s <-- %s",
"state_encoder_runok",
(hdw->state_encoder_runok ?
"true" : "false"));
}
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Giving up on command."
" This is normally recovered via a firmware"
" reload and re-initialization; concern"
" is only warranted if this happens repeatedly"
" and rapidly.");
break;
}
wrData[0] = 0x7;
for (idx = 0; idx < arg_cnt_recv; idx++) {
argp[idx] = rdData[idx+4];
}
wrData[0] = 0x0;
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,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 > ARRAY_SIZE(data)) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Failed to write cx23416 command"
" - too many arguments"
" (was given %u limit %lu)",
args, (long unsigned) ARRAY_SIZE(data));
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);
}
/* This implements some extra setup for the encoder that seems to be
specific to the PVR USB2 hardware. */
static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
{
int ret = 0;
int encMisc3Arg = 0;
#if 0
/* This inexplicable bit happens in the Hauppauge windows
driver (for both 24xxx and 29xxx devices). However I
currently see no difference in behavior with or without
this stuff. Leave this here as a note of its existence,
but don't use it. */
LOCK_TAKE(hdw->ctl_lock); do {
u32 dat[1];
dat[0] = 0x80000640;
pvr2_encoder_write_words(hdw,0x01fe,dat,1);
pvr2_encoder_write_words(hdw,0x023e,dat,1);
} while(0); LOCK_GIVE(hdw->ctl_lock);
#endif
/* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
sends the following list of ENC_MISC commands (for both
24xxx and 29xxx devices). Meanings are not entirely clear,
however without the ENC_MISC(3,1) command then we risk
random perpetual video corruption whenever the video input
breaks up for a moment (like when switching channels). */
#if 0
/* This ENC_MISC(5,0) command seems to hurt 29xxx sync
performance on channel changes, but is not a problem on
24xxx devices. */
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
#endif
/* This ENC_MISC(3,encMisc3Arg) command is critical - without
it there will eventually be video corruption. Also, the
saa7115 case is strange - the Windows driver is passing 1
regardless of device type but if we have 1 for saa7115
devices the video turns sluggish. */
if (hdw->hdw_desc->flag_has_cx25840) {
encMisc3Arg = 1;
} else {
encMisc3Arg = 0;
}
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
encMisc3Arg,0,0);
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
#if 0
/* This ENC_MISC(4,1) command is poisonous, so it is commented
out. But I'm leaving it here anyway to document its
existence in the Windows driver. The effect of this
command is that apps displaying the stream become sluggish
with stuttering video. */
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
#endif
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
/* prevent the PTSs from slowly drifting away in the generated
MPEG stream */
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1);
return ret;
}
int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
{
int ret;
ret = cx2341x_update(hdw,pvr2_encoder_cmd,
(hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
&hdw->enc_ctl_state);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Error from cx2341x module code=%d",ret);
} else {
memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
sizeof(struct cx2341x_mpeg_params));
hdw->enc_cur_valid = !0;
}
return ret;
}
int pvr2_encoder_configure(struct pvr2_hdw *hdw)
{
int ret;
int val;
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_525_60) ?
0 : 1);
ret = 0;
ret |= pvr2_encoder_prep_config(hdw);
/* saa7115: 0xf0 */
val = 0xf0;
if (hdw->hdw_desc->flag_has_cx25840) {
/* ivtv cx25840: 0x140 */
val = 0x140;
}
if (!ret) ret = pvr2_encoder_vcmd(
hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
val, val);
/* 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 cx23416");
return ret;
}
ret = pvr2_encoder_adjust(hdw);
if (ret) return ret;
ret = pvr2_encoder_vcmd(
hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Failed to initialize cx23416 video input");
return ret;
}
return 0;
}
int pvr2_encoder_start(struct pvr2_hdw *hdw)
{
int status;
/* unmask some interrupts */
pvr2_write_register(hdw, 0x0048, 0xbfffffff);
pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0x01,0x14);
break;
case pvr2_config_mpeg:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0,0x13);
break;
default: /* Unhandled cases for now */
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0,0x13);
break;
}
return status;
}
int pvr2_encoder_stop(struct pvr2_hdw *hdw)
{
int status;
/* mask all interrupts */
pvr2_write_register(hdw, 0x0048, 0xffffffff);
switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0x01,0x14);
break;
case pvr2_config_mpeg:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0,0x13);
break;
default: /* Unhandled cases for now */
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0,0x13);
break;
}
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: ***
*/

查看文件

@@ -0,0 +1,42 @@
/*
*
*
* 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_adjust(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: ***
*/

查看文件

@@ -0,0 +1,72 @@
/*
*
*
* Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org>
*
* 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_FX2_CMD_H_
#define _PVRUSB2_FX2_CMD_H_
#define FX2CMD_MEM_WRITE_DWORD 0x01u
#define FX2CMD_MEM_READ_DWORD 0x02u
#define FX2CMD_HCW_ZILOG_RESET 0x10u /* 1=reset 0=release */
#define FX2CMD_MEM_READ_64BYTES 0x28u
#define FX2CMD_REG_WRITE 0x04u
#define FX2CMD_REG_READ 0x05u
#define FX2CMD_MEMSEL 0x06u
#define FX2CMD_I2C_WRITE 0x08u
#define FX2CMD_I2C_READ 0x09u
#define FX2CMD_GET_USB_SPEED 0x0bu
#define FX2CMD_STREAMING_ON 0x36u
#define FX2CMD_STREAMING_OFF 0x37u
#define FX2CMD_FWPOST1 0x52u
#define FX2CMD_POWER_OFF 0xdcu
#define FX2CMD_POWER_ON 0xdeu
#define FX2CMD_DEEP_RESET 0xddu
#define FX2CMD_GET_EEPROM_ADDR 0xebu
#define FX2CMD_GET_IR_CODE 0xecu
#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u
#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u
#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u
#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u
#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u
#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u
#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u
#endif /* _PVRUSB2_FX2_CMD_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: ***
*/

查看文件

@@ -0,0 +1,406 @@
/*
*
*
* 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/videodev2.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
#include <media/v4l2-device.h>
#include <media/cx2341x.h>
#include <media/ir-kbd-i2c.h>
#include "pvrusb2-devattr.h"
/* 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)
typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *);
typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *);
typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int);
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_get_value get_def_value; /* Get its default value */
pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */
pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */
pvr2_ctlf_set_value set_value; /* Set its value */
pvr2_ctlf_check_value check_value; /* Check that value is valid */
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 * const *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;
};
/* 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
/* What state the device is in if it is a hybrid */
#define PVR2_PATHWAY_UNKNOWN 0
#define PVR2_PATHWAY_ANALOG 1
#define PVR2_PATHWAY_DIGITAL 2
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;
/* Our handle into the v4l2 sub-device architecture */
struct v4l2_device v4l2_dev;
/* Device description, anything that must adjust behavior based on
device specific info will use information held here. */
const struct pvr2_device_desc *hdw_desc;
/* Kernel worker thread handling */
struct workqueue_struct *workqueue;
struct work_struct workpoll; /* Update driver state */
/* Video spigot */
struct pvr2_stream *vid_stream;
/* Mutex for all hardware state control */
struct mutex big_lock_mutex;
int big_lock_held; /* For debugging */
/* This is a simple string which identifies the instance of this
driver. It is unique within the set of existing devices, but
there is no attempt to keep the name consistent with the same
physical device each time. */
char name[32];
/* This is a simple string which identifies the physical device
instance itself - if possible. (If not possible, then it is
based on the specific driver instance, similar to name above.)
The idea here is that userspace might hopefully be able to use
this recognize specific tuners. It will encode a serial number,
if available. */
char identifier[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;
/* IR related */
unsigned int ir_scheme_active; /* IR scheme as seen from the outside */
struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */
/* Frequency table */
unsigned int freqTable[FREQTABLE_SIZE];
unsigned int freqProgSlot;
/* 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;
int ctl_write_pend_flag;
int ctl_read_pend_flag;
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; //
/* Bits of state that describe what is going on with various parts
of the driver. */
int state_pathway_ok; /* Pathway config is ok */
int state_encoder_ok; /* Encoder is operational */
int state_encoder_run; /* Encoder is running */
int state_encoder_config; /* Encoder is configured */
int state_encoder_waitok; /* Encoder pre-wait done */
int state_encoder_runok; /* Encoder has run for >= .25 sec */
int state_decoder_run; /* Decoder is running */
int state_decoder_ready; /* Decoder is stabilized & streamable */
int state_usbstream_run; /* FX2 is streaming */
int state_decoder_quiescent; /* Decoder idle for minimal interval */
int state_pipeline_config; /* Pipeline is configured */
int state_pipeline_req; /* Somebody wants to stream */
int state_pipeline_pause; /* Pipeline must be paused */
int state_pipeline_idle; /* Pipeline not running */
/* This is the master state of the driver. It is the combined
result of other bits of state. Examining this will indicate the
overall state of the driver. Values here are one of
PVR2_STATE_xxxx */
unsigned int master_state;
/* True if device led is currently on */
int led_on;
/* True if states must be re-evaluated */
int state_stale;
void (*state_func)(void *);
void *state_data;
/* Timer for measuring required decoder settling time before we're
allowed to fire it up again. */
struct timer_list quiescent_timer;
/* Timer for measuring decoder stabilization time, which is the
amount of time we need to let the decoder run before we can
trust its output (otherwise the encoder might see garbage and
then fail to start correctly). */
struct timer_list decoder_stabilization_timer;
/* Timer for measuring encoder pre-wait time */
struct timer_list encoder_wait_timer;
/* Timer for measuring encoder minimum run time */
struct timer_list encoder_run_timer;
/* Place to block while waiting for state changes */
wait_queue_head_t state_wait_data;
int force_dirty; /* consider all controls dirty if true */
int flag_ok; /* device in known good state */
int flag_modulefail; /* true if at least one module failed to load */
int flag_disconnected; /* flag_ok == 0 due to disconnect */
int flag_init_ok; /* true if structure is fully initialized */
int fw1_state; /* current situation with fw1 */
int pathway_state; /* one of PVR2_PATHWAY_xxx */
int flag_decoder_missed;/* We've noticed missing decoder */
int flag_tripped; /* Indicates overall failure to start */
unsigned int decoder_client_id;
// CPU firmware info (used to help find / save firmware data)
char *fw_buffer;
unsigned int fw_size;
int fw_cpu_flag; /* True if we are dealing with the CPU */
/* Tuner / frequency control stuff */
unsigned int tuner_type;
int tuner_updated;
unsigned int freqValTelevision; /* Current freq for tv mode */
unsigned int freqValRadio; /* Current freq for radio mode */
unsigned int freqSlotTelevision; /* Current slot for tv mode */
unsigned int freqSlotRadio; /* Current slot for radio mode */
unsigned int freqSelector; /* 0=radio 1=television */
int freqDirty;
/* Current tuner info - this information is polled from the I2C bus */
struct v4l2_tuner tuner_signal_info;
int tuner_signal_stale;
/* Cropping capability info */
struct v4l2_cropcap cropcap_info;
int cropcap_stale;
/* 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)
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 pvr2_ctl_info std_info_detect;
// Generated string names, one per actual V4L2 standard
const char *std_mask_ptrs[32];
char std_mask_names[32][16];
int unit_number; /* ID for driver instance */
unsigned long serial_number; /* ID for hardware itself */
char bus_info[32]; /* Bus location info */
/* Minor numbers 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_video;
int v4l_minor_number_vbi;
int v4l_minor_number_radio;
/* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
unsigned int input_avail_mask;
/* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */
unsigned int input_allowed_mask;
/* Location of eeprom or a negative number if none */
int eeprom_addr;
enum pvr2_config active_stream_type;
enum pvr2_config desired_stream_type;
/* 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 an unsafe encoder attribute has changed */
int enc_unsafe_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(cropl);
VCREATE_DATA(cropt);
VCREATE_DATA(cropw);
VCREATE_DATA(croph);
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;
};
/* This function gets the current frequency */
unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
void pvr2_hdw_status_poll(struct pvr2_hdw *);
#endif /* __PVRUSB2_HDW_INTERNAL_H */
/*
Stuff for Emacs to see, in order to encourage consistent editing style:
*** Local Variables: ***
*** mode: c ***
*** fill-column: 75 ***
*** tab-width: 8 ***
*** c-basic-offset: 8 ***
*** End: ***
*/

檔案差異因為檔案過大而無法顯示 載入差異

查看文件

@@ -0,0 +1,360 @@
/*
*
*
* 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_STDCUR 2
#define PVR2_CID_STDAVAIL 3
#define PVR2_CID_INPUT 4
#define PVR2_CID_AUDIOMODE 5
#define PVR2_CID_FREQUENCY 6
#define PVR2_CID_HRES 7
#define PVR2_CID_VRES 8
#define PVR2_CID_CROPL 9
#define PVR2_CID_CROPT 10
#define PVR2_CID_CROPW 11
#define PVR2_CID_CROPH 12
#define PVR2_CID_CROPCAPPAN 13
#define PVR2_CID_CROPCAPPAD 14
#define PVR2_CID_CROPCAPBL 15
#define PVR2_CID_CROPCAPBT 16
#define PVR2_CID_CROPCAPBW 17
#define PVR2_CID_CROPCAPBH 18
#define PVR2_CID_STDDETECT 19
/* Legal values for the INPUT state variable */
#define PVR2_CVAL_INPUT_TV 0
#define PVR2_CVAL_INPUT_DTV 1
#define PVR2_CVAL_INPUT_COMPOSITE 2
#define PVR2_CVAL_INPUT_SVIDEO 3
#define PVR2_CVAL_INPUT_RADIO 4
enum pvr2_config {
pvr2_config_empty, /* No configuration */
pvr2_config_mpeg, /* Encoded / compressed video */
pvr2_config_vbi, /* Standard vbi info */
pvr2_config_pcm, /* Audio raw pcm stream */
pvr2_config_rawvideo, /* Video raw frames */
};
enum pvr2_v4l_type {
pvr2_v4l_type_video,
pvr2_v4l_type_vbi,
pvr2_v4l_type_radio,
};
/* Major states that we can be in:
*
* DEAD - Device is in an unusable state and cannot be recovered. This
* can happen if we completely lose the ability to communicate with it
* (but it might still on the bus). In this state there's nothing we can
* do; it must be replugged in order to recover.
*
* COLD - Device is in an unusable state, needs microcontroller firmware.
*
* WARM - We can communicate with the device and the proper
* microcontroller firmware is running, but other device initialization is
* still needed (e.g. encoder firmware).
*
* ERROR - A problem prevents capture operation (e.g. encoder firmware
* missing).
*
* READY - Device is operational, but not streaming.
*
* RUN - Device is streaming.
*
*/
#define PVR2_STATE_NONE 0
#define PVR2_STATE_DEAD 1
#define PVR2_STATE_COLD 2
#define PVR2_STATE_WARM 3
#define PVR2_STATE_ERROR 4
#define PVR2_STATE_READY 5
#define PVR2_STATE_RUN 6
/* Translate configuration enum to a string label */
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);
/* Perform second stage initialization, passing in a notification callback
for when the master state changes. */
int pvr2_hdw_initialize(struct pvr2_hdw *,
void (*callback_func)(void *),
void *callback_data);
/* Destroy hardware interaction structure */
void pvr2_hdw_destroy(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 *);
/* Retrieve bus location info of device */
const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *);
/* Retrieve per-instance string identifier for this specific device */
const char *pvr2_hdw_get_device_identifier(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 a bit mask of valid input selections for this device. Mask bits
* will be according to PVR_CVAL_INPUT_xxxx definitions. */
unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *);
/* Return a bit mask of allowed input selections for this device. Mask bits
* will be according to PVR_CVAL_INPUT_xxxx definitions. */
unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *);
/* Change the set of allowed input selections for this device. Both
change_mask and change_valu are mask bits according to
PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate
which settings are being changed and the change_val parameter indicates
whether corresponding settings are being set or cleared. */
int pvr2_hdw_set_input_allowed(struct pvr2_hdw *,
unsigned int change_mask,
unsigned int change_val);
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *);
/* Mark tuner status stale so that it will be re-fetched */
void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *);
/* Return information about the tuner */
int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
/* Return information about cropping capabilities */
int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *);
/* Query device and see if it thinks it is on a high-speed USB link */
int pvr2_hdw_is_hsm(struct pvr2_hdw *);
/* Return a string token representative of the hardware type */
const char *pvr2_hdw_get_type(struct pvr2_hdw *);
/* Return a single line description of the hardware type */
const char *pvr2_hdw_get_desc(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 *);
/* Retrieve driver overall state */
int pvr2_hdw_get_state(struct pvr2_hdw *);
/* Configure the type of stream to generate */
int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
/* Get handle to video output stream */
struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
/* Enable / disable retrieval of CPU firmware or prom contents. 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 mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */
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 a previously stored v4l minor device number */
int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index);
/* Store a v4l minor device number */
void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,
enum pvr2_v4l_type index,int);
/* Direct read/write access to chip's registers:
match - specify criteria to identify target chip (this is a v4l dbg struct)
reg_id - register number to access
setFl - true to set the register, false to read it
val_ptr - storage location for source / result. */
int pvr2_hdw_register_access(struct pvr2_hdw *,
struct v4l2_dbg_match *match, u64 reg_id,
int setFl, u64 *val_ptr);
/* The following entry points are all lower level things you normally don't
want to worry about. */
/* 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);
/* Slightly higher level device communication functions. */
int pvr2_write_register(struct pvr2_hdw *, u16, u32);
/* 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 *);
/* 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 *);
/* Reset worker's error trapping circuit breaker */
int pvr2_hdw_untrip(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 *);
/* suspend */
int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *);
/* Order decoder to reset */
int pvr2_hdw_cmd_decoder_reset(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_disconnected;
int flag_init_ok;
int flag_ok;
int fw1_state;
int flag_decoder_missed;
int flag_tripped;
int state_encoder_ok;
int state_encoder_run;
int state_decoder_run;
int state_decoder_ready;
int state_usbstream_run;
int state_decoder_quiescent;
int state_pipeline_config;
int state_pipeline_req;
int state_pipeline_pause;
int state_pipeline_idle;
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_unlocked(const struct pvr2_hdw *hdw,
struct pvr2_hdw_debug_info *);
/* Intrusively retrieve internal state info - this is useful for
diagnosing overall driver state. This operation synchronizes against
the overall driver mutex - so if there are locking problems this will
likely hang! This is *purely* a debugging aid. */
void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
struct pvr2_hdw_debug_info *);
/* Report out several lines of text that describes driver internal state.
Results are written into the passed-in buffer. */
unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
char *buf_ptr,unsigned int buf_size);
/* 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);
#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: ***
*/

查看文件

@@ -0,0 +1,698 @@
/*
*
*
* 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/i2c.h>
#include <linux/module.h>
#include <media/ir-kbd-i2c.h>
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-fx2-cmd.h"
#include "pvrusb2.h"
#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
/*
This module attempts to implement a compliant I2C adapter for the pvrusb2
device.
*/
static unsigned int i2c_scan;
module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 };
module_param_array(ir_mode, int, NULL, 0444);
MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR");
static int pvr2_disable_ir_video;
module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video,
int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(disable_autoload_ir_video,
"1=do not try to autoload ir_video IR receiver");
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] = FX2CMD_I2C_WRITE; /* 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] = FX2CMD_I2C_READ; /* 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. */
static 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);
}
}
/* This is a special entry point for cases of I2C transaction attempts to
the IR receiver. The implementation here simulates the IR receiver by
issuing a command to the FX2 firmware and using that response to return
what the real I2C receiver would have returned. We use this for 24xxx
devices, where the IR receiver chip has been removed and replaced with
FX2 related logic. */
static int i2c_24xxx_ir(struct pvr2_hdw *hdw,
u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
{
u8 dat[4];
unsigned int stat;
if (!(rlen || wlen)) {
/* This is a probe attempt. Just let it succeed. */
return 0;
}
/* We don't understand this kind of transaction */
if ((wlen != 0) || (rlen == 0)) return -EIO;
if (rlen < 3) {
/* Mike Isely <isely@pobox.com> Appears to be a probe
attempt from lirc. Just fill in zeroes and return. If
we try instead to do the full transaction here, then bad
things seem to happen within the lirc driver module
(version 0.8.0-7 sources from Debian, when run under
vanilla 2.6.17.6 kernel) - and I don't have the patience
to chase it down. */
if (rlen > 0) rdata[0] = 0;
if (rlen > 1) rdata[1] = 0;
return 0;
}
/* Issue a command to the FX2 to read the IR receiver. */
LOCK_TAKE(hdw->ctl_lock); do {
hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE;
stat = pvr2_send_request(hdw,
hdw->cmd_buffer,1,
hdw->cmd_buffer,4);
dat[0] = hdw->cmd_buffer[0];
dat[1] = hdw->cmd_buffer[1];
dat[2] = hdw->cmd_buffer[2];
dat[3] = hdw->cmd_buffer[3];
} while (0); LOCK_GIVE(hdw->ctl_lock);
/* Give up if that operation failed. */
if (stat != 0) return stat;
/* Mangle the results into something that looks like the real IR
receiver. */
rdata[2] = 0xc1;
if (dat[0] != 1) {
/* No code received. */
rdata[0] = 0;
rdata[1] = 0;
} else {
u16 val;
/* Mash the FX2 firmware-provided IR code into something
that the normal i2c chip-level driver expects. */
val = dat[1];
val <<= 8;
val |= dat[2];
val >>= 1;
val &= ~0x0003;
val |= 0x8000;
rdata[0] = (val >> 8) & 0xffu;
rdata[1] = val & 0xffu;
}
return 0;
}
/* 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 an entry point designed to always fail any attempt to perform a
transfer. We use this to cause certain I2C addresses to not be
probed. */
static int i2c_black_hole(struct pvr2_hdw *hdw,
u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
{
return -EIO;
}
/* 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] = NULL;
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;
}
/* 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 = NULL;
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
if (!num) {
ret = -EINVAL;
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,NULL,0,NULL,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,NULL,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,NULL,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",
idx+1,num,
msgs[idx].addr,
cnt,
(msgs[idx].flags & I2C_M_RD ?
"read" : "write"));
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 u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm pvr2_i2c_algo_template = {
.master_xfer = pvr2_i2c_xfer,
.functionality = pvr2_i2c_functionality,
};
static struct i2c_adapter pvr2_i2c_adap_template = {
.owner = THIS_MODULE,
.class = 0,
};
/* Return true if device exists at given address */
static int do_i2c_probe(struct pvr2_hdw *hdw, int addr)
{
struct i2c_msg msg[1];
int rc;
msg[0].addr = 0;
msg[0].flags = I2C_M_RD;
msg[0].len = 0;
msg[0].buf = NULL;
msg[0].addr = addr;
rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg));
return rc == 1;
}
static void do_i2c_scan(struct pvr2_hdw *hdw)
{
int i;
printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name);
for (i = 0; i < 128; i++) {
if (do_i2c_probe(hdw, i)) {
printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n",
hdw->name, i);
}
}
printk(KERN_INFO "%s: i2c scan done.\n", hdw->name);
}
static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw)
{
struct i2c_board_info info;
struct IR_i2c_init_data *init_data = &hdw->ir_init_data;
if (pvr2_disable_ir_video) {
pvr2_trace(PVR2_TRACE_INFO,
"Automatic binding of ir_video has been disabled.");
return;
}
memset(&info, 0, sizeof(struct i2c_board_info));
switch (hdw->ir_scheme_active) {
case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */
case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
init_data->type = RC_TYPE_RC5;
init_data->name = hdw->hdw_desc->description;
init_data->polling_interval = 100; /* ms From ir-kbd-i2c */
/* IR Receiver */
info.addr = 0x18;
info.platform_data = init_data;
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
info.type, info.addr);
i2c_new_device(&hdw->i2c_adap, &info);
break;
case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */
case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
init_data->type = RC_TYPE_RC5;
init_data->name = hdw->hdw_desc->description;
/* IR Receiver */
info.addr = 0x71;
info.platform_data = init_data;
strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE);
pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
info.type, info.addr);
i2c_new_device(&hdw->i2c_adap, &info);
/* IR Trasmitter */
info.addr = 0x70;
info.platform_data = init_data;
strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE);
pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
info.type, info.addr);
i2c_new_device(&hdw->i2c_adap, &info);
break;
default:
/* The device either doesn't support I2C-based IR or we
don't know (yet) how to operate IR on the device. */
break;
}
}
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;
}
/* However, deal with various special cases for 24xxx hardware. */
if (ir_mode[hdw->unit_number] == 0) {
printk(KERN_INFO "%s: IR disabled\n",hdw->name);
hdw->i2c_func[0x18] = i2c_black_hole;
} else if (ir_mode[hdw->unit_number] == 1) {
if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) {
/* Set up translation so that our IR looks like a
29xxx device */
hdw->i2c_func[0x18] = i2c_24xxx_ir;
}
}
if (hdw->hdw_desc->flag_has_cx25840) {
hdw->i2c_func[0x44] = i2c_hack_cx25840;
}
if (hdw->hdw_desc->flag_has_wm8775) {
hdw->i2c_func[0x1b] = i2c_hack_wm8775;
}
// 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.dev.parent = &hdw->usb_dev->dev;
hdw->i2c_adap.algo = &hdw->i2c_algo;
hdw->i2c_adap.algo_data = hdw;
hdw->i2c_linked = !0;
i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev);
i2c_add_adapter(&hdw->i2c_adap);
if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
/* Probe for a different type of IR receiver on this
device. This is really the only way to differentiate
older 24xxx devices from 24xxx variants that include an
IR blaster. If the IR blaster is present, the IR
receiver is part of that chip and thus we must disable
the emulated IR receiver. */
if (do_i2c_probe(hdw, 0x71)) {
pvr2_trace(PVR2_TRACE_INFO,
"Device has newer IR hardware;"
" disabling unneeded virtual IR device");
hdw->i2c_func[0x18] = NULL;
/* Remember that this is a different device... */
hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE;
}
}
if (i2c_scan) do_i2c_scan(hdw);
pvr2_i2c_register_ir(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: ***
*/

查看文件

@@ -0,0 +1,40 @@
/*
*
*
* 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
struct pvr2_hdw;
void pvr2_i2c_core_init(struct pvr2_hdw *);
void pvr2_i2c_core_done(struct pvr2_hdw *);
#endif /* __PVRUSB2_I2C_ADAPTER_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: ***
*/

查看文件

@@ -0,0 +1,695 @@
/*
*
*
* 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>
static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
#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;
unsigned int buffers_processed;
unsigned int buffers_failed;
unsigned int bytes_processed;
};
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;
};
static 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";
}
#ifdef SANITY_CHECK_BUFFERS
static 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 : NULL),
(bp ? bp->purb : NULL),
(bp ? bp->signature : 0));
}
#endif /* SANITY_CHECK_BUFFERS */
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 = NULL;
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] = NULL;
(sp->buffer_total_count)--;
pvr2_buffer_done(bp);
kfree(bp);
}
if (scnt < sp->buffer_slot_count) {
struct pvr2_buffer **nb = NULL;
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 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)) {
(sp->buffers_processed)++;
sp->bytes_processed += urb->actual_length;
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)++;
(sp->buffers_failed)++;
pvr2_trace(PVR2_TRACE_TOLERANCE,
"stream %p ignoring error %d"
" - fail count increased to %u",
sp,urb->status,sp->fail_count);
} else {
(sp->buffers_failed)++;
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 = kzalloc(sizeof(*sp),GFP_KERNEL);
if (!sp) return 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);
}
void pvr2_stream_get_stats(struct pvr2_stream *sp,
struct pvr2_stream_stats *stats,
int zero_counts)
{
unsigned long irq_flags;
spin_lock_irqsave(&sp->list_lock,irq_flags);
if (stats) {
stats->buffers_in_queue = sp->q_count;
stats->buffers_in_idle = sp->i_count;
stats->buffers_in_ready = sp->r_count;
stats->buffers_processed = sp->buffers_processed;
stats->buffers_failed = sp->buffers_failed;
stats->bytes_processed = sp->bytes_processed;
}
if (zero_counts) {
sp->buffers_processed = 0;
sp->buffers_failed = 0;
sp->bytes_processed = 0;
}
spin_unlock_irqrestore(&sp->list_lock,irq_flags);
}
/* 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 NULL;
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 NULL;
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 NULL;
if (id >= sp->buffer_total_count) return NULL;
return sp->buffers[id];
}
int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
{
return sp->r_count;
}
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)) != NULL) {
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_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;
}
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: ***
*/

查看文件

@@ -0,0 +1,102 @@
/*
*
*
* 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;
struct pvr2_stream_stats {
unsigned int buffers_in_queue;
unsigned int buffers_in_idle;
unsigned int buffers_in_ready;
unsigned int buffers_processed;
unsigned int buffers_failed;
unsigned int bytes_processed;
};
/* 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);
void pvr2_stream_get_stats(struct pvr2_stream *,
struct pvr2_stream_stats *,
int zero_counts);
/* 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_ready_count(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 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 *);
#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: ***
*/

查看文件

@@ -0,0 +1,512 @@
/*
*
*
* 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/mm.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 = NULL;
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,NULL);
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 = kzalloc(sizeof(*cp),GFP_KERNEL);
if (!cp) return NULL;
pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
if (pvr2_ioread_init(cp) < 0) {
kfree(cp);
return NULL;
}
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 = NULL;
}
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 = NULL;
}
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 = NULL;
cp->c_data_ptr = NULL;
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)) != NULL) {
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 = NULL;
cp->c_data_ptr = NULL;
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);
if (pvr2_stream_get_buffer_count(cp->stream)) {
pvr2_stream_set_buffer_count(cp->stream,0);
}
cp->stream = NULL;
}
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) {
mutex_unlock(&cp->mutex);
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;
}
static 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 = NULL;
cp->c_data_ptr = NULL;
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;
}
static 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: ***
*/

查看文件

@@ -0,0 +1,48 @@
/*
*
*
* 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_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: ***
*/

查看文件

@@ -0,0 +1,182 @@
/*
*
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include "pvrusb2-hdw.h"
#include "pvrusb2-devattr.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_STD| \
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 = NULL;
#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_DVB
/* Create association with dvb layer */
pvr2_dvb_create(pvr);
#endif
#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");
ret = pvr2_context_global_init();
if (ret != 0) {
pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret);
return ret;
}
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
class_ptr = pvr2_sysfs_class_create();
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
ret = usb_register(&pvr_driver);
if (ret == 0)
printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":"
DRIVER_DESC "\n");
if (pvrusb2_debug)
printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n",
pvrusb2_debug,pvrusb2_debug);
pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete");
return ret;
}
static void __exit pvr_exit(void)
{
pvr2_trace(PVR2_TRACE_INIT,"pvr_exit");
usb_deregister(&pvr_driver);
pvr2_context_global_done();
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
pvr2_sysfs_class_destroy(class_ptr);
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete");
}
module_init(pvr_init);
module_exit(pvr_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION("0.9.1");
/*
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: ***
*/

查看文件

@@ -0,0 +1,411 @@
/*
*
*
* 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_ATSC \
(V4L2_STD_ATSC_8_VSB| \
V4L2_STD_ATSC_16_VSB)
#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_ATSC|CSTD_SECAM)
/* Mapping of standard bits to color system */
static const struct std_name std_groups[] = {
{"PAL",CSTD_PAL},
{"NTSC",CSTD_NTSC},
{"SECAM",CSTD_SECAM},
{"ATSC",CSTD_ATSC},
};
/* Mapping of standard bits to modulation system */
static const 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},
{"8VSB",V4L2_STD_ATSC_8_VSB},
{"16VSB",V4L2_STD_ATSC_16_VSB},
};
// 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 NULL;
}
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, ARRAY_SIZE(std_groups),
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, ARRAY_SIZE(std_items),
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 < ARRAY_SIZE(std_groups); idx1++) {
gp = std_groups + idx1;
gfl = 0;
for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); 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}
}
};
static struct v4l2_standard *match_std(v4l2_std_id id)
{
unsigned int idx;
for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) {
if (generic_standards[idx].id & id) {
return generic_standards + idx;
}
}
return NULL;
}
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_STD,"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_STD) {
char buf[100];
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
pvr2_trace(
PVR2_TRACE_STD,"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 < ARRAY_SIZE(std_mixes); idx2++) {
if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
}
/* Don't complain about ATSC standard values */
fmsk &= ~CSTD_ATSC;
if (fmsk) {
char buf[100];
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_STD,"Setting up %u unique standard(s)",
std_cnt);
if (!std_cnt) return NULL; // paranoia
stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt,
GFP_KERNEL);
if (!stddefs)
return NULL;
for (idx = 0; idx < std_cnt; idx++)
stddefs[idx].index = idx;
idx = 0;
/* Enumerate potential special cases */
for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (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: ***
*/

查看文件

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

查看文件

@@ -0,0 +1,861 @@
/*
*
*
* 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-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 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 device_attribute attr_v4l_minor_number;
struct device_attribute attr_v4l_radio_minor_number;
struct device_attribute attr_unit_number;
struct device_attribute attr_bus_info;
struct device_attribute attr_hdw_name;
struct device_attribute attr_hdw_desc;
int v4l_minor_number_created_ok;
int v4l_radio_minor_number_created_ok;
int unit_number_created_ok;
int bus_info_created_ok;
int hdw_name_created_ok;
int hdw_desc_created_ok;
};
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
struct pvr2_sysfs_debugifc {
struct device_attribute attr_debugcmd;
struct device_attribute attr_debuginfo;
int debugcmd_created_ok;
int debuginfo_created_ok;
};
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
struct pvr2_sysfs_ctl_item {
struct device_attribute attr_name;
struct device_attribute attr_type;
struct device_attribute attr_min;
struct device_attribute attr_max;
struct device_attribute attr_def;
struct device_attribute attr_enum;
struct device_attribute attr_bits;
struct device_attribute attr_val;
struct device_attribute attr_custom;
struct pvr2_ctrl *cptr;
int ctl_id;
struct pvr2_sysfs *chptr;
struct pvr2_sysfs_ctl_item *item_next;
struct attribute *attr_gen[8];
struct attribute_group grp;
int created_ok;
char name[80];
};
struct pvr2_sysfs_class {
struct class class;
};
static ssize_t show_name(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
const char *name;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name);
name = pvr2_ctrl_get_desc(cip->cptr);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",
cip->chptr, cip->ctl_id, name);
if (!name) return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%s\n", name);
}
static ssize_t show_type(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
const char *name;
enum pvr2_ctl_type tp;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type);
tp = pvr2_ctrl_get_type(cip->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",
cip->chptr, cip->ctl_id, name);
if (!name) return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%s\n", name);
}
static ssize_t show_min(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
long val;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min);
val = pvr2_ctrl_get_min(cip->cptr);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",
cip->chptr, cip->ctl_id, val);
return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
}
static ssize_t show_max(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
long val;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max);
val = pvr2_ctrl_get_max(cip->cptr);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",
cip->chptr, cip->ctl_id, val);
return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
}
static ssize_t show_def(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
int val;
int ret;
unsigned int cnt = 0;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def);
ret = pvr2_ctrl_get_def(cip->cptr, &val);
if (ret < 0) return ret;
ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
buf, PAGE_SIZE - 1, &cnt);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)",
cip->chptr, cip->ctl_id, cnt, buf, val);
buf[cnt] = '\n';
return cnt + 1;
}
static ssize_t show_val_norm(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
int val;
int ret;
unsigned int cnt = 0;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
ret = pvr2_ctrl_get_value(cip->cptr, &val);
if (ret < 0) return ret;
ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
buf, PAGE_SIZE - 1, &cnt);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
cip->chptr, cip->ctl_id, cnt, buf, val);
buf[cnt] = '\n';
return cnt+1;
}
static ssize_t show_val_custom(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
int val;
int ret;
unsigned int cnt = 0;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
ret = pvr2_ctrl_get_value(cip->cptr, &val);
if (ret < 0) return ret;
ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val,
buf, PAGE_SIZE - 1, &cnt);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
cip->chptr, cip->ctl_id, cnt, buf, val);
buf[cnt] = '\n';
return cnt+1;
}
static ssize_t show_enum(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
long val;
unsigned int bcnt, ccnt, ecnt;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum);
ecnt = pvr2_ctrl_get_cnt(cip->cptr);
bcnt = 0;
for (val = 0; val < ecnt; val++) {
pvr2_ctrl_get_valname(cip->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)",
cip->chptr, cip->ctl_id);
return bcnt;
}
static ssize_t show_bits(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs_ctl_item *cip;
int valid_bits, msk;
unsigned int bcnt, ccnt;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits);
valid_bits = pvr2_ctrl_get_mask(cip->cptr);
bcnt = 0;
for (msk = 1; valid_bits; msk <<= 1) {
if (!(msk & valid_bits)) continue;
valid_bits &= ~msk;
pvr2_ctrl_get_valname(cip->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)",
cip->chptr, cip->ctl_id);
return bcnt;
}
static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl,
const char *buf,unsigned int count)
{
int ret;
int mask,val;
if (customfl) {
ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count,
&mask, &val);
} else {
ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count,
&mask, &val);
}
if (ret < 0) return ret;
ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val);
pvr2_hdw_commit_ctl(cip->chptr->channel.hdw);
return ret;
}
static ssize_t store_val_norm(struct device *class_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pvr2_sysfs_ctl_item *cip;
int ret;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"",
cip->chptr, cip->ctl_id, (int)count, buf);
ret = store_val_any(cip, 0, buf, count);
if (!ret) ret = count;
return ret;
}
static ssize_t store_val_custom(struct device *class_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pvr2_sysfs_ctl_item *cip;
int ret;
cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"",
cip->chptr, cip->ctl_id, (int)count, buf);
ret = store_val_any(cip, 1, buf, count);
if (!ret) ret = count;
return ret;
}
static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
{
struct pvr2_sysfs_ctl_item *cip;
struct pvr2_ctrl *cptr;
unsigned int cnt,acnt;
int ret;
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
if (!cptr) return;
cip = kzalloc(sizeof(*cip),GFP_KERNEL);
if (!cip) return;
pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
cip->cptr = cptr;
cip->ctl_id = ctl_id;
cip->chptr = sfp;
cip->item_next = NULL;
if (sfp->item_last) {
sfp->item_last->item_next = cip;
} else {
sfp->item_first = cip;
}
sfp->item_last = cip;
sysfs_attr_init(&cip->attr_name.attr);
cip->attr_name.attr.name = "name";
cip->attr_name.attr.mode = S_IRUGO;
cip->attr_name.show = show_name;
sysfs_attr_init(&cip->attr_type.attr);
cip->attr_type.attr.name = "type";
cip->attr_type.attr.mode = S_IRUGO;
cip->attr_type.show = show_type;
sysfs_attr_init(&cip->attr_min.attr);
cip->attr_min.attr.name = "min_val";
cip->attr_min.attr.mode = S_IRUGO;
cip->attr_min.show = show_min;
sysfs_attr_init(&cip->attr_max.attr);
cip->attr_max.attr.name = "max_val";
cip->attr_max.attr.mode = S_IRUGO;
cip->attr_max.show = show_max;
sysfs_attr_init(&cip->attr_def.attr);
cip->attr_def.attr.name = "def_val";
cip->attr_def.attr.mode = S_IRUGO;
cip->attr_def.show = show_def;
sysfs_attr_init(&cip->attr_val.attr);
cip->attr_val.attr.name = "cur_val";
cip->attr_val.attr.mode = S_IRUGO;
sysfs_attr_init(&cip->attr_custom.attr);
cip->attr_custom.attr.name = "custom_val";
cip->attr_custom.attr.mode = S_IRUGO;
sysfs_attr_init(&cip->attr_enum.attr);
cip->attr_enum.attr.name = "enum_val";
cip->attr_enum.attr.mode = S_IRUGO;
cip->attr_enum.show = show_enum;
sysfs_attr_init(&cip->attr_bits.attr);
cip->attr_bits.attr.name = "bit_val";
cip->attr_bits.attr.mode = S_IRUGO;
cip->attr_bits.show = 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_gen[acnt++] = &cip->attr_def.attr;
cip->attr_val.show = show_val_norm;
cip->attr_val.store = store_val_norm;
if (pvr2_ctrl_has_custom_symbols(cptr)) {
cip->attr_gen[acnt++] = &cip->attr_custom.attr;
cip->attr_custom.show = show_val_custom;
cip->attr_custom.store = 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;
ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"sysfs_create_group error: %d",
ret);
return;
}
cip->created_ok = !0;
}
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
static ssize_t debuginfo_show(struct device *, struct device_attribute *,
char *);
static ssize_t debugcmd_show(struct device *, struct device_attribute *,
char *);
static ssize_t debugcmd_store(struct device *, struct device_attribute *,
const char *, size_t count);
static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp)
{
struct pvr2_sysfs_debugifc *dip;
int ret;
dip = kzalloc(sizeof(*dip),GFP_KERNEL);
if (!dip) return;
sysfs_attr_init(&dip->attr_debugcmd.attr);
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;
sysfs_attr_init(&dip->attr_debuginfo.attr);
dip->attr_debuginfo.attr.name = "debuginfo";
dip->attr_debuginfo.attr.mode = S_IRUGO;
dip->attr_debuginfo.show = debuginfo_show;
sfp->debugifc = dip;
ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
dip->debugcmd_created_ok = !0;
}
ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
dip->debuginfo_created_ok = !0;
}
}
static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp)
{
if (!sfp->debugifc) return;
if (sfp->debugifc->debuginfo_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->debugifc->attr_debuginfo);
}
if (sfp->debugifc->debugcmd_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->debugifc->attr_debugcmd);
}
kfree(sfp->debugifc);
sfp->debugifc = NULL;
}
#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;
if (cip1->created_ok) {
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 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)
{
struct device *dev;
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);
if (sfp->hdw_desc_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_hdw_desc);
}
if (sfp->hdw_name_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_hdw_name);
}
if (sfp->bus_info_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_bus_info);
}
if (sfp->v4l_minor_number_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_v4l_minor_number);
}
if (sfp->v4l_radio_minor_number_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_v4l_radio_minor_number);
}
if (sfp->unit_number_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_unit_number);
}
pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev);
dev_set_drvdata(sfp->class_dev, NULL);
dev = sfp->class_dev->parent;
sfp->class_dev->parent = NULL;
put_device(dev);
device_unregister(sfp->class_dev);
sfp->class_dev = NULL;
}
static ssize_t v4l_minor_number_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return scnprintf(buf,PAGE_SIZE,"%d\n",
pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw,
pvr2_v4l_type_video));
}
static ssize_t bus_info_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return scnprintf(buf,PAGE_SIZE,"%s\n",
pvr2_hdw_get_bus_info(sfp->channel.hdw));
}
static ssize_t hdw_name_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return scnprintf(buf,PAGE_SIZE,"%s\n",
pvr2_hdw_get_type(sfp->channel.hdw));
}
static ssize_t hdw_desc_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return scnprintf(buf,PAGE_SIZE,"%s\n",
pvr2_hdw_get_desc(sfp->channel.hdw));
}
static ssize_t v4l_radio_minor_number_show(struct device *class_dev,
struct device_attribute *attr,
char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return scnprintf(buf,PAGE_SIZE,"%d\n",
pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw,
pvr2_v4l_type_radio));
}
static ssize_t unit_number_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
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 device *class_dev;
int ret;
usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw);
if (!usb_dev) return;
class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL);
if (!class_dev) return;
pvr2_sysfs_trace("Creating class_dev id=%p",class_dev);
class_dev->class = &class_ptr->class;
dev_set_name(class_dev, "%s",
pvr2_hdw_get_device_identifier(sfp->channel.hdw));
class_dev->parent = get_device(&usb_dev->dev);
sfp->class_dev = class_dev;
dev_set_drvdata(class_dev, sfp);
ret = device_register(class_dev);
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_register failed");
put_device(class_dev);
return;
}
sysfs_attr_init(&sfp->attr_v4l_minor_number.attr);
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 = NULL;
ret = device_create_file(sfp->class_dev,
&sfp->attr_v4l_minor_number);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->v4l_minor_number_created_ok = !0;
}
sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr);
sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number";
sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO;
sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show;
sfp->attr_v4l_radio_minor_number.store = NULL;
ret = device_create_file(sfp->class_dev,
&sfp->attr_v4l_radio_minor_number);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->v4l_radio_minor_number_created_ok = !0;
}
sysfs_attr_init(&sfp->attr_unit_number.attr);
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 = NULL;
ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->unit_number_created_ok = !0;
}
sysfs_attr_init(&sfp->attr_bus_info.attr);
sfp->attr_bus_info.attr.name = "bus_info_str";
sfp->attr_bus_info.attr.mode = S_IRUGO;
sfp->attr_bus_info.show = bus_info_show;
sfp->attr_bus_info.store = NULL;
ret = device_create_file(sfp->class_dev,
&sfp->attr_bus_info);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->bus_info_created_ok = !0;
}
sysfs_attr_init(&sfp->attr_hdw_name.attr);
sfp->attr_hdw_name.attr.name = "device_hardware_type";
sfp->attr_hdw_name.attr.mode = S_IRUGO;
sfp->attr_hdw_name.show = hdw_name_show;
sfp->attr_hdw_name.store = NULL;
ret = device_create_file(sfp->class_dev,
&sfp->attr_hdw_name);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->hdw_name_created_ok = !0;
}
sysfs_attr_init(&sfp->attr_hdw_desc.attr);
sfp->attr_hdw_desc.attr.name = "device_hardware_description";
sfp->attr_hdw_desc.attr.mode = S_IRUGO;
sfp->attr_hdw_desc.show = hdw_desc_show;
sfp->attr_hdw_desc.store = NULL;
ret = device_create_file(sfp->class_dev,
&sfp->attr_hdw_desc);
if (ret < 0) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_create_file error: %d",
ret);
} else {
sfp->hdw_desc_created_ok = !0;
}
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 = kzalloc(sizeof(*sfp),GFP_KERNEL);
if (!sfp) return 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;
}
struct pvr2_sysfs_class *pvr2_sysfs_class_create(void)
{
struct pvr2_sysfs_class *clp;
clp = kzalloc(sizeof(*clp),GFP_KERNEL);
if (!clp) return clp;
pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p",
clp);
clp->class.name = "pvrusb2";
clp->class.class_release = pvr2_sysfs_class_release;
clp->class.dev_release = pvr2_sysfs_release;
if (class_register(&clp->class)) {
pvr2_sysfs_trace(
"Registration failed for pvr2_sysfs_class id=%p",clp);
kfree(clp);
clp = NULL;
}
return clp;
}
void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp)
{
pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp);
class_unregister(&clp->class);
}
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
static ssize_t debuginfo_show(struct device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
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 device *class_dev,
struct device_attribute *attr, char *buf)
{
struct pvr2_sysfs *sfp;
sfp = dev_get_drvdata(class_dev);
if (!sfp) return -EINVAL;
return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE);
}
static ssize_t debugcmd_store(struct device *class_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pvr2_sysfs *sfp;
int ret;
sfp = dev_get_drvdata(class_dev);
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: ***
*/

查看文件

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

查看文件

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

檔案差異因為檔案過大而無法顯示 載入差異

查看文件

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

查看文件

@@ -0,0 +1,114 @@
/*
*
*
* 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-hdw-internal.h"
#include "pvrusb2-debug.h"
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/saa7115.h>
#include <linux/errno.h>
struct routing_scheme {
const int *def;
unsigned int cnt;
};
static const int routing_scheme0[] = {
[PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
/* In radio mode, we mute the video, but point at one
spot just to stay consistent */
[PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
[PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5,
[PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2,
};
static const struct routing_scheme routing_def0 = {
.def = routing_scheme0,
.cnt = ARRAY_SIZE(routing_scheme0),
};
static const int routing_scheme1[] = {
[PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
[PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
[PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3,
[PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */
};
static const struct routing_scheme routing_def1 = {
.def = routing_scheme1,
.cnt = ARRAY_SIZE(routing_scheme1),
};
static const struct routing_scheme *routing_schemes[] = {
[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
[PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1,
};
void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
{
if (hdw->input_dirty || hdw->force_dirty) {
const struct routing_scheme *sp;
unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
u32 input;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
hdw->input_val);
sp = (sid < ARRAY_SIZE(routing_schemes)) ?
routing_schemes[sid] : NULL;
if ((sp == NULL) ||
(hdw->input_val < 0) ||
(hdw->input_val >= sp->cnt)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"*** WARNING *** subdev v4l2 set_input:"
" Invalid routing scheme (%u)"
" and/or input (%d)",
sid, hdw->input_val);
return;
}
input = sp->def[hdw->input_val];
sd->ops->video->s_routing(sd, input, 0, 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: ***
*/

查看文件

@@ -0,0 +1,48 @@
/*
*
*
* 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-hdw-internal.h"
void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
#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: ***
*/

查看文件

@@ -0,0 +1,70 @@
/*
*
*
* 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-hdw-internal.h"
#include "pvrusb2-debug.h"
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/errno.h>
void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
{
if (hdw->input_dirty || hdw->force_dirty) {
u32 input;
switch (hdw->input_val) {
case PVR2_CVAL_INPUT_RADIO:
input = 1;
break;
default:
/* All other cases just use the second input */
input = 2;
break;
}
pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775"
" set_input(val=%d route=0x%x)",
hdw->input_val, input);
sd->ops->audio->s_routing(sd, input, 0, 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: ***
*/

查看文件

@@ -0,0 +1,52 @@
/*
*
*
* 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-hdw-internal.h"
void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
#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: ***
*/

查看文件

@@ -0,0 +1,42 @@
/*
*
*
* 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 parameters 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: ***
*/