From 781aa1d1ab7ba13314af0af6c5d70c0eb0e96bf4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:30:53 -0300 Subject: V4L/DVB (4922): Add usbvision driver This patch adds usbvision into V4L/DVB HG tree. Usbvision driver is a GPL driver, made by: Joerg Heckenbach and Dwaine Garden V4L2 migration made by: Thierry Merle Kconfig/Makefile scripts by: Mauro Carvalho Chehab Signed-off-by: Joerg Heckenbach Signed-off-by: Dwaine Garden Signed-off-by: Thierry Merle Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/usbvision/usbvision-i2c.c | 265 ++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 drivers/media/video/usbvision/usbvision-i2c.c (limited to 'drivers/media/video/usbvision/usbvision-i2c.c') diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c new file mode 100644 index 000000000000..e050e5559ac5 --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -0,0 +1,265 @@ +/* + * I2C_ALGO_USB.C + * i2c algorithm for USB-I2C Bridges + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include "usbvision-i2c.h" + +static int debug = 0; + +#if defined(module_param) // Showing parameters under SYSFS +module_param (debug, int, 0444); // debug mode of the device driver +#else +MODULE_PARM(debug, "i"); // debug mode of the device driver +#endif + +MODULE_AUTHOR("Joerg Heckenbach"); +MODULE_DESCRIPTION("I2C algorithm for USB-I2C-bridges"); +MODULE_LICENSE("GPL"); + + +static inline int try_write_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret = -1; + char buf[4]; + + data = i2c_get_adapdata(i2c_adap); + buf[0] = 0x00; + for (i = 0; i <= retries; i++) { + ret = (adap->outb(data, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5 /*adap->udelay */ ); + if (i == retries) /* no success */ + break; + udelay(adap->udelay); + } + if (debug) { + if (i) { + info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); + info("%s: Maybe there's no device at this address", __FUNCTION__); + } + } + return ret; +} + +static inline int try_read_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret = -1; + char buf[4]; + + data = i2c_get_adapdata(i2c_adap); + for (i = 0; i <= retries; i++) { + ret = (adap->inb(data, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5 /*adap->udelay */ ); + if (i == retries) /* no success */ + break; + udelay(adap->udelay); + } + if (debug) { + if (i) { + info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); + info("%s: Maybe there's no device at this address", __FUNCTION__); + } + } + return ret; +} + +static inline int usb_find_address(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, int retries, + unsigned char *add) +{ + unsigned short flags = msg->flags; + + unsigned char addr; + int ret; + if ((flags & I2C_M_TEN)) { + /* a ten bit address */ + addr = 0xf0 | ((msg->addr >> 7) & 0x03); + /* try extended address code... */ + ret = try_write_address(i2c_adap, addr, retries); + if (ret != 1) { + err("died at extended address code, while writing"); + return -EREMOTEIO; + } + add[0] = addr; + if (flags & I2C_M_RD) { + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_read_address(i2c_adap, addr, retries); + if (ret != 1) { + err("died at extended address code, while reading"); + return -EREMOTEIO; + } + } + + } else { /* normal 7bit address */ + addr = (msg->addr << 1); + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + add[0] = addr; + if (flags & I2C_M_RD) + ret = try_read_address(i2c_adap, addr, retries); + else + ret = try_write_address(i2c_adap, addr, retries); + + if (ret != 1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int +usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret; + unsigned char addr; + + data = i2c_get_adapdata(i2c_adap); + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); + if (ret != 0) { + if (debug) { + info("%s: got NAK from device, message #%d\n", __FUNCTION__, i); + } + return (ret < 0) ? ret : -EREMOTEIO; + } + + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer */ + ret = (adap->inb(data, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) { + return (ret < 0) ? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = (adap->outb(data, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) { + return (ret < 0) ? ret : -EREMOTEIO; + } + } + } + return num; +} + +static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 usb_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_usb_algo = { + .master_xfer = usb_xfer, + .smbus_xfer = NULL, + .slave_send = NULL, + .slave_recv = NULL, + .algo_control = algo_control, + .functionality = usb_func, +}; + + +/* + * registering functions to load algorithms at runtime + */ +int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) +{ + /* register new adapter to i2c module... */ + + adap->algo = &i2c_usb_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) + MOD_INC_USE_COUNT; + #endif +#endif + + i2c_add_adapter(adap); + + if (debug) { + info("i2c bus for %s registered", adap->name); + } + + return 0; +} + + +int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + if (debug) { + info("i2c bus for %s unregistered", adap->name); + } +#ifdef MODULE + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) + MOD_DEC_USE_COUNT; + #endif +#endif + + return 0; +} + +EXPORT_SYMBOL(usbvision_i2c_usb_add_bus); +EXPORT_SYMBOL(usbvision_i2c_usb_del_bus); -- cgit v1.2.3