mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
V4L/DVB (11154): pvrusb2: Split i2c module handling from i2c adapter
This is the first step in the effort to move the pvrusb2 driver over to using the v4l2-subdev framework. This commit involves mainly splitting apart pvrusb2-i2c-core - part of it is the driver's I2C adapter driver and the rest is the old i2c module handling logic. The i2c module handling junk is moved out to pvrusb2-i2c-track and various header references are correspondingly updated. Yes, this patch has a huge pile of checkpatch complaints, but I'm NOT going to fix any of it. Why? First, I'm moving a large chunk of existing code and I'm not going to spend time adjusting it to match someone's idea of coding style. Second, in the end I expect all that moved code to go away by the time the rework is done so wasting time on it now to adhere to the standard is in the end a large waste of time. Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
c457377a3a
commit
59af336795
14 changed files with 595 additions and 476 deletions
|
@ -3,6 +3,7 @@ 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-i2c-cmd-v4l2.o \
|
||||
pvrusb2-i2c-track.o \
|
||||
pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
|
||||
pvrusb2-encoder.o pvrusb2-video-v4l.o \
|
||||
pvrusb2-eeprom.o pvrusb2-tuner.o \
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef __PVRUSB2_AUDIO_H
|
||||
#define __PVRUSB2_AUDIO_H
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "pvrusb2-debugifc.h"
|
||||
#include "pvrusb2-hdw.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
struct debugifc_mask_item {
|
||||
const char *name;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "pvrusb2-util.h"
|
||||
#include "pvrusb2-hdw.h"
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
#include "pvrusb2-tuner.h"
|
||||
#include "pvrusb2-eeprom.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
|
@ -1990,6 +1991,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
|
|||
}
|
||||
|
||||
// This step MUST happen after the earlier powerup step.
|
||||
pvr2_i2c_track_init(hdw);
|
||||
pvr2_i2c_core_init(hdw);
|
||||
if (!pvr2_hdw_dev_ok(hdw)) return;
|
||||
|
||||
|
@ -2501,6 +2503,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
|
|||
hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt);
|
||||
}
|
||||
pvr2_i2c_core_done(hdw);
|
||||
pvr2_i2c_track_done(hdw);
|
||||
pvr2_hdw_remove_usb_stuff(hdw);
|
||||
mutex_lock(&pvr2_unit_mtx); do {
|
||||
if ((hdw->unit_number >= 0) &&
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include "pvrusb2-i2c-cmd-v4l2.h"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef __PVRUSB2_CMD_V4L2_H
|
||||
#define __PVRUSB2_CMD_V4L2_H
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_init;
|
||||
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard;
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
#include "pvrusb2-hdw-internal.h"
|
||||
#include "pvrusb2-debug.h"
|
||||
#include "pvrusb2-fx2-cmd.h"
|
||||
|
@ -29,8 +31,7 @@
|
|||
/*
|
||||
|
||||
This module attempts to implement a compliant I2C adapter for the pvrusb2
|
||||
device. By doing this we can then make use of existing functionality in
|
||||
V4L (e.g. tuner.c) rather than rolling our own.
|
||||
device.
|
||||
|
||||
*/
|
||||
|
||||
|
@ -42,10 +43,6 @@ 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 unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
|
||||
unsigned int detail,
|
||||
char *buf,unsigned int maxlen);
|
||||
|
||||
static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
|
||||
u8 i2c_addr, /* I2C address we're talking to */
|
||||
u8 *data, /* Data to write */
|
||||
|
@ -524,414 +521,15 @@ static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
|
|||
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_core_singleton(struct i2c_client *cp,
|
||||
unsigned int cmd,void *arg)
|
||||
{
|
||||
int stat;
|
||||
if (!cp) return -EINVAL;
|
||||
if (!(cp->driver)) return -EINVAL;
|
||||
if (!(cp->driver->command)) return -EINVAL;
|
||||
if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;
|
||||
stat = cp->driver->command(cp,cmd,arg);
|
||||
module_put(cp->driver->driver.owner);
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
|
||||
{
|
||||
int stat;
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND (code=%u 0x%x) to %.*s",
|
||||
cmd,cmd,cnt,buf);
|
||||
}
|
||||
stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
|
||||
{
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
int stat = -EINVAL;
|
||||
|
||||
if (!hdw) return stat;
|
||||
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
|
||||
if (!cp->recv_enable) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
stat = pvr2_i2c_client_cmd(cp,cmd,arg);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
static int handler_check(struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_i2c_handler *hp = cp->handler;
|
||||
if (!hp) return 0;
|
||||
if (!hp->func_table->check) return 0;
|
||||
return hp->func_table->check(hp->func_data) != 0;
|
||||
}
|
||||
|
||||
#define BUFSIZE 500
|
||||
|
||||
|
||||
void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct pvr2_i2c_client *cp;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
|
||||
memset(vtp,0,sizeof(*vtp));
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
if (!cp->detected_flag) continue;
|
||||
if (!cp->status_poll) continue;
|
||||
cp->status_poll(cp);
|
||||
}
|
||||
hdw->tuner_signal_stale = 0;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll"
|
||||
" type=%u strength=%u audio=0x%x cap=0x%x"
|
||||
" low=%u hi=%u",
|
||||
vtp->type,
|
||||
vtp->signal,vtp->rxsubchans,vtp->capability,
|
||||
vtp->rangelow,vtp->rangehigh);
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Issue various I2C operations to bring chip-level drivers into sync with
|
||||
state stored in this driver. */
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
|
||||
if (!hdw->i2c_linked) return;
|
||||
if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
|
||||
return;
|
||||
}
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
|
||||
/* One or more I2C clients have attached since we
|
||||
last synced. So scan the list and identify the
|
||||
new clients. */
|
||||
char *buf;
|
||||
unsigned int cnt;
|
||||
unsigned long amask = 0;
|
||||
buf = kmalloc(BUFSIZE,GFP_KERNEL);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
if (!cp->detected_flag) {
|
||||
cp->ctl_mask = 0;
|
||||
pvr2_i2c_probe(hdw,cp);
|
||||
cp->detected_flag = !0;
|
||||
msk = cp->ctl_mask;
|
||||
cnt = 0;
|
||||
if (buf) {
|
||||
cnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
PVR2_I2C_DETAIL_ALL,
|
||||
buf,BUFSIZE);
|
||||
}
|
||||
trace_i2c("Probed: %.*s",cnt,buf);
|
||||
if (handler_check(cp)) {
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_CLIENT;
|
||||
}
|
||||
cp->pend_mask = msk;
|
||||
hdw->i2c_pend_mask |= msk;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
if (buf) kfree(buf);
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
|
||||
/* Need to do one or more global updates. Arrange
|
||||
for this to happen. */
|
||||
unsigned long m2;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: PEND_STALE (0x%lx)",
|
||||
hdw->i2c_stale_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
m2 = hdw->i2c_stale_mask;
|
||||
m2 &= cp->ctl_mask;
|
||||
m2 &= ~cp->pend_mask;
|
||||
if (m2) {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p setting 0x%lx",
|
||||
cp,m2);
|
||||
cp->pend_mask |= m2;
|
||||
}
|
||||
}
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
|
||||
/* One or more client handlers are asking for an
|
||||
update. Run through the list of known clients
|
||||
and update each one. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients,
|
||||
list) {
|
||||
if (!cp->handler) continue;
|
||||
if (!cp->handler->func_table->update) continue;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p update",cp);
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
cp->handler->func_table->update(
|
||||
cp->handler->func_data);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
/* If client's update function set some
|
||||
additional pending bits, account for that
|
||||
here. */
|
||||
if (cp->pend_mask & ~hdw->i2c_pend_mask) {
|
||||
hdw->i2c_pend_mask |= cp->pend_mask;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
|
||||
const struct pvr2_i2c_op *opf;
|
||||
unsigned long pm;
|
||||
/* Some actual updates are pending. Walk through
|
||||
each update type and perform it. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
|
||||
" (0x%lx)",hdw->i2c_pend_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
|
||||
pm = hdw->i2c_pend_mask;
|
||||
hdw->i2c_pend_mask = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(pm & msk)) continue;
|
||||
pm &= ~msk;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients,
|
||||
list) {
|
||||
if (cp->pend_mask & msk) {
|
||||
cp->pend_mask &= ~msk;
|
||||
cp->recv_enable = !0;
|
||||
} else {
|
||||
cp->recv_enable = 0;
|
||||
}
|
||||
}
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!opf) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
opf->update(hdw);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
}
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk,sm,pm;
|
||||
unsigned int idx;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
struct pvr2_i2c_client *cp;
|
||||
unsigned int pt = 0;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
|
||||
|
||||
pm = hdw->i2c_active_mask;
|
||||
sm = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(msk & pm)) continue;
|
||||
pm &= ~msk;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!(opf && opf->check)) continue;
|
||||
if (opf->check(hdw)) {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) pt |= PVR2_I2C_PEND_STALE;
|
||||
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list)
|
||||
if (handler_check(cp))
|
||||
pt |= PVR2_I2C_PEND_CLIENT;
|
||||
|
||||
if (pt) {
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->i2c_pend_types |= pt;
|
||||
hdw->i2c_stale_mask |= sm;
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: types=0x%x stale=0x%lx pend=0x%lx",
|
||||
hdw->i2c_pend_types,
|
||||
hdw->i2c_stale_mask,
|
||||
hdw->i2c_pend_mask);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
|
||||
|
||||
return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
|
||||
}
|
||||
|
||||
static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
|
||||
unsigned int detail,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
int spcfl = 0;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
|
||||
ccnt = 0;
|
||||
if (detail & PVR2_I2C_DETAIL_DEBUG) {
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"ctxt=%p ctl_mask=0x%lx",
|
||||
cp,cp->ctl_mask);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"%s%s @ 0x%x",
|
||||
(spcfl ? " " : ""),
|
||||
cp->client->name,
|
||||
cp->client->addr);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
|
||||
cp->handler && cp->handler->func_table->describe) {
|
||||
bcnt = scnprintf(buf,maxlen," (");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = cp->handler->func_table->describe(
|
||||
cp->handler->func_data,buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,")");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
|
||||
unsigned int idx;
|
||||
unsigned long msk,sm;
|
||||
|
||||
bcnt = scnprintf(buf,maxlen," [");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
sm = 0;
|
||||
spcfl = 0;
|
||||
for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
|
||||
if (!(cp->ctl_mask & msk)) continue;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (opf) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%s",
|
||||
spcfl ? " " : "",
|
||||
opf->name);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
} else {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%lx",
|
||||
idx != 0 ? " " : "",sm);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,"]");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
struct pvr2_i2c_client *cp;
|
||||
ccnt = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
bcnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
(PVR2_I2C_DETAIL_HANDLER|
|
||||
PVR2_I2C_DETAIL_CTLMASK),
|
||||
buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,"\n");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_attach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp;
|
||||
int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
|
||||
cp = kzalloc(sizeof(*cp),GFP_KERNEL);
|
||||
trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (!cp) return -ENOMEM;
|
||||
cp->hdw = hdw;
|
||||
INIT_LIST_HEAD(&cp->list);
|
||||
cp->client = client;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->cropcap_stale = !0;
|
||||
list_add_tail(&cp->list,&hdw->i2c_clients);
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
|
||||
pvr2_i2c_track_attach_inform(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvr2_i2c_detach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
unsigned long amask = 0;
|
||||
int foundfl = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->cropcap_stale = !0;
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
|
||||
if (cp->client == client) {
|
||||
trace_i2c("pvr2_i2c_detach"
|
||||
" [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (cp->handler &&
|
||||
cp->handler->func_table->detach) {
|
||||
cp->handler->func_table->detach(
|
||||
cp->handler->func_data);
|
||||
}
|
||||
list_del(&cp->list);
|
||||
kfree(cp);
|
||||
foundfl = !0;
|
||||
continue;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (!foundfl) {
|
||||
trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
|
||||
client->name,
|
||||
client->addr);
|
||||
}
|
||||
pvr2_i2c_track_detach_inform(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1009,11 +607,6 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
|
|||
hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev;
|
||||
hdw->i2c_adap.algo = &hdw->i2c_algo;
|
||||
hdw->i2c_adap.algo_data = hdw;
|
||||
hdw->i2c_pend_mask = 0;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_active_mask = 0;
|
||||
INIT_LIST_HEAD(&hdw->i2c_clients);
|
||||
mutex_init(&hdw->i2c_list_lock);
|
||||
hdw->i2c_linked = !0;
|
||||
i2c_add_adapter(&hdw->i2c_adap);
|
||||
if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
|
||||
|
|
|
@ -20,68 +20,13 @@
|
|||
#ifndef __PVRUSB2_I2C_CORE_H
|
||||
#define __PVRUSB2_I2C_CORE_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
struct pvr2_hdw;
|
||||
struct pvr2_i2c_client;
|
||||
struct pvr2_i2c_handler;
|
||||
struct pvr2_i2c_handler_functions;
|
||||
struct pvr2_i2c_op;
|
||||
struct pvr2_i2c_op_functions;
|
||||
|
||||
struct pvr2_i2c_client {
|
||||
struct i2c_client *client;
|
||||
struct pvr2_i2c_handler *handler;
|
||||
struct list_head list;
|
||||
struct pvr2_hdw *hdw;
|
||||
int detected_flag;
|
||||
int recv_enable;
|
||||
unsigned long pend_mask;
|
||||
unsigned long ctl_mask;
|
||||
void (*status_poll)(struct pvr2_i2c_client *);
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler {
|
||||
void *func_data;
|
||||
const struct pvr2_i2c_handler_functions *func_table;
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler_functions {
|
||||
void (*detach)(void *);
|
||||
int (*check)(void *);
|
||||
void (*update)(void *);
|
||||
unsigned int (*describe)(void *,char *,unsigned int);
|
||||
};
|
||||
|
||||
struct pvr2_i2c_op {
|
||||
int (*check)(struct pvr2_hdw *);
|
||||
void (*update)(struct pvr2_hdw *);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
void pvr2_i2c_core_init(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_done(struct pvr2_hdw *);
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_status_poll(struct pvr2_hdw *);
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
|
||||
#define PVR2_I2C_DETAIL_DEBUG 0x0001
|
||||
#define PVR2_I2C_DETAIL_HANDLER 0x0002
|
||||
#define PVR2_I2C_DETAIL_CTLMASK 0x0004
|
||||
#define PVR2_I2C_DETAIL_ALL (\
|
||||
PVR2_I2C_DETAIL_DEBUG |\
|
||||
PVR2_I2C_DETAIL_HANDLER |\
|
||||
PVR2_I2C_DETAIL_CTLMASK)
|
||||
|
||||
void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
|
||||
|
||||
#endif /* __PVRUSB2_I2C_CORE_H */
|
||||
#endif /* __PVRUSB2_I2C_ADAPTER_H */
|
||||
|
||||
|
||||
/*
|
||||
|
|
480
drivers/media/video/pvrusb2/pvrusb2-i2c-track.c
Normal file
480
drivers/media/video/pvrusb2/pvrusb2-i2c-track.c
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "pvrusb2-i2c-track.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 implements the foundation of a rather large architecture for
|
||||
tracking state in all the various V4L I2C modules. This is obsolete with
|
||||
kernels later than roughly 2.6.24, but it is still present in the
|
||||
standalone pvrusb2 driver to allow continued operation with older
|
||||
kernel.
|
||||
|
||||
*/
|
||||
|
||||
static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
|
||||
unsigned int detail,
|
||||
char *buf,unsigned int maxlen);
|
||||
|
||||
static int pvr2_i2c_core_singleton(struct i2c_client *cp,
|
||||
unsigned int cmd,void *arg)
|
||||
{
|
||||
int stat;
|
||||
if (!cp) return -EINVAL;
|
||||
if (!(cp->driver)) return -EINVAL;
|
||||
if (!(cp->driver->command)) return -EINVAL;
|
||||
if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;
|
||||
stat = cp->driver->command(cp,cmd,arg);
|
||||
module_put(cp->driver->driver.owner);
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
|
||||
{
|
||||
int stat;
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND (code=%u 0x%x) to %.*s",
|
||||
cmd,cmd,cnt,buf);
|
||||
}
|
||||
stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
|
||||
if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
|
||||
char buf[100];
|
||||
unsigned int cnt;
|
||||
cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
|
||||
buf,sizeof(buf));
|
||||
pvr2_trace(PVR2_TRACE_I2C_CMD,
|
||||
"i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
|
||||
{
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
int stat = -EINVAL;
|
||||
|
||||
if (!hdw) return stat;
|
||||
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
|
||||
if (!cp->recv_enable) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
stat = pvr2_i2c_client_cmd(cp,cmd,arg);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
static int handler_check(struct pvr2_i2c_client *cp)
|
||||
{
|
||||
struct pvr2_i2c_handler *hp = cp->handler;
|
||||
if (!hp) return 0;
|
||||
if (!hp->func_table->check) return 0;
|
||||
return hp->func_table->check(hp->func_data) != 0;
|
||||
}
|
||||
|
||||
#define BUFSIZE 500
|
||||
|
||||
|
||||
void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw)
|
||||
{
|
||||
struct pvr2_i2c_client *cp;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
|
||||
memset(vtp,0,sizeof(*vtp));
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
if (!cp->detected_flag) continue;
|
||||
if (!cp->status_poll) continue;
|
||||
cp->status_poll(cp);
|
||||
}
|
||||
hdw->tuner_signal_stale = 0;
|
||||
pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll"
|
||||
" type=%u strength=%u audio=0x%x cap=0x%x"
|
||||
" low=%u hi=%u",
|
||||
vtp->type,
|
||||
vtp->signal,vtp->rxsubchans,vtp->capability,
|
||||
vtp->rangelow,vtp->rangehigh);
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Issue various I2C operations to bring chip-level drivers into sync with
|
||||
state stored in this driver. */
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk;
|
||||
unsigned int idx;
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
|
||||
if (!hdw->i2c_linked) return;
|
||||
if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
|
||||
return;
|
||||
}
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
|
||||
/* One or more I2C clients have attached since we
|
||||
last synced. So scan the list and identify the
|
||||
new clients. */
|
||||
char *buf;
|
||||
unsigned int cnt;
|
||||
unsigned long amask = 0;
|
||||
buf = kmalloc(BUFSIZE,GFP_KERNEL);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
if (!cp->detected_flag) {
|
||||
cp->ctl_mask = 0;
|
||||
pvr2_i2c_probe(hdw,cp);
|
||||
cp->detected_flag = !0;
|
||||
msk = cp->ctl_mask;
|
||||
cnt = 0;
|
||||
if (buf) {
|
||||
cnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
PVR2_I2C_DETAIL_ALL,
|
||||
buf,BUFSIZE);
|
||||
}
|
||||
trace_i2c("Probed: %.*s",cnt,buf);
|
||||
if (handler_check(cp)) {
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_CLIENT;
|
||||
}
|
||||
cp->pend_mask = msk;
|
||||
hdw->i2c_pend_mask |= msk;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
if (buf) kfree(buf);
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
|
||||
/* Need to do one or more global updates. Arrange
|
||||
for this to happen. */
|
||||
unsigned long m2;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: PEND_STALE (0x%lx)",
|
||||
hdw->i2c_stale_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
m2 = hdw->i2c_stale_mask;
|
||||
m2 &= cp->ctl_mask;
|
||||
m2 &= ~cp->pend_mask;
|
||||
if (m2) {
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p setting 0x%lx",
|
||||
cp,m2);
|
||||
cp->pend_mask |= m2;
|
||||
}
|
||||
}
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
|
||||
/* One or more client handlers are asking for an
|
||||
update. Run through the list of known clients
|
||||
and update each one. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients,
|
||||
list) {
|
||||
if (!cp->handler) continue;
|
||||
if (!cp->handler->func_table->update) continue;
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: cp=%p update",cp);
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
cp->handler->func_table->update(
|
||||
cp->handler->func_data);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
/* If client's update function set some
|
||||
additional pending bits, account for that
|
||||
here. */
|
||||
if (cp->pend_mask & ~hdw->i2c_pend_mask) {
|
||||
hdw->i2c_pend_mask |= cp->pend_mask;
|
||||
hdw->i2c_pend_types |=
|
||||
PVR2_I2C_PEND_REFRESH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
|
||||
const struct pvr2_i2c_op *opf;
|
||||
unsigned long pm;
|
||||
/* Some actual updates are pending. Walk through
|
||||
each update type and perform it. */
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
|
||||
" (0x%lx)",hdw->i2c_pend_mask);
|
||||
hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
|
||||
pm = hdw->i2c_pend_mask;
|
||||
hdw->i2c_pend_mask = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(pm & msk)) continue;
|
||||
pm &= ~msk;
|
||||
list_for_each_entry(cp, &hdw->i2c_clients,
|
||||
list) {
|
||||
if (cp->pend_mask & msk) {
|
||||
cp->pend_mask &= ~msk;
|
||||
cp->recv_enable = !0;
|
||||
} else {
|
||||
cp->recv_enable = 0;
|
||||
}
|
||||
}
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!opf) continue;
|
||||
mutex_unlock(&hdw->i2c_list_lock);
|
||||
opf->update(hdw);
|
||||
mutex_lock(&hdw->i2c_list_lock);
|
||||
}
|
||||
}
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
|
||||
{
|
||||
unsigned long msk,sm,pm;
|
||||
unsigned int idx;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
struct pvr2_i2c_client *cp;
|
||||
unsigned int pt = 0;
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
|
||||
|
||||
pm = hdw->i2c_active_mask;
|
||||
sm = 0;
|
||||
for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
|
||||
if (!(msk & pm)) continue;
|
||||
pm &= ~msk;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (!(opf && opf->check)) continue;
|
||||
if (opf->check(hdw)) {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) pt |= PVR2_I2C_PEND_STALE;
|
||||
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list)
|
||||
if (handler_check(cp))
|
||||
pt |= PVR2_I2C_PEND_CLIENT;
|
||||
|
||||
if (pt) {
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->i2c_pend_types |= pt;
|
||||
hdw->i2c_stale_mask |= sm;
|
||||
hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,
|
||||
"i2c: types=0x%x stale=0x%lx pend=0x%lx",
|
||||
hdw->i2c_pend_types,
|
||||
hdw->i2c_stale_mask,
|
||||
hdw->i2c_pend_mask);
|
||||
pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
|
||||
|
||||
return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
|
||||
}
|
||||
|
||||
static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
|
||||
unsigned int detail,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
int spcfl = 0;
|
||||
const struct pvr2_i2c_op *opf;
|
||||
|
||||
ccnt = 0;
|
||||
if (detail & PVR2_I2C_DETAIL_DEBUG) {
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"ctxt=%p ctl_mask=0x%lx",
|
||||
cp,cp->ctl_mask);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,
|
||||
"%s%s @ 0x%x",
|
||||
(spcfl ? " " : ""),
|
||||
cp->client->name,
|
||||
cp->client->addr);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
|
||||
cp->handler && cp->handler->func_table->describe) {
|
||||
bcnt = scnprintf(buf,maxlen," (");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = cp->handler->func_table->describe(
|
||||
cp->handler->func_data,buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,")");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
|
||||
unsigned int idx;
|
||||
unsigned long msk,sm;
|
||||
|
||||
bcnt = scnprintf(buf,maxlen," [");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
sm = 0;
|
||||
spcfl = 0;
|
||||
for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
|
||||
if (!(cp->ctl_mask & msk)) continue;
|
||||
opf = pvr2_i2c_get_op(idx);
|
||||
if (opf) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%s",
|
||||
spcfl ? " " : "",
|
||||
opf->name);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
spcfl = !0;
|
||||
} else {
|
||||
sm |= msk;
|
||||
}
|
||||
}
|
||||
if (sm) {
|
||||
bcnt = scnprintf(buf,maxlen,"%s%lx",
|
||||
idx != 0 ? " " : "",sm);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
bcnt = scnprintf(buf,maxlen,"]");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
|
||||
char *buf,unsigned int maxlen)
|
||||
{
|
||||
unsigned int ccnt,bcnt;
|
||||
struct pvr2_i2c_client *cp;
|
||||
ccnt = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
list_for_each_entry(cp, &hdw->i2c_clients, list) {
|
||||
bcnt = pvr2_i2c_client_describe(
|
||||
cp,
|
||||
(PVR2_I2C_DETAIL_HANDLER|
|
||||
PVR2_I2C_DETAIL_CTLMASK),
|
||||
buf,maxlen);
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
bcnt = scnprintf(buf,maxlen,"\n");
|
||||
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
|
||||
}
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
return ccnt;
|
||||
}
|
||||
|
||||
void pvr2_i2c_track_attach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp;
|
||||
int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
|
||||
cp = kzalloc(sizeof(*cp),GFP_KERNEL);
|
||||
trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (!cp) {
|
||||
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
||||
"Unable to allocate tracking memory for incoming"
|
||||
" i2c module; ignoring module. This is likely"
|
||||
" going to be a problem.");
|
||||
return;
|
||||
}
|
||||
cp->hdw = hdw;
|
||||
INIT_LIST_HEAD(&cp->list);
|
||||
cp->client = client;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->cropcap_stale = !0;
|
||||
list_add_tail(&cp->list,&hdw->i2c_clients);
|
||||
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
|
||||
}
|
||||
|
||||
void pvr2_i2c_track_detach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
|
||||
struct pvr2_i2c_client *cp, *ncp;
|
||||
unsigned long amask = 0;
|
||||
int foundfl = 0;
|
||||
mutex_lock(&hdw->i2c_list_lock); do {
|
||||
hdw->cropcap_stale = !0;
|
||||
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
|
||||
if (cp->client == client) {
|
||||
trace_i2c("pvr2_i2c_detach"
|
||||
" [client=%s @ 0x%x ctxt=%p]",
|
||||
client->name,
|
||||
client->addr,cp);
|
||||
if (cp->handler &&
|
||||
cp->handler->func_table->detach) {
|
||||
cp->handler->func_table->detach(
|
||||
cp->handler->func_data);
|
||||
}
|
||||
list_del(&cp->list);
|
||||
kfree(cp);
|
||||
foundfl = !0;
|
||||
continue;
|
||||
}
|
||||
amask |= cp->ctl_mask;
|
||||
}
|
||||
hdw->i2c_active_mask = amask;
|
||||
} while (0); mutex_unlock(&hdw->i2c_list_lock);
|
||||
if (!foundfl) {
|
||||
trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
|
||||
client->name,
|
||||
client->addr);
|
||||
}
|
||||
}
|
||||
|
||||
void pvr2_i2c_track_init(struct pvr2_hdw *hdw)
|
||||
{
|
||||
hdw->i2c_pend_mask = 0;
|
||||
hdw->i2c_stale_mask = 0;
|
||||
hdw->i2c_active_mask = 0;
|
||||
INIT_LIST_HEAD(&hdw->i2c_clients);
|
||||
mutex_init(&hdw->i2c_list_lock);
|
||||
}
|
||||
|
||||
void pvr2_i2c_track_done(struct pvr2_hdw *hdw)
|
||||
{
|
||||
/* Empty for now */
|
||||
}
|
||||
|
||||
/*
|
||||
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: ***
|
||||
*/
|
97
drivers/media/video/pvrusb2/pvrusb2-i2c-track.h
Normal file
97
drivers/media/video/pvrusb2/pvrusb2-i2c-track.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* 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_TRACK_H
|
||||
#define __PVRUSB2_I2C_TRACK_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
struct pvr2_hdw;
|
||||
struct pvr2_i2c_client;
|
||||
struct pvr2_i2c_handler;
|
||||
struct pvr2_i2c_handler_functions;
|
||||
struct pvr2_i2c_op;
|
||||
struct pvr2_i2c_op_functions;
|
||||
|
||||
struct pvr2_i2c_client {
|
||||
struct i2c_client *client;
|
||||
struct pvr2_i2c_handler *handler;
|
||||
struct list_head list;
|
||||
struct pvr2_hdw *hdw;
|
||||
int detected_flag;
|
||||
int recv_enable;
|
||||
unsigned long pend_mask;
|
||||
unsigned long ctl_mask;
|
||||
void (*status_poll)(struct pvr2_i2c_client *);
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler {
|
||||
void *func_data;
|
||||
const struct pvr2_i2c_handler_functions *func_table;
|
||||
};
|
||||
|
||||
struct pvr2_i2c_handler_functions {
|
||||
void (*detach)(void *);
|
||||
int (*check)(void *);
|
||||
void (*update)(void *);
|
||||
unsigned int (*describe)(void *,char *,unsigned int);
|
||||
};
|
||||
|
||||
struct pvr2_i2c_op {
|
||||
int (*check)(struct pvr2_hdw *);
|
||||
void (*update)(struct pvr2_hdw *);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
void pvr2_i2c_track_init(struct pvr2_hdw *);
|
||||
void pvr2_i2c_track_done(struct pvr2_hdw *);
|
||||
void pvr2_i2c_track_attach_inform(struct i2c_client *);
|
||||
void pvr2_i2c_track_detach_inform(struct i2c_client *);
|
||||
|
||||
int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
|
||||
int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
|
||||
|
||||
int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_sync(struct pvr2_hdw *);
|
||||
void pvr2_i2c_core_status_poll(struct pvr2_hdw *);
|
||||
unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
|
||||
#define PVR2_I2C_DETAIL_DEBUG 0x0001
|
||||
#define PVR2_I2C_DETAIL_HANDLER 0x0002
|
||||
#define PVR2_I2C_DETAIL_CTLMASK 0x0004
|
||||
#define PVR2_I2C_DETAIL_ALL (\
|
||||
PVR2_I2C_DETAIL_DEBUG |\
|
||||
PVR2_I2C_DETAIL_HANDLER |\
|
||||
PVR2_I2C_DETAIL_CTLMASK)
|
||||
|
||||
void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
|
||||
|
||||
#endif /* __PVRUSB2_I2C_CORE_H */
|
||||
|
||||
|
||||
/*
|
||||
Stuff for Emacs to see, in order to encourage consistent editing style:
|
||||
*** Local Variables: ***
|
||||
*** mode: c ***
|
||||
*** fill-column: 75 ***
|
||||
*** tab-width: 8 ***
|
||||
*** c-basic-offset: 8 ***
|
||||
*** End: ***
|
||||
*/
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef __PVRUSB2_TUNER_H
|
||||
#define __PVRUSB2_TUNER_H
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
|
||||
|
||||
#include "pvrusb2-i2c-core.h"
|
||||
#include "pvrusb2-i2c-track.h"
|
||||
|
||||
int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
|
||||
|
||||
|
|
Loading…
Reference in a new issue