⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pvrusb2-i2c-core.c

📁 V4l driver for DVB HD
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *  $Id$ * *  Copyright (C) 2005 Mike Isely <isely@pobox.com> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */#include "compat.h"#include "pvrusb2-i2c-core.h"#include "pvrusb2-hdw-internal.h"#include "pvrusb2-debug.h"#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)/*  This module attempts to implement a compliant I2C adapter for the pvrusb2  device.  By doing this we can then make use of existing functionality in  V4L (e.g. tuner.c) rather than rolling our own.*/static unsigned int i2c_scan = 0;module_param(i2c_scan, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */			  u8 i2c_addr,      /* I2C address we're talking to */			  u8 *data,         /* Data to write */			  u16 length)       /* Size of data to write */{	/* Return value - default 0 means success */	int ret;#if 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] = 0x08;      /* write prefix */	hdw->cmd_buffer[1] = i2c_addr;  /* i2c addr of chip */	hdw->cmd_buffer[2] = length;    /* length of what follows */	if (length) memcpy(hdw->cmd_buffer + 3, data, length);	/* Do the operation */	ret = pvr2_send_request(hdw,				hdw->cmd_buffer,				length + 3,				hdw->cmd_buffer,				1);	if (!ret) {		if (hdw->cmd_buffer[0] != 8) {			ret = -EIO;			if (hdw->cmd_buffer[0] != 7) {				trace_i2c("unexpected status"					  " from i2_write[%d]: %d",					  i2c_addr,hdw->cmd_buffer[0]);			}		}	}#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)) return -ENOTSUPP;	if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP;	LOCK_TAKE(hdw->ctl_lock);	/* Clear the command buffer (likely to be paranoia) */	memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));	/* Set up command buffer for an I2C write followed by a read */	hdw->cmd_buffer[0] = 0x09;  /* read prefix */	hdw->cmd_buffer[1] = dlen;  /* arg length */	hdw->cmd_buffer[2] = rlen;  /* answer length. Device will send one				       more byte (status). */	hdw->cmd_buffer[3] = i2c_addr;  /* i2c addr of chip */	if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen);	/* Do the operation */	ret = pvr2_send_request(hdw,				hdw->cmd_buffer,				4 + dlen,				hdw->cmd_buffer,				rlen + 1);	if (!ret) {		if (hdw->cmd_buffer[0] != 8) {			ret = -EIO;			if (hdw->cmd_buffer[0] != 7) {				trace_i2c("unexpected status"					  " from i2_read[%d]: %d",					  i2c_addr,hdw->cmd_buffer[0]);			}		}	}#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 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;	struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);	if ((msgs[0].flags & I2C_M_NOSTART)) {		trace_i2c("i2c refusing I2C_M_NOSTART");		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 (pvr2_i2c_read(hdw,msgs[0].addr,						  0,0,0,0)) {					ret = -EIO;					goto done;				}				ret = 1;				goto done;			}			/* If the read is short enough we'll do the whole			   thing atomically.  Otherwise we have no choice			   but to break apart the reads. */			tcnt = msgs[0].len;			offs = 0;			while (tcnt) {				bcnt = tcnt;				if (bcnt > sizeof(hdw->cmd_buffer)-1) {					bcnt = sizeof(hdw->cmd_buffer)-1;				}				if (pvr2_i2c_read(hdw,msgs[0].addr,						  0,0,						  msgs[0].buf+offs,bcnt)) {					ret = -EIO;					goto done;				}				offs += bcnt;				tcnt -= bcnt;			}			ret = 1;			goto done;		} else {			/* Simple write */			ret = 1;			if (pvr2_i2c_write(hdw,msgs[0].addr,					   msgs[0].buf,msgs[0].len)) {				ret = -EIO;			}			goto done;		}	} else if (num == 2) {		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 (pvr2_i2c_read(hdw,msgs[0].addr,						  msgs[0].buf,wcnt,						  msgs[1].buf+offs,bcnt)) {					ret = -EIO;					goto done;				}				offs += bcnt;				tcnt -= bcnt;				wcnt = 0;			}			ret = 2;			goto done;		} else {			trace_i2c("i2c refusing complex transfer"				  " read0=%d read1=%d",				  (msgs[0].flags & I2C_M_RD),				  (msgs[1].flags & I2C_M_RD));		}	} else {		trace_i2c("i2c refusing %d phase transfer",num);	} done:	if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) {		unsigned int idx,offs,cnt;		for (idx = 0; idx < num; idx++) {			cnt = msgs[idx].len;			printk(KERN_INFO			       "pvrusb2 i2c xfer %u/%u:"			       " addr=0x%x len=%d %s%s",			       idx+1,num,			       msgs[idx].addr,			       cnt,			       (msgs[idx].flags & I2C_M_RD ?				"read" : "write"),			       (msgs[idx].flags & I2C_M_NOSTART ?				" nostart" : ""));			if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {				if (cnt > 8) cnt = 8;				printk(" [");				for (offs = 0; offs < (cnt>8?8:cnt); offs++) {					if (offs) printk(" ");					printk("%02x",msgs[idx].buf[offs]);				}				if (offs < cnt) printk(" ...");				printk("]");			}			if (idx+1 == num) {				printk(" result=%d",ret);			}			printk("\n");		}		if (!num) {			printk(KERN_INFO			       "pvrusb2 i2c xfer null transfer result=%d\n",			       ret);		}	}	return ret;}static int pvr2_i2c_control(struct i2c_adapter *adapter,			    unsigned int cmd, unsigned long arg){	return 0;}static u32 pvr2_i2c_functionality(struct i2c_adapter *adap){	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA;}static int pvr2_i2c_core_singleton(struct i2c_client *cp,				   unsigned int cmd,void *arg){	int stat;	if (!cp) return -EINVAL;	if (!(cp->driver)) return -EINVAL;	if (!(cp->driver->command)) return -EINVAL;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)	if (!try_module_get(cp->driver->owner)) return -EAGAIN;#else	if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;#endif	stat = cp->driver->command(cp,cmd,arg);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)	module_put(cp->driver->owner);#else	module_put(cp->driver->driver.owner);#endif	return stat;}int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg){	int stat;	if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {		char buf[100];		unsigned int cnt;		cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,					       buf,sizeof(buf));		pvr2_trace(PVR2_TRACE_I2C_CMD,			   "i2c COMMAND (code=%u 0x%x) to %.*s",			   cmd,cmd,cnt,buf);	}	stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);	if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {		char buf[100];		unsigned int cnt;		cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,					       buf,sizeof(buf));		pvr2_trace(PVR2_TRACE_I2C_CMD,			   "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);	}	return stat;}int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg){	struct list_head *item,*nc;	struct pvr2_i2c_client *cp;	int stat = -EINVAL;	if (!hdw) return stat;	mutex_lock(&hdw->i2c_list_lock);	list_for_each_safe(item,nc,&hdw->i2c_clients) {		cp = list_entry(item,struct pvr2_i2c_client,list);		if (!cp->recv_enable) continue;		mutex_unlock(&hdw->i2c_list_lock);		stat = pvr2_i2c_client_cmd(cp,cmd,arg);		mutex_lock(&hdw->i2c_list_lock);	}	mutex_unlock(&hdw->i2c_list_lock);	return stat;}static int handler_check(struct pvr2_i2c_client *cp){	struct pvr2_i2c_handler *hp = cp->handler;	if (!hp) return 0;	if (!hp->func_table->check) return 0;	return hp->func_table->check(hp->func_data) != 0;}#define BUFSIZE 500void pvr2_i2c_core_sync(struct pvr2_hdw *hdw){	unsigned long msk;	unsigned int idx;	struct list_head *item,*nc;	struct pvr2_i2c_client *cp;	if (!hdw->i2c_linked) return;	if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {		return;

⌨️ 快捷键说明

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