pvrusb2-i2c-core.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,124 行 · 第 1/3 页

C
1,124
字号
/* * * *  Copyright (C) 2005 Mike Isely <isely@pobox.com> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */#include "pvrusb2-i2c-core.h"#include "pvrusb2-hdw-internal.h"#include "pvrusb2-debug.h"#include "pvrusb2-fx2-cmd.h"#include "pvrusb2.h"#include "compat.h"#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)/*  This module attempts to implement a compliant I2C adapter for the pvrusb2  device.  By doing this we can then make use of existing functionality in  V4L (e.g. tuner.c) rather than rolling our own.*/static unsigned int i2c_scan;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 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 */			  u16 length)       /* Size of data to write */{	/* Return value - default 0 means success */	int ret;#if 0	trace_i2c("pvr2_i2c_write");#endif	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]);			}		}	}#if 0	trace_i2c("i2c_write(0x%x) len=%d ret=%d stat=%d",i2c_addr,length,ret,		  hdw->cmd_buffer[0]);#endif	LOCK_GIVE(hdw->ctl_lock);	return ret;}static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */			 u8 i2c_addr,       /* I2C address we're talking to */			 u8 *data,          /* Data to write */			 u16 dlen,          /* Size of data to write */			 u8 *res,           /* Where to put data we read */			 u16 rlen)          /* Amount of data to read */{	/* Return value - default 0 means success */	int ret;#if 0	trace_i2c("pvr2_i2c_read");#endif	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]);			}		}	}#if 0	trace_i2c("i2c_read(0x%x) wlen=%d rlen=%d ret=%d stat=%d",		  i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]);#endif	/* Copy back the result */	if (res && rlen) {		if (ret) {			/* Error, just blank out the return buffer */			memset(res, 0, rlen);		} else {			memcpy(res, hdw->cmd_buffer + 1, rlen);		}	}	LOCK_GIVE(hdw->ctl_lock);	return ret;}/* This is 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);	}}#if 0/* This is special code for spying on IR transactions for 29xxx devices.   We use this to reverse-engineer the IR protocol in order to simulate it   correctly for 24xxx devices. */static int i2c_29xxx_ir(struct pvr2_hdw *hdw,			u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen){	char buf[200];	unsigned int c1,c2;	unsigned int idx,stat;	if (!(rlen || wlen)) {		/* This is a probe attempt.  Just let it succeed. */		pvr2_trace(PVR2_TRACE_DEBUG,"IR: probe");		return 0;	}	stat = pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);	if ((wlen == 0) && (rlen == 3) && (stat == 0) &&	    (rdata[0] == 0) && (rdata[1] == 0) && (rdata[2] == 0xc1)) {		return stat;	}	c1 = 0;	c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       "IR: stat=%d",stat);	c1 += c2;	if (wlen) {		c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       " write [");		c1 += c2;		for (idx = 0; idx < wlen; idx++) {			c2 = scnprintf(buf+c1,sizeof(buf)-c1,				       "%s%02x",idx == 0 ? "" : " ",				       wdata[idx]);			c1 += c2;		}		c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       "]");		c1 += c2;	}	if (rlen && (stat == 0)) {		c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       " read [");		c1 += c2;		for (idx = 0; idx < rlen; idx++) {			c2 = scnprintf(buf+c1,sizeof(buf)-c1,				       "%s%02x",idx == 0 ? "" : " ",				       rdata[idx]);			c1 += c2;		}		c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       "]");		c1 += c2;	} else if (rlen) {		c2 = scnprintf(buf+c1,sizeof(buf)-c1,			       " read cnt=%u",rlen);		c1 += c2;	}	pvr2_trace(PVR2_TRACE_DEBUG,"%.*s",c1,buf);	return stat;}#endif/* 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?