From 8112248a1b66440f6c562c2669aba6950ef48199 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Jan 2011 14:20:49 -0200 Subject: [media] Move CI cxd2099 driver to staging This driver is abusing the kernel<=>userspace API, due to the lack of a proper solution for it. A discussion were done at: http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html But there's not a solution for it yet. So, move the driver to staging, while we don't have a final solution. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/cxd2099/Kconfig | 11 + drivers/staging/cxd2099/Makefile | 5 + drivers/staging/cxd2099/TODO | 12 + drivers/staging/cxd2099/cxd2099.c | 574 ++++++++++++++++++++++++++++++++++++++ drivers/staging/cxd2099/cxd2099.h | 41 +++ 7 files changed, 646 insertions(+) create mode 100644 drivers/staging/cxd2099/Kconfig create mode 100644 drivers/staging/cxd2099/Makefile create mode 100644 drivers/staging/cxd2099/TODO create mode 100644 drivers/staging/cxd2099/cxd2099.c create mode 100644 drivers/staging/cxd2099/cxd2099.h (limited to 'drivers/staging') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5c8fcfc42c3e..4a88e69bb9bb 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" +source "drivers/staging/cxd2099/Kconfig" + source "drivers/staging/dabusb/Kconfig" source "drivers/staging/se401/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d53886317826..80ee49ab59de 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_SE401) += se401/ diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/cxd2099/Kconfig new file mode 100644 index 000000000000..9d638c30735d --- /dev/null +++ b/drivers/staging/cxd2099/Kconfig @@ -0,0 +1,11 @@ +config DVB_CXD2099 + tristate "CXD2099AR Common Interface driver" + depends on DVB_CORE && PCI && I2C && DVB_NGENE + ---help--- + Support for the CI module found on cineS2 DVB-S2, supported by + the Micronas PCIe device driver (ngene). + + For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/cxd2099/Makefile new file mode 100644 index 000000000000..72b14558c119 --- /dev/null +++ b/drivers/staging/cxd2099/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/ +EXTRA_CFLAGS += -Idrivers/media/common/tuners/ diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/cxd2099/TODO new file mode 100644 index 000000000000..375bb6f8ee2c --- /dev/null +++ b/drivers/staging/cxd2099/TODO @@ -0,0 +1,12 @@ +For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. + +But this is wrong. There are some discussions about the proper way for +doing it, as seen at: + http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html + +While there's no proper fix for it, the driver should be kept in staging. + +Patches should be submitted to: linux-media@vger.kernel.org. diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/cxd2099/cxd2099.c new file mode 100644 index 000000000000..b49186c74eb3 --- /dev/null +++ b/drivers/staging/cxd2099/cxd2099.c @@ -0,0 +1,574 @@ +/* + * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cxd2099.h" + +#define MAX_BUFFER_SIZE 248 + +struct cxd { + struct dvb_ca_en50221 en; + + struct i2c_adapter *i2c; + u8 adr; + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + u32 bitrate; + int ready; + int dr; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; +}; + +static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 data) +{ + u8 m[2] = {reg, data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", + reg, adr); + return -1; + } + return 0; +} + +static int i2c_write(struct i2c_adapter *adapter, u8 adr, + u8 *data, u8 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C!\n"); + return -1; + } + return 0; +} + +static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *val) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read_reg\n"); + return -1; + } + return 0; +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *data, u8 n) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = n } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read\n"); + return -1; + } + return 0; +} + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + status = i2c_read(ci->i2c, ci->adr, 1, data, n); + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) { + u8 buf[256] = {3}; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, u8 *val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, val, 1); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + u8 buf[2] = { 3, val }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_write(ci->i2c, ci->adr, buf, 2); + + return status; +} + + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) + status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]); + ci->regs[reg] = (ci->regs[reg]&(~mask))|val; + if (!status) { + ci->lastaddress = reg; + status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]); + } + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +#ifdef BUFFER_MODE +static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) +{ + int status; + u8 buf[256] = {1}; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} +#endif + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: + printk(KERN_INFO "enable cam buffer mode\n"); + /* write_reg(ci, 0x0d, 0x00); */ + /* write_reg(ci, 0x0e, 0x01); */ + write_regm(ci, 0x08, 0x40, 0x40); + /* read_reg(ci, 0x12, &dummy); */ + write_regm(ci, 0x08, 0x80, 0x80); + break; + default: + break; + } + ci->cammode = mode; +} + + + +#define CHK_ERROR(s) if ((status = s)) break + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + CHK_ERROR(write_reg(ci, 0x00, 0x00)); + CHK_ERROR(write_reg(ci, 0x01, 0x00)); + CHK_ERROR(write_reg(ci, 0x02, 0x10)); + CHK_ERROR(write_reg(ci, 0x03, 0x00)); + CHK_ERROR(write_reg(ci, 0x05, 0xFF)); + CHK_ERROR(write_reg(ci, 0x06, 0x1F)); + CHK_ERROR(write_reg(ci, 0x07, 0x1F)); + CHK_ERROR(write_reg(ci, 0x08, 0x28)); + CHK_ERROR(write_reg(ci, 0x14, 0x20)); + + CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ + CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ + + /* Sync detector */ + CHK_ERROR(write_reg(ci, 0x0B, 0x33)); + CHK_ERROR(write_reg(ci, 0x0C, 0x33)); + + CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b)); + CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f)); + + CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */ + CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */ + CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */ + + + CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */ + + CHK_ERROR(write_regm(ci, 0x03, 0x02, 02)); /* Enable IREQA Interrupt */ + CHK_ERROR(write_reg(ci, 0x01, 0x04)); /* Enable CD Interrupt */ + CHK_ERROR(write_reg(ci, 0x00, 0x31)); /* Enable TS1,Hot Swap,Slot A */ + CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08)); /* Put TS in bypass */ + ci->cammode = -1; +#ifdef BUFFER_MODE + cam_mode(ci, 0); +#endif + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; + u8 val; + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + return val; +} + + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; + ci->mode = -1; + { + int i; + for (i = 0; i < 100; i++) { + msleep(10); + if (ci->ready) + break; + } + } + mutex_unlock(&ci->lock); + /* msleep(500); */ + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + printk(KERN_INFO "slot_shutdown\n"); + mutex_lock(&ci->lock); + /* write_regm(ci, 0x09, 0x08, 0x08); */ + write_regm(ci, 0x20, 0x80, 0x80); + write_regm(ci, 0x06, 0x07, 0x07); + ci->mode = -1; + mutex_unlock(&ci->lock); + return 0; /* shutdown(ci); */ +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); +#ifdef BUFFER_MODE + cam_mode(ci, 1); +#endif + mutex_unlock(&ci->lock); + return 0; +} + + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat&0x40) { + ci->dr = 1; + printk(KERN_INFO "DR\n"); + } + if (istat&0x20) + printk(KERN_INFO "WC\n"); + + if (istat&2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2&slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + printk(KERN_INFO "NO CAM\n"); + ci->ready = 0; + } + } + if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + printk(KERN_INFO "READY\n"); + } + } + return 0; +} + + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +#ifdef BUFFER_MODE +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + printk(KERN_INFO "read_data\n"); + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = (msb<<8)|lsb; + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + printk(KERN_INFO "write_data %d\n", ecount); + write_reg(ci, 0x0d, ecount>>8); + write_reg(ci, 0x0e, ecount&0xff); + write_block(ci, 0x11, ebuf, ecount); + mutex_unlock(&ci->lock); + return ecount; +} +#endif + +static struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, +#ifdef BUFFER_MODE + .read_data = read_data, + .write_data = write_data, +#endif + +}; + +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, + struct i2c_adapter *i2c) +{ + struct cxd *ci = 0; + u32 bitrate = 62000000; + u8 val; + + if (i2c_read_reg(i2c, adr, 0, &val) < 0) { + printk(KERN_ERR "No CXD2099 detected at %02x\n", adr); + return 0; + } + + ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); + if (!ci) + return 0; + memset(ci, 0, sizeof(*ci)); + + mutex_init(&ci->lock); + ci->i2c = i2c; + ci->adr = adr; + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + ci->bitrate = bitrate; + + memcpy(&ci->en, &en_templ, sizeof(en_templ)); + ci->en.data = ci; + init(ci); + printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr); + return &ci->en; +} +EXPORT_SYMBOL(cxd2099_attach); + +MODULE_DESCRIPTION("cxd2099"); +MODULE_AUTHOR("Ralph Metzler "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h new file mode 100644 index 000000000000..a313dc26b962 --- /dev/null +++ b/drivers/staging/cxd2099/cxd2099.h @@ -0,0 +1,41 @@ +/* + * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _CXD2099_H_ +#define _CXD2099_H_ + +#include + +#if defined(CONFIG_DVB_CXD2099) || \ + (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); +#else +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From dbf05b279b8ed398a8e92b0c1187882f6df2bf7f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Jan 2011 22:18:02 -0200 Subject: [media] ngene: Fix compilation when cxd2099 is not enabled drivers/media/dvb/ngene/ngene-i2c.o: In function `cxd2099_attach': ngene-i2c.c:(.text+0x195): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here drivers/media/dvb/ngene/ngene-cards.o: In function `cxd2099_attach': ngene-cards.c:(.text+0x116): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here drivers/media/dvb/ngene/ngene-dvb.o: In function `cxd2099_attach': ngene-dvb.c:(.text+0x7ad): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/cxd2099/cxd2099.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h index a313dc26b962..bed54ff3e30b 100644 --- a/drivers/staging/cxd2099/cxd2099.h +++ b/drivers/staging/cxd2099/cxd2099.h @@ -31,7 +31,7 @@ (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); #else -struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) +static inline struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; -- cgit v1.2.3 From 8e030cabf1bb8601ad3efe09b9dc65a441a294ec Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 20 Jan 2011 03:05:08 -0300 Subject: [media] tm6000: add/rework reg.defines Rework registers defines. Add TM6000 specific registers defines. Add marks and comments for TM6010 specific registers. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-regs.h | 63 ++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/staging/tm6000/tm6000-regs.h index 1f0ced8fa20f..5375a8347374 100644 --- a/drivers/staging/tm6000/tm6000-regs.h +++ b/drivers/staging/tm6000/tm6000-regs.h @@ -97,6 +97,34 @@ enum { TM6000_URB_MSG_ERR, }; +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + /* Define TM6000/TM6010 Video decoder registers */ #define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 #define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 @@ -241,6 +269,7 @@ enum { #define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 #define TM6010_REQ07_RCA_VEND0 0x07, 0xca #define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ #define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc #define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 #define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 @@ -250,32 +279,59 @@ enum { #define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 #define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 #define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ #define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ #define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ #define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe #define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff @@ -477,7 +533,8 @@ enum { #define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 #define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc -/* Define TM6000/TM6010 Audio decoder registers */ +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ #define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 #define TM6010_REQ08_R01_A_INIT 0x08, 0x01 #define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 @@ -518,7 +575,7 @@ enum { #define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 #define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 -/* Define TM6000/TM6010 Video ADC registers */ +/* Define TM6010 Video ADC registers */ #define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 #define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 #define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 @@ -534,7 +591,7 @@ enum { #define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec #define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed -/* Define TM6000/TM6010 Audio ADC registers */ +/* Define TM6010 Audio ADC registers */ #define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 #define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 #define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 -- cgit v1.2.3 From 437f5fa34225db0d981536692678afc590e5c87f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Feb 2011 21:03:59 -0300 Subject: [media] tuner: Remove remaining usages of T_DIGITAL_TV A few places used T_DIGITAL_TV internally. Remove the usage of this obsolete mode mask. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tuner-xc2028.c | 8 ++++---- drivers/staging/tm6000/tm6000-cards.c | 2 -- include/media/tuner.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index d95f3b256e30..efcbc3ed0aea 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -933,7 +933,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, * that xc2028 will be in a safe state. * Maybe this might also be needed for DTV. */ - if (new_mode == T_ANALOG_TV) { + if (new_mode == V4L2_TUNER_ANALOG_TV) { rc = send_seq(priv, {0x00, 0x00}); /* Analog modes require offset = 0 */ @@ -1054,7 +1054,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe, if (priv->ctrl.input1) type |= INPUT1; return generic_set_freq(fe, (625l * p->frequency) / 10, - T_RADIO, type, 0, 0); + V4L2_TUNER_RADIO, type, 0, 0); } /* if std is not defined, choose one */ @@ -1069,7 +1069,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe, p->std |= parse_audio_std_option(); return generic_set_freq(fe, 62500l * p->frequency, - T_ANALOG_TV, type, p->std, 0); + V4L2_TUNER_ANALOG_TV, type, p->std, 0); } static int xc2028_set_params(struct dvb_frontend *fe, @@ -1174,7 +1174,7 @@ static int xc2028_set_params(struct dvb_frontend *fe, } return generic_set_freq(fe, p->frequency, - T_DIGITAL_TV, type, 0, demod); + V4L2_TUNER_DIGITAL_TV, type, 0, demod); } static int xc2028_sleep(struct dvb_frontend *fe) diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 455038bdfc9f..22a222241cc4 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -588,8 +588,6 @@ static void tm6000_config_tuner(struct tm6000_core *dev) tun_setup.mode_mask = 0; if (dev->caps.has_tuner) tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); - if (dev->caps.has_dvb) - tun_setup.mode_mask |= T_DIGITAL_TV; switch (dev->tuner_type) { case TUNER_XC2028: diff --git a/include/media/tuner.h b/include/media/tuner.h index 32dfd5f33cd7..963e33471835 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -161,7 +161,7 @@ enum tuner_mode { T_RADIO = 1 << V4L2_TUNER_RADIO, T_ANALOG_TV = 1 << V4L2_TUNER_ANALOG_TV, - T_DIGITAL_TV = 1 << V4L2_TUNER_DIGITAL_TV, + /* Don't need to map V4L2_TUNER_DIGITAL_TV, as tuner-core won't use it */ }; /* Older boards only had a single tuner device. Nowadays multiple tuner -- cgit v1.2.3 From 9839029b052f094b9c14cb60e7c729950dd28712 Mon Sep 17 00:00:00 2001 From: Stefan Ringel Date: Tue, 25 Jan 2011 13:40:55 -0300 Subject: [media] tm6000: relabeling any registers relabeling any registers Signed-off-by: Stefan Ringel Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-core.c | 64 ++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 25 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c index 96aed4ace467..5162cd4a8288 100644 --- a/drivers/staging/tm6000/tm6000-core.c +++ b/drivers/staging/tm6000/tm6000-core.c @@ -268,11 +268,11 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); /* AP Software reset */ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); @@ -284,8 +284,8 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); /* E3: Select input 0 - TV tuner */ - tm6000_set_reg(dev, TM6010_REQ07_RE3_OUT_SEL1, 0x00); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60); + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x60); /* This controls input */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0); @@ -344,21 +344,21 @@ int tm6000_init_digital_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); - tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x37); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); - tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); - tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); msleep(50); tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); @@ -388,18 +388,19 @@ struct reg_init { /* The meaning of those initializations are unknown */ struct reg_init tm6000_init_tab[] = { /* REG VALUE */ - { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f }, + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, - { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23 }, - { TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0x08 }, - { TM6010_REQ07_RE2_OUT_SEL2, 0x00 }, - { TM6010_REQ07_RE3_OUT_SEL1, 0x10 }, - { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0x00 }, - { TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0x00 }, - { REQ_07_SET_GET_AVREG, 0xeb, 0x64 }, /* 48000 bits/sample, external input */ - { REQ_07_SET_GET_AVREG, 0xee, 0xc2 }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, @@ -592,6 +593,7 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) { int val; + /* enable I2S, if we use sif or external I2S device */ if (dev->dev_type == TM6010) { val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0); if (val < 0) @@ -602,9 +604,17 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) return val; } - val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); - if (val < 0) - return val; + /* different reg's to set audio bitrate */ + if (dev->dev_type == TM6010) { + val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x0); + if (val < 0) + return val; + } else { + val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0); + if (val < 0) + return val; + } val &= 0x0f; /* Preserve the audio input control bits */ switch (bitrate) { @@ -617,7 +627,11 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) dev->audio_bitrate = bitrate; break; } - val = tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, val); + if (dev->dev_type == TM6010) + val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + val); + else + val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val); return val; } -- cgit v1.2.3 From 37d803ed0b86a3252457da971e0e9a8a937c2ba4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Feb 2011 13:39:36 -0300 Subject: [media] staging-usbvideo: remove With the new gspca v4l2 vicam driver, there is no more reason to keep the old v4l1 usbvideo vicam driver around, and since that is the last usbvideo framework using driver, the old usbvideo framework itself can go too. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/usbvideo/Kconfig | 15 - drivers/staging/usbvideo/Makefile | 2 - drivers/staging/usbvideo/TODO | 5 - drivers/staging/usbvideo/usbvideo.c | 2230 ----------------------------------- drivers/staging/usbvideo/usbvideo.h | 395 ------- drivers/staging/usbvideo/vicam.c | 952 --------------- drivers/staging/usbvideo/videodev.h | 318 ----- 9 files changed, 3920 deletions(-) delete mode 100644 drivers/staging/usbvideo/Kconfig delete mode 100644 drivers/staging/usbvideo/Makefile delete mode 100644 drivers/staging/usbvideo/TODO delete mode 100644 drivers/staging/usbvideo/usbvideo.c delete mode 100644 drivers/staging/usbvideo/usbvideo.h delete mode 100644 drivers/staging/usbvideo/vicam.c delete mode 100644 drivers/staging/usbvideo/videodev.h (limited to 'drivers/staging') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4a88e69bb9bb..a3f340bb6b59 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -57,8 +57,6 @@ source "drivers/staging/dabusb/Kconfig" source "drivers/staging/se401/Kconfig" -source "drivers/staging/usbvideo/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 80ee49ab59de..532331ceb12d 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ -obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ diff --git a/drivers/staging/usbvideo/Kconfig b/drivers/staging/usbvideo/Kconfig deleted file mode 100644 index 566d659e6ff3..000000000000 --- a/drivers/staging/usbvideo/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_USBVIDEO - tristate - -config USB_VICAM - tristate "USB 3com HomeConnect (aka vicam) support (DEPRECATED)" - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB - select VIDEO_USBVIDEO - ---help--- - Say Y here if you have 3com homeconnect camera (vicam). - - This driver uses the deprecated V4L1 API and will be removed in - 2.6.39, unless someone converts it to the V4L2 API. - - To compile this driver as a module, choose M here: the - module will be called vicam. diff --git a/drivers/staging/usbvideo/Makefile b/drivers/staging/usbvideo/Makefile deleted file mode 100644 index 3c99a9a2d8d3..000000000000 --- a/drivers/staging/usbvideo/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o -obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/drivers/staging/usbvideo/TODO b/drivers/staging/usbvideo/TODO deleted file mode 100644 index 3b2c03836286..000000000000 --- a/drivers/staging/usbvideo/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is an obsolete driver for some old webcams that still use V4L1 API. -As V4L1 support is being removed from kernel, if nobody take care on it, -the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/usbvideo/usbvideo.c b/drivers/staging/usbvideo/usbvideo.c deleted file mode 100644 index f1fcf9744961..000000000000 --- a/drivers/staging/usbvideo/usbvideo.c +++ /dev/null @@ -1,2230 +0,0 @@ -/* - * 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, 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 "usbvideo.h" - -#if defined(MAP_NR) -#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ -#endif - -static int video_nr = -1; -module_param(video_nr, int, 0); - -/* - * Local prototypes. - */ -static void usbvideo_Disconnect(struct usb_interface *intf); -static void usbvideo_CameraRelease(struct uvd *uvd); - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); -static int usbvideo_v4l_open(struct file *file); -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); -static int usbvideo_v4l_close(struct file *file); - -static int usbvideo_StartDataPump(struct uvd *uvd); -static void usbvideo_StopDataPump(struct uvd *uvd); -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); -static int usbvideo_NewFrame(struct uvd *uvd, int framenum); -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame); - -/*******************************/ -/* Memory management functions */ -/*******************************/ -static void *usbvideo_rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void usbvideo_rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -static void RingQueue_Initialize(struct RingQueue *rq) -{ - assert(rq != NULL); - init_waitqueue_head(&rq->wqh); -} - -static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) -{ - /* Make sure the requested size is a power of 2 and - round up if necessary. This allows index wrapping - using masks rather than modulo */ - - int i = 1; - assert(rq != NULL); - assert(rqLen > 0); - - while(rqLen >> i) - i++; - if(rqLen != 1 << (i-1)) - rqLen = 1 << i; - - rq->length = rqLen; - rq->ri = rq->wi = 0; - rq->queue = usbvideo_rvmalloc(rq->length); - assert(rq->queue != NULL); -} - -static int RingQueue_IsAllocated(const struct RingQueue *rq) -{ - if (rq == NULL) - return 0; - return (rq->queue != NULL) && (rq->length > 0); -} - -static void RingQueue_Free(struct RingQueue *rq) -{ - assert(rq != NULL); - if (RingQueue_IsAllocated(rq)) { - usbvideo_rvfree(rq->queue, rq->length); - rq->queue = NULL; - rq->length = 0; - } -} - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) -{ - int rql, toread; - - assert(rq != NULL); - assert(dst != NULL); - - rql = RingQueue_GetLength(rq); - if(!rql) - return 0; - - /* Clip requested length to available data */ - if(len > rql) - len = rql; - - toread = len; - if(rq->ri > rq->wi) { - /* Read data from tail */ - int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; - memcpy(dst, rq->queue + rq->ri, read); - toread -= read; - dst += read; - rq->ri = (rq->ri + read) & (rq->length-1); - } - if(toread) { - /* Read data from head */ - memcpy(dst, rq->queue + rq->ri, toread); - rq->ri = (rq->ri + toread) & (rq->length-1); - } - return len; -} - -EXPORT_SYMBOL(RingQueue_Dequeue); - -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) -{ - int enqueued = 0; - - assert(rq != NULL); - assert(cdata != NULL); - assert(rq->length > 0); - while (n > 0) { - int m, q_avail; - - /* Calculate the largest chunk that fits the tail of the ring */ - q_avail = rq->length - rq->wi; - if (q_avail <= 0) { - rq->wi = 0; - q_avail = rq->length; - } - m = n; - assert(q_avail > 0); - if (m > q_avail) - m = q_avail; - - memcpy(rq->queue + rq->wi, cdata, m); - RING_QUEUE_ADVANCE_INDEX(rq, wi, m); - cdata += m; - enqueued += m; - n -= m; - } - return enqueued; -} - -EXPORT_SYMBOL(RingQueue_Enqueue); - -static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) -{ - assert(rq != NULL); - interruptible_sleep_on(&rq->wqh); -} - -void RingQueue_WakeUpInterruptible(struct RingQueue *rq) -{ - assert(rq != NULL); - if (waitqueue_active(&rq->wqh)) - wake_up_interruptible(&rq->wqh); -} - -EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); - -void RingQueue_Flush(struct RingQueue *rq) -{ - assert(rq != NULL); - rq->ri = 0; - rq->wi = 0; -} - -EXPORT_SYMBOL(RingQueue_Flush); - - -/* - * usbvideo_VideosizeToString() - * - * This procedure converts given videosize value to readable string. - * - * History: - * 07-Aug-2000 Created. - * 19-Oct-2000 Reworked for usbvideo module. - */ -static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) -{ - char tmp[40]; - int n; - - n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); - assert(n < sizeof(tmp)); - if ((buf == NULL) || (bufLen < n)) - err("usbvideo_VideosizeToString: buffer is too small."); - else - memmove(buf, tmp, n); -} - -/* - * usbvideo_OverlayChar() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, int ch) -{ - static const unsigned short digits[16] = { - 0xF6DE, /* 0 */ - 0x2492, /* 1 */ - 0xE7CE, /* 2 */ - 0xE79E, /* 3 */ - 0xB792, /* 4 */ - 0xF39E, /* 5 */ - 0xF3DE, /* 6 */ - 0xF492, /* 7 */ - 0xF7DE, /* 8 */ - 0xF79E, /* 9 */ - 0x77DA, /* a */ - 0xD75C, /* b */ - 0xF24E, /* c */ - 0xD6DC, /* d */ - 0xF34E, /* e */ - 0xF348 /* f */ - }; - unsigned short digit; - int ix, iy; - int value; - - if ((uvd == NULL) || (frame == NULL)) - return; - - value = hex_to_bin(ch); - if (value < 0) - return; - digit = digits[value]; - - for (iy=0; iy < 5; iy++) { - for (ix=0; ix < 3; ix++) { - if (digit & 0x8000) { - if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { -/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); - } - } - digit = digit << 1; - } - } -} - -/* - * usbvideo_OverlayString() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, const char *str) -{ - while (*str) { - usbvideo_OverlayChar(uvd, frame, x, y, *str); - str++; - x += 4; /* 3 pixels character + 1 space */ - } -} - -/* - * usbvideo_OverlayStats() - * - * Overlays important debugging information. - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) -{ - const int y_diff = 8; - char tmp[16]; - int x = 10, y=10; - long i, j, barLength; - const int qi_x1 = 60, qi_y1 = 10; - const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; - - /* Call the user callback, see if we may proceed after that */ - if (VALID_CALLBACK(uvd, overlayHook)) { - if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) - return; - } - - /* - * We draw a (mostly) hollow rectangle with qi_xxx coordinates. - * Left edge symbolizes the queue index 0; right edge symbolizes - * the full capacity of the queue. - */ - barLength = qi_x2 - qi_x1 - 2; - if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { -/* TODO */ long u_lo, u_hi, q_used; - long m_ri, m_wi, m_lo, m_hi; - - /* - * Determine fill zones (used areas of the queue): - * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length - * - * if u_lo < 0 then there is no first filler. - */ - - q_used = RingQueue_GetLength(&uvd->dp); - if ((uvd->dp.ri + q_used) >= uvd->dp.length) { - u_hi = uvd->dp.length; - u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); - } else { - u_hi = (q_used + uvd->dp.ri); - u_lo = -1; - } - - /* Convert byte indices into screen units */ - m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); - m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); - m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; - m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); - - for (j=qi_y1; j < (qi_y1 + qi_h); j++) { - for (i=qi_x1; i < qi_x2; i++) { - /* Draw border lines */ - if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || - (i == qi_x1) || (i == (qi_x2 - 1))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); - continue; - } - /* For all other points the Y coordinate does not matter */ - if ((i >= m_ri) && (i <= (m_ri + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); - } else if ((i >= m_wi) && (i <= (m_wi + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); - } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) - RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); - } - } - } - - sprintf(tmp, "%8lx", uvd->stats.frame_num); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_length); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.data_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.header_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_err_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.colour); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.hue); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; -} - -/* - * usbvideo_ReportStatistics() - * - * This procedure prints packet and transfer statistics. - * - * History: - * 14-Jan-2000 Corrected default multiplier. - */ -static void usbvideo_ReportStatistics(const struct uvd *uvd) -{ - if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { - unsigned long allPackets, badPackets, goodPackets, percent; - allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; - badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; - goodPackets = allPackets - badPackets; - /* Calculate percentage wisely, remember integer limits */ - assert(allPackets != 0); - if (goodPackets < (((unsigned long)-1)/100)) - percent = (100 * goodPackets) / allPackets; - else - percent = goodPackets / (allPackets / 100); - dev_info(&uvd->dev->dev, - "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n", - allPackets, badPackets, percent); - if (uvd->iso_packet_len > 0) { - unsigned long allBytes, xferBytes; - char multiplier = ' '; - allBytes = allPackets * uvd->iso_packet_len; - xferBytes = uvd->stats.data_count; - assert(allBytes != 0); - if (xferBytes < (((unsigned long)-1)/100)) - percent = (100 * xferBytes) / allBytes; - else - percent = xferBytes / (allBytes / 100); - /* Scale xferBytes for easy reading */ - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'K'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'M'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'G'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'T'; - } - } - } - } - dev_info(&uvd->dev->dev, - "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n", - xferBytes, multiplier, percent); - } - } -} - -/* - * usbvideo_TestPattern() - * - * Procedure forms a test pattern (yellow grid on blue background). - * - * Parameters: - * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. - * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid - * - * History: - * 01-Feb-2000 Created. - */ -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) -{ - struct usbvideo_frame *frame; - int num_cell = 0; - int scan_length = 0; - static int num_pass; - - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { - err("%s: uvd->curframe=%d.", __func__, uvd->curframe); - return; - } - - /* Grab the current frame */ - frame = &uvd->frame[uvd->curframe]; - - /* Optionally start at the beginning */ - if (fullframe) { - frame->curline = 0; - frame->seqRead_Length = 0; - } -#if 0 - { /* For debugging purposes only */ - char tmp[20]; - usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); - dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp); - } -#endif - /* Form every scan line */ - for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { - int i; - unsigned char *f = frame->data + - (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); - for (i=0; i < VIDEOSIZE_X(frame->request); i++) { - unsigned char cb=0x80; - unsigned char cg = 0; - unsigned char cr = 0; - - if (pmode == 1) { - if (frame->curline % 32 == 0) - cb = 0, cg = cr = 0xFF; - else if (i % 32 == 0) { - if (frame->curline % 32 == 1) - num_cell++; - cb = 0, cg = cr = 0xFF; - } else { - cb = ((num_cell*7) + num_pass) & 0xFF; - cg = ((num_cell*5) + num_pass*2) & 0xFF; - cr = ((num_cell*3) + num_pass*3) & 0xFF; - } - } else { - /* Just the blue screen */ - } - - *f++ = cb; - *f++ = cg; - *f++ = cr; - scan_length += 3; - } - } - - frame->frameState = FrameState_Done; - frame->seqRead_Length += scan_length; - ++num_pass; - - /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_TestPattern); - - -#ifdef DEBUG -/* - * usbvideo_HexDump() - * - * A debugging tool. Prints hex dumps. - * - * History: - * 29-Jul-2000 Added printing of offsets. - */ -void usbvideo_HexDump(const unsigned char *data, int len) -{ - const int bytes_per_line = 32; - char tmp[128]; /* 32*3 + 5 */ - int i, k; - - for (i=k=0; len > 0; i++, len--) { - if (i > 0 && ((i % bytes_per_line) == 0)) { - printk("%s\n", tmp); - k=0; - } - if ((i % bytes_per_line) == 0) - k += sprintf(&tmp[k], "%04x: ", i); - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk("%s\n", tmp); -} - -EXPORT_SYMBOL(usbvideo_HexDump); - -#endif - -/* ******************************************************************** */ - -/* XXX: this piece of crap really wants some error handling.. */ -static int usbvideo_ClientIncModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return -EINVAL; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return -EINVAL; - } - if (!try_module_get(uvd->handle->md_module)) { - err("%s: try_module_get() == 0", __func__); - return -ENODEV; - } - return 0; -} - -static void usbvideo_ClientDecModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return; - } - if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __func__); - return; - } - module_put(uvd->handle->md_module); -} - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTbl, - struct module *md, - const struct usb_device_id *id_table) -{ - struct usbvideo *cams; - int i, base_size, result; - - /* Check parameters for sanity */ - if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { - err("%s: Illegal call", __func__); - return -EINVAL; - } - - /* Check registration callback - must be set! */ - if (cbTbl->probe == NULL) { - err("%s: probe() is required!", __func__); - return -EINVAL; - } - - base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); - cams = kzalloc(base_size, GFP_KERNEL); - if (cams == NULL) { - err("Failed to allocate %d. bytes for usbvideo struct", base_size); - return -ENOMEM; - } - dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", - __func__, cams, base_size, num_cams); - - /* Copy callbacks, apply defaults for those that are not set */ - memmove(&cams->cb, cbTbl, sizeof(cams->cb)); - if (cams->cb.getFrame == NULL) - cams->cb.getFrame = usbvideo_GetFrame; - if (cams->cb.disconnect == NULL) - cams->cb.disconnect = usbvideo_Disconnect; - if (cams->cb.startDataPump == NULL) - cams->cb.startDataPump = usbvideo_StartDataPump; - if (cams->cb.stopDataPump == NULL) - cams->cb.stopDataPump = usbvideo_StopDataPump; - - cams->num_cameras = num_cams; - cams->cam = (struct uvd *) &cams[1]; - cams->md_module = md; - mutex_init(&cams->lock); /* to 1 == available */ - - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - - up->handle = cams; - - /* Allocate user_data separately because of kmalloc's limits */ - if (num_extra > 0) { - up->user_size = num_cams * num_extra; - up->user_data = kmalloc(up->user_size, GFP_KERNEL); - if (up->user_data == NULL) { - err("%s: Failed to allocate user_data (%d. bytes)", - __func__, up->user_size); - while (i) { - up = &cams->cam[--i]; - kfree(up->user_data); - } - kfree(cams); - return -ENOMEM; - } - dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", - __func__, i, up->user_data, up->user_size); - } - } - - /* - * Register ourselves with USB stack. - */ - strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); - cams->usbdrv.name = cams->drvName; - cams->usbdrv.probe = cams->cb.probe; - cams->usbdrv.disconnect = cams->cb.disconnect; - cams->usbdrv.id_table = id_table; - - /* - * Update global handle to usbvideo. This is very important - * because probe() can be called before usb_register() returns. - * If the handle is not yet updated then the probe() will fail. - */ - *pCams = cams; - result = usb_register(&cams->usbdrv); - if (result) { - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - kfree(up->user_data); - } - kfree(cams); - } - - return result; -} - -EXPORT_SYMBOL(usbvideo_register); - -/* - * usbvideo_Deregister() - * - * Procedure frees all usbvideo and user data structures. Be warned that - * if you had some dynamically allocated components in ->user field then - * you should free them before calling here. - */ -void usbvideo_Deregister(struct usbvideo **pCams) -{ - struct usbvideo *cams; - int i; - - if (pCams == NULL) { - err("%s: pCams == NULL", __func__); - return; - } - cams = *pCams; - if (cams == NULL) { - err("%s: cams == NULL", __func__); - return; - } - - dbg("%s: Deregistering %s driver.", __func__, cams->drvName); - usb_deregister(&cams->usbdrv); - - dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras); - for (i=0; i < cams->num_cameras; i++) { - struct uvd *up = &cams->cam[i]; - int warning = 0; - - if (up->user_data != NULL) { - if (up->user_size <= 0) - ++warning; - } else { - if (up->user_size > 0) - ++warning; - } - if (warning) { - err("%s: Warning: user_data=$%p user_size=%d.", - __func__, up->user_data, up->user_size); - } else { - dbg("%s: Freeing %d. $%p->user_data=$%p", - __func__, i, up, up->user_data); - kfree(up->user_data); - } - } - /* Whole array was allocated in one chunk */ - dbg("%s: Freed %d uvd structures", - __func__, cams->num_cameras); - kfree(cams); - *pCams = NULL; -} - -EXPORT_SYMBOL(usbvideo_Deregister); - -/* - * usbvideo_Disconnect() - * - * This procedure stops all driver activity. Deallocation of - * the interface-private structure (pointed by 'ptr') is done now - * (if we don't have any open files) or later, when those files - * are closed. After that driver should be removable. - * - * This code handles surprise removal. The uvd->user is a counter which - * increments on open() and decrements on close(). If we see here that - * this counter is not 0 then we have a client who still has us opened. - * We set uvd->remove_pending flag as early as possible, and after that - * all access to the camera will gracefully fail. These failures should - * prompt client to (eventually) close the video device, and then - in - * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. - * - * History: - * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. - * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - * 19-Oct-2000 Moved to usbvideo module. - */ -static void usbvideo_Disconnect(struct usb_interface *intf) -{ - struct uvd *uvd = usb_get_intfdata (intf); - int i; - - if (uvd == NULL) { - err("%s($%p): Illegal call.", __func__, intf); - return; - } - - usb_set_intfdata (intf, NULL); - - usbvideo_ClientIncModCount(uvd); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s(%p.)\n", __func__, intf); - - mutex_lock(&uvd->lock); - uvd->remove_pending = 1; /* Now all ISO data will be ignored */ - - /* At this time we ask to cancel outstanding URBs */ - GET_CALLBACK(uvd, stopDataPump)(uvd); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) - usb_free_urb(uvd->sbuf[i].urb); - - usb_put_dev(uvd->dev); - uvd->dev = NULL; /* USB device is no more */ - - video_unregister_device(&uvd->vdev); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s: Video unregistered.\n", __func__); - - if (uvd->user) - dev_info(&intf->dev, "%s: In use, disconnect pending.\n", - __func__); - else - usbvideo_CameraRelease(uvd); - mutex_unlock(&uvd->lock); - dev_info(&intf->dev, "USB camera disconnected.\n"); - - usbvideo_ClientDecModCount(uvd); -} - -/* - * usbvideo_CameraRelease() - * - * This code does final release of uvd. This happens - * after the device is disconnected -and- all clients - * closed their files. - * - * History: - * 27-Jan-2000 Created. - */ -static void usbvideo_CameraRelease(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: Illegal call", __func__); - return; - } - - RingQueue_Free(&uvd->dp); - if (VALID_CALLBACK(uvd, userFree)) - GET_CALLBACK(uvd, userFree)(uvd); - uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ -} - -/* - * usbvideo_find_struct() - * - * This code searches the array of preallocated (static) structures - * and returns index of the first one that isn't in use. Returns -1 - * if there are no free structures. - * - * History: - * 27-Jan-2000 Created. - */ -static int usbvideo_find_struct(struct usbvideo *cams) -{ - int u, rv = -1; - - if (cams == NULL) { - err("No usbvideo handle?"); - return -1; - } - mutex_lock(&cams->lock); - for (u = 0; u < cams->num_cameras; u++) { - struct uvd *uvd = &cams->cam[u]; - if (!uvd->uvd_used) /* This one is free */ - { - uvd->uvd_used = 1; /* In use now */ - mutex_init(&uvd->lock); /* to 1 == available */ - uvd->dev = NULL; - rv = u; - break; - } - } - mutex_unlock(&cams->lock); - return rv; -} - -static const struct v4l2_file_operations usbvideo_fops = { - .owner = THIS_MODULE, - .open = usbvideo_v4l_open, - .release =usbvideo_v4l_close, - .read = usbvideo_v4l_read, - .mmap = usbvideo_v4l_mmap, - .ioctl = usbvideo_v4l_ioctl, -}; -static const struct video_device usbvideo_template = { - .fops = &usbvideo_fops, -}; - -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) -{ - int i, devnum; - struct uvd *uvd = NULL; - - if (cams == NULL) { - err("No usbvideo handle?"); - return NULL; - } - - devnum = usbvideo_find_struct(cams); - if (devnum == -1) { - err("IBM USB camera driver: Too many devices!"); - return NULL; - } - uvd = &cams->cam[devnum]; - dbg("Device entry #%d. at $%p", devnum, uvd); - - /* Not relying upon caller we increase module counter ourselves */ - usbvideo_ClientIncModCount(uvd); - - mutex_lock(&uvd->lock); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (uvd->sbuf[i].urb == NULL) { - err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); - uvd->uvd_used = 0; - uvd = NULL; - goto allocate_done; - } - } - uvd->user=0; - uvd->remove_pending = 0; - uvd->last_error = 0; - RingQueue_Initialize(&uvd->dp); - - /* Initialize video device structure */ - uvd->vdev = usbvideo_template; - sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); - /* - * The client is free to overwrite those because we - * return control to the client's probe function right now. - */ -allocate_done: - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - return uvd; -} - -EXPORT_SYMBOL(usbvideo_AllocateDevice); - -int usbvideo_RegisterVideoDevice(struct uvd *uvd) -{ - char tmp1[20], tmp2[20]; /* Buffers for printing */ - - if (uvd == NULL) { - err("%s: Illegal call.", __func__); - return -EINVAL; - } - if (uvd->video_endp == 0) { - dev_info(&uvd->dev->dev, - "%s: No video endpoint specified; data pump disabled.\n", - __func__); - } - if (uvd->paletteBits == 0) { - err("%s: No palettes specified!", __func__); - return -EINVAL; - } - if (uvd->defaultPalette == 0) { - dev_info(&uvd->dev->dev, "%s: No default palette!\n", - __func__); - } - - uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * - VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; - usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); - usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); - - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n", - __func__, uvd->iface, uvd->video_endp, - uvd->paletteBits); - } - if (uvd->dev == NULL) { - err("%s: uvd->dev == NULL", __func__); - return -EINVAL; - } - uvd->vdev.parent = &uvd->dev->dev; - uvd->vdev.release = video_device_release_empty; - if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - err("%s: video_register_device failed", __func__); - return -EPIPE; - } - if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: video_register_device() successful\n", __func__); - } - - dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n", - (uvd->handle != NULL) ? uvd->handle->drvName : "???", - video_device_node_name(&uvd->vdev), tmp2, tmp1); - - usb_get_dev(uvd->dev); - return 0; -} - -EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); - -/* ******************************************************************** */ - -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct uvd *uvd = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EFAULT; - - if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - return -EINVAL; - - pos = (unsigned long) uvd->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -/* - * usbvideo_v4l_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'uvdser'). The procedure - * then allocates buffers needed for video processing. - * - * History: - * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the - * camera is also initialized here (once per connect), at - * expense of V4L client (it waits on open() call). - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - */ -static int usbvideo_v4l_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct uvd *uvd = (struct uvd *) dev; - const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; - int i, errCode = 0; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - if (usbvideo_ClientIncModCount(uvd) < 0) - return -ENODEV; - mutex_lock(&uvd->lock); - - if (uvd->user) { - err("%s: Someone tried to open an already opened device!", __func__); - errCode = -EBUSY; - } else { - /* Clear statistics */ - memset(&uvd->stats, 0, sizeof(uvd->stats)); - - /* Clean pointers so we know if we allocated something */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) - uvd->sbuf[i].data = NULL; - - /* Allocate memory for the frame buffers */ - uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; - uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); - RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); - if ((uvd->fbuf == NULL) || - (!RingQueue_IsAllocated(&uvd->dp))) { - err("%s: Failed to allocate fbuf or dp", __func__); - errCode = -ENOMEM; - } else { - /* Allocate all buffers */ - for (i=0; i < USBVIDEO_NUMFRAMES; i++) { - uvd->frame[i].frameState = FrameState_Unused; - uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); - /* - * Set default sizes in case IOCTL (VIDIOCMCAPTURE) - * is not used (using read() instead). - */ - uvd->frame[i].canvas = uvd->canvas; - uvd->frame[i].seqRead_Index = 0; - } - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); - if (uvd->sbuf[i].data == NULL) { - errCode = -ENOMEM; - break; - } - } - } - if (errCode != 0) { - /* Have to free all that memory */ - if (uvd->fbuf != NULL) { - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - } - RingQueue_Free(&uvd->dp); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - } - } - - /* If so far no errors then we shall start the camera */ - if (errCode == 0) { - /* Start data pump if we have valid endpoint */ - if (uvd->video_endp != 0) - errCode = GET_CALLBACK(uvd, startDataPump)(uvd); - if (errCode == 0) { - if (VALID_CALLBACK(uvd, setupOnOpen)) { - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback\n", - __func__); - errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); - if (errCode < 0) { - err("%s: setupOnOpen callback failed (%d.).", - __func__, errCode); - } else if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback successful\n", - __func__); - } - } - if (errCode == 0) { - uvd->settingsAdjusted = 0; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: Open succeeded.\n", - __func__); - uvd->user++; - file->private_data = uvd; - } - } - } - mutex_unlock(&uvd->lock); - if (errCode != 0) - usbvideo_ClientDecModCount(uvd); - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__, - errCode); - return errCode; -} - -/* - * usbvideo_v4l_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in usbvideo_v4l_open(). - * - * History: - * 22-Jan-2000 Moved scratch buffer deallocation here. - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. - */ -static int usbvideo_v4l_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct uvd *uvd = (struct uvd *) dev; - int i; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - mutex_lock(&uvd->lock); - GET_CALLBACK(uvd, stopDataPump)(uvd); - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - RingQueue_Free(&uvd->dp); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - -#if USBVIDEO_REPORT_STATS - usbvideo_ReportStatistics(uvd); -#endif - - uvd->user--; - if (uvd->remove_pending) { - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Final disconnect.\n", - __func__); - usbvideo_CameraRelease(uvd); - } - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__); - file->private_data = NULL; - return 0; -} - -/* - * usbvideo_v4l_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * - * History: - * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. - */ -static long usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct uvd *uvd = file->private_data; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - *b = uvd->vcap; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - *v = uvd->vchan; - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - *pic = uvd->vpic; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *pic = arg; - /* - * Use temporary 'video_picture' structure to preserve our - * own settings (such as color depth, palette) that we - * aren't allowing everyone (V4L client) to change. - */ - uvd->vpic.brightness = pic->brightness; - uvd->vpic.hue = pic->hue; - uvd->vpic.colour = pic->colour; - uvd->vpic.contrast = pic->contrast; - uvd->settingsAdjusted = 0; /* Will force new settings */ - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if(VALID_CALLBACK(uvd, setVideoMode)) { - return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); - } - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (vw->width != VIDEOSIZE_X(uvd->canvas)) - return -EINVAL; - if (vw->height != VIDEOSIZE_Y(uvd->canvas)) - return -EINVAL; - - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = VIDEOSIZE_X(uvd->videosize); - vw->height = VIDEOSIZE_Y(uvd->videosize); - vw->chromakey = 0; - if (VALID_CALLBACK(uvd, getFPS)) - vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); - else - vw->flags = 10; /* FIXME: do better! */ - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; - vm->frames = USBVIDEO_NUMFRAMES; - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) - vm->offsets[i] = i * uvd->max_frame_size; - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (uvd->debug >= 1) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n", - vm->frame, vm->width, vm->height, vm->format); - } - /* - * Check if the requested size is supported. If the requestor - * requests too big a frame then we may be tricked into accessing - * outside of own preallocated frame buffer (in uvd->frame). - * This will cause oops or a security hole. Theoretically, we - * could only clamp the size down to acceptable bounds, but then - * we'd need to figure out how to insert our smaller buffer into - * larger caller's buffer... this is not an easy question. So we - * here just flatly reject too large requests, assuming that the - * caller will resubmit with smaller size. Callers should know - * what size we support (returned by VIDIOCGCAP). However vidcat, - * for one, does not care and allows to ask for any size. - */ - if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || - (vm->height > VIDEOSIZE_Y(uvd->canvas))) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: Size=%dx%d " - "too large; allowed only up " - "to %ldx%ld\n", vm->width, - vm->height, - VIDEOSIZE_X(uvd->canvas), - VIDEOSIZE_Y(uvd->canvas)); - } - return -EINVAL; - } - /* Check if the palette is supported */ - if (((1L << vm->format) & uvd->paletteBits) == 0) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: format=%d. " - "not supported " - "(paletteBits=$%08lx)\n", - vm->format, uvd->paletteBits); - } - return -EINVAL; - } - if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { - err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); - return -EINVAL; - } - if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { - /* Not an error - can happen */ - } - uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); - uvd->frame[vm->frame].palette = vm->format; - - /* Mark it as ready */ - uvd->frame[vm->frame].frameState = FrameState_Ready; - - return usbvideo_NewFrame(uvd, vm->frame); - } - case VIDIOCSYNC: - { - int *frameNum = arg; - int ret; - - if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) - return -EINVAL; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "VIDIOCSYNC: syncing to frame %d.\n", - *frameNum); - if (uvd->flags & FLAGS_NO_DECODING) - ret = usbvideo_GetFrame(uvd, *frameNum); - else if (VALID_CALLBACK(uvd, getFrame)) { - ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); - if ((ret < 0) && (uvd->debug >= 1)) { - err("VIDIOCSYNC: getFrame() returned %d.", ret); - } - } else { - err("VIDIOCSYNC: getFrame is not set"); - ret = -EFAULT; - } - - /* - * The frame is in FrameState_Done_Hold state. Release it - * right now because its data is already mapped into - * the user space and it's up to the application to - * make use of it until it asks for another frame. - */ - uvd->frame[*frameNum].frameState = FrameState_Unused; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - - case VIDIOCCAPTURE: - return -EINVAL; - - case VIDIOCSFBUF: - - case VIDIOCGTUNER: - case VIDIOCSTUNER: - - case VIDIOCGFREQ: - case VIDIOCSFREQ: - - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); -} - -/* - * usbvideo_v4l_read() - * - * This is mostly boring stuff. We simply ask for a frame and when it - * arrives copy all the video data from it into user space. There is - * no obvious need to override this method. - * - * History: - * 20-Oct-2000 Created. - * 01-Nov-2000 Added mutex (uvd->lock). - */ -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct uvd *uvd = file->private_data; - int noblock = file->f_flags & O_NONBLOCK; - int frmx = -1, i; - struct usbvideo_frame *frame; - - if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) - return -EFAULT; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "%s: %Zd. bytes, noblock=%d.\n", - __func__, count, noblock); - - mutex_lock(&uvd->lock); - - /* See if a frame is completed, then use it. */ - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if ((uvd->frame[i].frameState == FrameState_Done) || - (uvd->frame[i].frameState == FrameState_Done_Hold) || - (uvd->frame[i].frameState == FrameState_Error)) { - frmx = i; - break; - } - } - - /* FIXME: If we don't start a frame here then who ever does? */ - if (noblock && (frmx == -1)) { - count = -EAGAIN; - goto read_done; - } - - /* - * If no FrameState_Done, look for a FrameState_Grabbing state. - * See if a frame is in process (grabbing), then use it. - * We will need to wait until it becomes cooked, of course. - */ - if (frmx == -1) { - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if (uvd->frame[i].frameState == FrameState_Grabbing) { - frmx = i; - break; - } - } - } - - /* - * If no frame is active, start one. We don't care which one - * it will be, so #0 is as good as any. - * In read access mode we don't have convenience of VIDIOCMCAPTURE - * to specify the requested palette (video format) on per-frame - * basis. This means that we have to return data in -some- format - * and just hope that the client knows what to do with it. - * The default format is configured in uvd->defaultPalette field - * as one of VIDEO_PALETTE_xxx values. We stuff it into the new - * frame and initiate the frame filling process. - */ - if (frmx == -1) { - if (uvd->defaultPalette == 0) { - err("%s: No default palette; don't know what to do!", __func__); - count = -EFAULT; - goto read_done; - } - frmx = 0; - /* - * We have no per-frame control over video size. - * Therefore we only can use whatever size was - * specified as default. - */ - uvd->frame[frmx].request = uvd->videosize; - uvd->frame[frmx].palette = uvd->defaultPalette; - uvd->frame[frmx].frameState = FrameState_Ready; - usbvideo_NewFrame(uvd, frmx); - /* Now frame 0 is supposed to start filling... */ - } - - /* - * Get a pointer to the active frame. It is either previously - * completed frame or frame in progress but not completed yet. - */ - frame = &uvd->frame[frmx]; - - /* - * Sit back & wait until the frame gets filled and postprocessed. - * If we fail to get the picture [in time] then return the error. - * In this call we specify that we want the frame to be waited for, - * postprocessed and switched into FrameState_Done_Hold state. This - * state is used to hold the frame as "fully completed" between - * subsequent partial reads of the same frame. - */ - if (frame->frameState != FrameState_Done_Hold) { - long rv = -EFAULT; - if (uvd->flags & FLAGS_NO_DECODING) - rv = usbvideo_GetFrame(uvd, frmx); - else if (VALID_CALLBACK(uvd, getFrame)) - rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); - else - err("getFrame is not set"); - if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { - count = rv; - goto read_done; - } - } - - /* - * Copy bytes to user space. We allow for partial reads, which - * means that the user application can request read less than - * the full frame size. It is up to the application to issue - * subsequent calls until entire frame is read. - * - * First things first, make sure we don't copy more than we - * have - even if the application wants more. That would be - * a big security embarassment! - */ - if ((count + frame->seqRead_Index) > frame->seqRead_Length) - count = frame->seqRead_Length - frame->seqRead_Index; - - /* - * Copy requested amount of data to user space. We start - * copying from the position where we last left it, which - * will be zero for a new frame (not read before). - */ - if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { - count = -EFAULT; - goto read_done; - } - - /* Update last read position */ - frame->seqRead_Index += count; - if (uvd->debug >= 1) { - err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", - __func__, count, frame->seqRead_Index); - } - - /* Finally check if the frame is done with and "release" it */ - if (frame->seqRead_Index >= frame->seqRead_Length) { - /* All data has been read */ - frame->seqRead_Index = 0; - - /* Mark it as available to be used again. */ - uvd->frame[frmx].frameState = FrameState_Unused; - if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { - err("%s: usbvideo_NewFrame failed.", __func__); - } - } -read_done: - mutex_unlock(&uvd->lock); - return count; -} - -/* - * Make all of the blocks of data contiguous - */ -static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) -{ - char *cdata; - int i, totlen = 0; - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* Detect and ignore errored packets */ - if (st < 0) { - if (uvd->debug >= 1) - err("Data error: packet=%d. len=%d. status=%d.", i, n, st); - uvd->stats.iso_err_count++; - continue; - } - - /* Detect and ignore empty packets */ - if (n <= 0) { - uvd->stats.iso_skip_count++; - continue; - } - totlen += n; /* Little local accounting */ - RingQueue_Enqueue(&uvd->dp, cdata, n); - } - return totlen; -} - -static void usbvideo_IsocIrq(struct urb *urb) -{ - int i, ret, len; - struct uvd *uvd = urb->context; - - /* We don't want to do anything if we are about to be removed! */ - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; -#if 0 - if (urb->actual_length > 0) { - dev_info(&uvd->dev->dev, - "urb=$%p status=%d. errcount=%d. length=%d.\n", - urb, urb->status, urb->error_count, - urb->actual_length); - } else { - static int c = 0; - if (c++ % 100 == 0) - dev_info(&uvd->dev->dev, "No Isoc data\n"); - } -#endif - - if (!uvd->streaming) { - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "Not streaming, but interrupt!\n"); - return; - } - - uvd->stats.urb_count++; - if (urb->actual_length <= 0) - goto urb_done_with; - - /* Copy the data received into ring queue */ - len = usbvideo_CompressIsochronous(uvd, urb); - uvd->stats.urb_length = len; - if (len <= 0) - goto urb_done_with; - - /* Here we got some data */ - uvd->stats.data_count += len; - RingQueue_WakeUpInterruptible(&uvd->dp); - -urb_done_with: - for (i = 0; i < FRAMES_PER_DESC; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - urb->dev = uvd->dev; - ret = usb_submit_urb (urb, GFP_KERNEL); - if(ret) - err("usb_submit_urb error (%d)", ret); - return; -} - -/* - * usbvideo_StartDataPump() - * - * History: - * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead - * of hardcoded values. Simplified by using for loop, - * allowed any number of URBs. - */ -static int usbvideo_StartDataPump(struct uvd *uvd) -{ - struct usb_device *dev = uvd->dev; - int i, errFlag; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("%s: Camera is not operational", __func__); - return -EFAULT; - } - uvd->curframe = -1; - - /* Alternate interface 1 is is the biggest frame size */ - i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); - if (i < 0) { - err("%s: usb_set_interface error", __func__); - uvd->last_error = i; - return -EBUSY; - } - if (VALID_CALLBACK(uvd, videoStart)) - GET_CALLBACK(uvd, videoStart)(uvd); - else - err("%s: videoStart not set", __func__); - - /* We double buffer the Iso lists */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - int j, k; - struct urb *urb = uvd->sbuf[i].urb; - urb->dev = dev; - urb->context = uvd; - urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = uvd->sbuf[i].data; - urb->complete = usbvideo_IsocIrq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = uvd->iso_packet_len; - } - } - - /* Submit all URBs */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errFlag) - err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag); - } - - uvd->streaming = 1; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: streaming=1 video_endp=$%02x\n", __func__, - uvd->video_endp); - return 0; -} - -/* - * usbvideo_StopDataPump() - * - * This procedure stops streaming and deallocates URBs. Then it - * activates zero-bandwidth alt. setting of the video interface. - * - * History: - * 22-Jan-2000 Corrected order of actions to work after surprise removal. - * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. - */ -static void usbvideo_StopDataPump(struct uvd *uvd) -{ - int i, j; - - if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) - return; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - usb_kill_urb(uvd->sbuf[i].urb); - } - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__); - uvd->streaming = 0; - - if (!uvd->remove_pending) { - /* Invoke minidriver's magic to stop the camera */ - if (VALID_CALLBACK(uvd, videoStop)) - GET_CALLBACK(uvd, videoStop)(uvd); - else - err("%s: videoStop not set", __func__); - - /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); - if (j < 0) { - err("%s: usb_set_interface() error %d.", __func__, j); - uvd->last_error = j; - } - } -} - -/* - * usbvideo_NewFrame() - * - * History: - * 29-Mar-00 Added copying of previous frame into the current one. - * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. - */ -static int usbvideo_NewFrame(struct uvd *uvd, int framenum) -{ - struct usbvideo_frame *frame; - int n; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd, - framenum); - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (uvd->curframe != -1) - return 0; - - /* If necessary we adjust picture settings between frames */ - if (!uvd->settingsAdjusted) { - if (VALID_CALLBACK(uvd, adjustPicture)) - GET_CALLBACK(uvd, adjustPicture)(uvd); - uvd->settingsAdjusted = 1; - } - - n = (framenum + 1) % USBVIDEO_NUMFRAMES; - if (uvd->frame[n].frameState == FrameState_Ready) - framenum = n; - - frame = &uvd->frame[framenum]; - - frame->frameState = FrameState_Grabbing; - frame->scanstate = ScanState_Scanning; - frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ - frame->deinterlace = Deinterlace_None; - frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ - uvd->curframe = framenum; - - /* - * Normally we would want to copy previous frame into the current one - * before we even start filling it with data; this allows us to stop - * filling at any moment; top portion of the frame will be new and - * bottom portion will stay as it was in previous frame. If we don't - * do that then missing chunks of video stream will result in flickering - * portions of old data whatever it was before. - * - * If we choose not to copy previous frame (to, for example, save few - * bus cycles - the frame can be pretty large!) then we have an option - * to clear the frame before using. If we experience losses in this - * mode then missing picture will be black (no flickering). - * - * Finally, if user chooses not to clean the current frame before - * filling it with data then the old data will be visible if we fail - * to refill entire frame with new data. - */ - if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { - /* This copies previous frame into this one to mask losses */ - int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; - memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); - } else { - if (uvd->flags & FLAGS_CLEAN_FRAMES) { - /* This provides a "clean" frame but slows things down */ - memset(frame->data, 0, uvd->max_frame_size); - } - } - return 0; -} - -/* - * usbvideo_CollectRawData() - * - * This procedure can be used instead of 'processData' callback if you - * only want to dump the raw data from the camera into the output - * device (frame buffer). You can look at it with V4L client, but the - * image will be unwatchable. The main purpose of this code and of the - * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from - * new, unknown cameras. This procedure will be automatically invoked - * instead of the specified callback handler when uvd->flags has bit - * FLAGS_NO_DECODING set. Therefore, any regular build of any driver - * based on usbvideo can use this feature at any time. - */ -static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) -{ - int n; - - assert(uvd != NULL); - assert(frame != NULL); - - /* Try to move data from queue into frame buffer */ - n = RingQueue_GetLength(&uvd->dp); - if (n > 0) { - int m; - /* See how much space we have left */ - m = uvd->max_frame_size - frame->seqRead_Length; - if (n > m) - n = m; - /* Now move that much data into frame buffer */ - RingQueue_Dequeue( - &uvd->dp, - frame->data + frame->seqRead_Length, - m); - frame->seqRead_Length += m; - } - /* See if we filled the frame */ - if (frame->seqRead_Length >= uvd->max_frame_size) { - frame->frameState = FrameState_Done; - uvd->curframe = -1; - uvd->stats.frame_num++; - } -} - -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) -{ - struct usbvideo_frame *frame = &uvd->frame[frameNum]; - - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd, - frameNum); - - switch (frame->frameState) { - case FrameState_Unused: - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n", - __func__); - return -EINVAL; - case FrameState_Ready: - case FrameState_Grabbing: - case FrameState_Error: - { - int ntries, signalPending; - redo: - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not operational (1)\n", - __func__); - return -EIO; - } - ntries = 0; - do { - RingQueue_InterruptibleSleepOn(&uvd->dp); - signalPending = signal_pending(current); - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not " - "operational (2)\n", __func__); - return -EIO; - } - assert(uvd->fbuf != NULL); - if (signalPending) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Signal=$%08x\n", __func__, - signalPending); - if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { - usbvideo_TestPattern(uvd, 1, 0); - uvd->curframe = -1; - uvd->stats.frame_num++; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Forced test " - "pattern screen\n", - __func__); - return 0; - } else { - /* Standard answer: Interrupted! */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Interrupted!\n", - __func__); - return -EINTR; - } - } else { - /* No signals - we just got new data in dp queue */ - if (uvd->flags & FLAGS_NO_DECODING) - usbvideo_CollectRawData(uvd, frame); - else if (VALID_CALLBACK(uvd, processData)) - GET_CALLBACK(uvd, processData)(uvd, frame); - else - err("%s: processData not set", __func__); - } - } while (frame->frameState == FrameState_Grabbing); - if (uvd->debug >= 2) { - dev_info(&uvd->dev->dev, - "%s: Grabbing done; state=%d. (%lu. bytes)\n", - __func__, frame->frameState, - frame->seqRead_Length); - } - if (frame->frameState == FrameState_Error) { - int ret = usbvideo_NewFrame(uvd, frameNum); - if (ret < 0) { - err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret); - return ret; - } - goto redo; - } - /* Note that we fall through to meet our destiny below */ - } - case FrameState_Done: - /* - * Do all necessary postprocessing of data prepared in - * "interrupt" code and the collecting code above. The - * frame gets marked as FrameState_Done by queue parsing code. - * This status means that we collected enough data and - * most likely processed it as we went through. However - * the data may need postprocessing, such as deinterlacing - * or picture adjustments implemented in software (horror!) - * - * As soon as the frame becomes "final" it gets promoted to - * FrameState_Done_Hold status where it will remain until the - * caller consumed all the video data from the frame. Then - * the empty shell of ex-frame is thrown out for dogs to eat. - * But we, worried about pets, will recycle the frame! - */ - uvd->stats.frame_num++; - if ((uvd->flags & FLAGS_NO_DECODING) == 0) { - if (VALID_CALLBACK(uvd, postProcess)) - GET_CALLBACK(uvd, postProcess)(uvd, frame); - if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) - usbvideo_SoftwareContrastAdjustment(uvd, frame); - } - frame->frameState = FrameState_Done_Hold; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Entered FrameState_Done_Hold state.\n", - __func__); - return 0; - - case FrameState_Done_Hold: - /* - * We stay in this state indefinitely until someone external, - * like ioctl() or read() call finishes digesting the frame - * data. Then it will mark the frame as FrameState_Unused and - * it will be released back into the wild to roam freely. - */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: FrameState_Done_Hold state.\n", - __func__); - return 0; - } - - /* Catch-all for other cases. We shall not be here. */ - err("%s: Invalid state %d.", __func__, frame->frameState); - frame->frameState = FrameState_Unused; - return 0; -} - -/* - * usbvideo_DeinterlaceFrame() - * - * This procedure deinterlaces the given frame. Some cameras produce - * only half of scanlines - sometimes only even lines, sometimes only - * odd lines. The deinterlacing method is stored in frame->deinterlace - * variable. - * - * Here we scan the frame vertically and replace missing scanlines with - * average between surrounding ones - before and after. If we have no - * line above then we just copy next line. Similarly, if we need to - * create a last line then preceding line is used. - */ -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) -{ - if ((uvd == NULL) || (frame == NULL)) - return; - - if ((frame->deinterlace == Deinterlace_FillEvenLines) || - (frame->deinterlace == Deinterlace_FillOddLines)) - { - const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; - - for (; i < VIDEOSIZE_Y(frame->request); i += 2) { - const unsigned char *fs1, *fs2; - unsigned char *fd; - int ip, in, j; /* Previous and next lines */ - - /* - * Need to average lines before and after 'i'. - * If we go out of bounds seeking those lines then - * we point back to existing line. - */ - ip = i - 1; /* First, get rough numbers */ - in = i + 1; - - /* Now validate */ - if (ip < 0) - ip = in; - if (in >= VIDEOSIZE_Y(frame->request)) - in = ip; - - /* Sanity check */ - if ((ip < 0) || (in < 0) || - (ip >= VIDEOSIZE_Y(frame->request)) || - (in >= VIDEOSIZE_Y(frame->request))) - { - err("Error: ip=%d. in=%d. req.height=%ld.", - ip, in, VIDEOSIZE_Y(frame->request)); - break; - } - - /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ - fs1 = frame->data + (v4l_linesize * ip); - fs2 = frame->data + (v4l_linesize * in); - fd = frame->data + (v4l_linesize * i); - - /* Average lines around destination */ - for (j=0; j < v4l_linesize; j++) { - fd[j] = (unsigned char)((((unsigned) fs1[j]) + - ((unsigned)fs2[j])) >> 1); - } - } - } - - /* Optionally display statistics on the screen */ - if (uvd->flags & FLAGS_OVERLAY_STATS) - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); - -/* - * usbvideo_SoftwareContrastAdjustment() - * - * This code adjusts the contrast of the frame, assuming RGB24 format. - * As most software image processing, this job is CPU-intensive. - * Get a camera that supports hardware adjustment! - * - * History: - * 09-Feb-2001 Created. - */ -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame) -{ - int i, j, v4l_linesize; - signed long adj; - const int ccm = 128; /* Color correction median - see below */ - - if ((uvd == NULL) || (frame == NULL)) { - err("%s: Illegal call.", __func__); - return; - } - adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ - RESTRICT_TO_RANGE(adj, -ccm, ccm+1); - if (adj == 0) { - /* In rare case of no adjustment */ - return; - } - v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { - unsigned char *fd = frame->data + (v4l_linesize * i); - for (j=0; j < v4l_linesize; j++) { - signed long v = (signed long) fd[j]; - /* Magnify up to 2 times, reduce down to zero */ - v = 128 + ((ccm + adj) * (v - 128)) / ccm; - RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ - fd[j] = (unsigned char) v; - } - } -} - -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/usbvideo/usbvideo.h b/drivers/staging/usbvideo/usbvideo.h deleted file mode 100644 index 95638a072b19..000000000000 --- a/drivers/staging/usbvideo/usbvideo.h +++ /dev/null @@ -1,395 +0,0 @@ -/* - * 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, 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. - */ -#ifndef usbvideo_h -#define usbvideo_h - -#include "videodev.h" -#include -#include -#include -#include - -/* Most helpful debugging aid */ -#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) - -#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ - -/* Bit flags (options) */ -#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) -#define FLAGS_MONOCHROME (1 << 1) -#define FLAGS_DISPLAY_HINTS (1 << 2) -#define FLAGS_OVERLAY_STATS (1 << 3) -#define FLAGS_FORCE_TESTPATTERN (1 << 4) -#define FLAGS_SEPARATE_FRAMES (1 << 5) -#define FLAGS_CLEAN_FRAMES (1 << 6) -#define FLAGS_NO_DECODING (1 << 7) - -/* Bit flags for frames (apply to the frame where they are specified) */ -#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) - -/* Camera capabilities (maximum) */ -#define CAMERA_URB_FRAMES 32 -#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ -#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) -#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) - -/* This macro restricts an int variable to an inclusive range */ -#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } - -#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ - -/* - * Use this macro to construct constants for different video sizes. - * We have to deal with different video sizes that have to be - * configured in the device or compared against when we receive - * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y - * #defines and that's the end of story. However this solution - * does not allow to convert between real pixel sizes and the - * constant (integer) value that may be used to tag a frame or - * whatever. The set of macros below constructs videosize constants - * from the pixel size and allows to reconstruct the pixel size - * from the combined value later. - */ -#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) -#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) -#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) -typedef unsigned long videosize_t; - -/* - * This macro checks if the camera is still operational. The 'uvd' - * pointer must be valid, uvd->dev must be valid, we are not - * removing the device and the device has not erred on us. - */ -#define CAMERA_IS_OPERATIONAL(uvd) (\ - (uvd != NULL) && \ - ((uvd)->dev != NULL) && \ - ((uvd)->last_error == 0) && \ - (!(uvd)->remove_pending)) - -/* - * We use macros to do YUV -> RGB conversion because this is - * very important for speed and totally unimportant for size. - * - * YUV -> RGB Conversion - * --------------------- - * - * B = 1.164*(Y-16) + 2.018*(V-128) - * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) - * R = 1.164*(Y-16) + 1.596*(U-128) - * - * If you fancy integer arithmetics (as you should), hear this: - * - * 65536*B = 76284*(Y-16) + 132252*(V-128) - * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) - * 65536*R = 76284*(Y-16) + 104595*(U-128) - * - * Make sure the output values are within [0..255] range. - */ -#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) -#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ - int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ - mm_y = (my) - 16; \ - mm_u = (mu) - 128; \ - mm_v = (mv) - 128; \ - mm_yc= mm_y * 76284; \ - mm_b = (mm_yc + 132252*mm_v ) >> 16; \ - mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ - mm_r = (mm_yc + 104595*mm_u ) >> 16; \ - mb = LIMIT_RGB(mm_b); \ - mg = LIMIT_RGB(mm_g); \ - mr = LIMIT_RGB(mm_r); \ -} - -#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ -#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) -#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) -#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) - -struct RingQueue { - unsigned char *queue; /* Data from the Isoc data pump */ - int length; /* How many bytes allocated for the queue */ - int wi; /* That's where we write */ - int ri; /* Read from here until you hit write index */ - wait_queue_head_t wqh; /* Processes waiting */ -}; - -enum ScanState { - ScanState_Scanning, /* Scanning for header */ - ScanState_Lines /* Parsing lines */ -}; - -/* Completion states of the data parser */ -enum ParseState { - scan_Continue, /* Just parse next item */ - scan_NextFrame, /* Frame done, send it to V4L */ - scan_Out, /* Not enough data for frame */ - scan_EndParse /* End parsing */ -}; - -enum FrameState { - FrameState_Unused, /* Unused (no MCAPTURE) */ - FrameState_Ready, /* Ready to start grabbing */ - FrameState_Grabbing, /* In the process of being grabbed into */ - FrameState_Done, /* Finished grabbing, but not been synced yet */ - FrameState_Done_Hold, /* Are syncing or reading */ - FrameState_Error, /* Something bad happened while processing */ -}; - -/* - * Some frames may contain only even or odd lines. This type - * specifies what type of deinterlacing is required. - */ -enum Deinterlace { - Deinterlace_None=0, - Deinterlace_FillOddLines, - Deinterlace_FillEvenLines -}; - -#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ -#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ - -/* This structure represents one Isoc request - URB and buffer */ -struct usbvideo_sbuf { - char *data; - struct urb *urb; -}; - -struct usbvideo_frame { - char *data; /* Frame buffer */ - unsigned long header; /* Significant bits from the header */ - - videosize_t canvas; /* The canvas (max. image) allocated */ - videosize_t request; /* That's what the application asked for */ - unsigned short palette; /* The desired format */ - - enum FrameState frameState;/* State of grabbing */ - enum ScanState scanstate; /* State of scanning */ - enum Deinterlace deinterlace; - int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ - - int curline; /* Line of frame we're working on */ - - long seqRead_Length; /* Raw data length of frame */ - long seqRead_Index; /* Amount of data that has been already read */ - - void *user; /* Additional data that user may need */ -}; - -/* Statistics that can be overlaid on screen */ -struct usbvideo_statistics { - unsigned long frame_num; /* Sequential number of the frame */ - unsigned long urb_count; /* How many URBs we received so far */ - unsigned long urb_length; /* Length of last URB */ - unsigned long data_count; /* How many bytes we received */ - unsigned long header_count; /* How many frame headers we found */ - unsigned long iso_skip_count; /* How many empty ISO packets received */ - unsigned long iso_err_count; /* How many bad ISO packets received */ -}; - -struct usbvideo; - -struct uvd { - struct video_device vdev; /* Must be the first field! */ - struct usb_device *dev; - struct usbvideo *handle; /* Points back to the struct usbvideo */ - void *user_data; /* Camera-dependent data */ - int user_size; /* Size of that camera-dependent data */ - int debug; /* Debug level for usbvideo */ - unsigned char iface; /* Video interface number */ - unsigned char video_endp; - unsigned char ifaceAltActive; - unsigned char ifaceAltInactive; /* Alt settings */ - unsigned long flags; /* FLAGS_USBVIDEO_xxx */ - unsigned long paletteBits; /* Which palettes we accept? */ - unsigned short defaultPalette; /* What palette to use for read() */ - struct mutex lock; - int user; /* user count for exclusive use */ - - videosize_t videosize; /* Current setting */ - videosize_t canvas; /* This is the width,height of the V4L canvas */ - int max_frame_size; /* Bytes in one video frame */ - - int uvd_used; /* Is this structure in use? */ - int streaming; /* Are we streaming Isochronous? */ - int grabbing; /* Are we grabbing? */ - int settingsAdjusted; /* Have we adjusted contrast etc.? */ - int last_error; /* What calamity struck us? */ - - char *fbuf; /* Videodev buffer area */ - int fbuf_size; /* Videodev buffer size */ - - int curframe; - int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ - - struct RingQueue dp; /* Isoc data pump */ - struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; - struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; - - volatile int remove_pending; /* If set then about to exit */ - - struct video_picture vpic, vpic_old; /* Picture settings */ - struct video_capability vcap; /* Video capabilities */ - struct video_channel vchan; /* May be used for tuner support */ - struct usbvideo_statistics stats; - char videoName[32]; /* Holds name like "video7" */ -}; - -/* - * usbvideo callbacks (virtual methods). They are set when usbvideo - * services are registered. All of these default to NULL, except those - * that default to usbvideo-provided methods. - */ -struct usbvideo_cb { - int (*probe)(struct usb_interface *, const struct usb_device_id *); - void (*userFree)(struct uvd *); - void (*disconnect)(struct usb_interface *); - int (*setupOnOpen)(struct uvd *); - void (*videoStart)(struct uvd *); - void (*videoStop)(struct uvd *); - void (*processData)(struct uvd *, struct usbvideo_frame *); - void (*postProcess)(struct uvd *, struct usbvideo_frame *); - void (*adjustPicture)(struct uvd *); - int (*getFPS)(struct uvd *); - int (*overlayHook)(struct uvd *, struct usbvideo_frame *); - int (*getFrame)(struct uvd *, int); - int (*startDataPump)(struct uvd *uvd); - void (*stopDataPump)(struct uvd *uvd); - int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); -}; - -struct usbvideo { - int num_cameras; /* As allocated */ - struct usb_driver usbdrv; /* Interface to the USB stack */ - char drvName[80]; /* Driver name */ - struct mutex lock; /* Mutex protecting camera structures */ - struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ - struct video_device vdt; /* Video device template */ - struct uvd *cam; /* Array of camera structures */ - struct module *md_module; /* Minidriver module */ -}; - - -/* - * This macro retrieves callback address from the struct uvd object. - * No validity checks are done here, so be sure to check the - * callback beforehand with VALID_CALLBACK. - */ -#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) - -/* - * This macro returns either callback pointer or NULL. This is safe - * macro, meaning that most of components of data structures involved - * may be NULL - this only results in NULL being returned. You may - * wish to use this macro to make sure that the callback is callable. - * However keep in mind that those checks take time. - */ -#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ - ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); -void RingQueue_WakeUpInterruptible(struct RingQueue *rq); -void RingQueue_Flush(struct RingQueue *rq); - -static inline int RingQueue_GetLength(const struct RingQueue *rq) -{ - return (rq->wi - rq->ri + rq->length) & (rq->length-1); -} - -static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) -{ - return rq->length - RingQueue_GetLength(rq); -} - -void usbvideo_DrawLine( - struct usbvideo_frame *frame, - int x1, int y1, - int x2, int y2, - unsigned char cr, unsigned char cg, unsigned char cb); -void usbvideo_HexDump(const unsigned char *data, int len); -void usbvideo_SayAndWait(const char *what); -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); - -/* Memory allocation routines */ -unsigned long usbvideo_kvirt_to_pa(unsigned long adr); - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTable, - struct module *md, - const struct usb_device_id *id_table); -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); -int usbvideo_RegisterVideoDevice(struct uvd *uvd); -void usbvideo_Deregister(struct usbvideo **uvt); - -int usbvideo_v4l_initialize(struct video_device *dev); - -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); - -/* - * This code performs bounds checking - use it when working with - * new formats, or else you may get oopses all over the place. - * If pixel falls out of bounds then it gets shoved back (as close - * to place of offence as possible) and is painted bright red. - * - * There are two important concepts: frame width, height and - * V4L canvas width, height. The former is the area requested by - * the application -for this very frame-. The latter is the largest - * possible frame that we can serve (we advertise that via V4L ioctl). - * The frame data is expected to be formatted as lines of length - * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. - */ -static inline void RGB24_PUTPIXEL( - struct usbvideo_frame *fr, - int ix, int iy, - unsigned char vr, - unsigned char vg, - unsigned char vb) -{ - register unsigned char *pf; - int limiter = 0, mx, my; - mx = ix; - my = iy; - if (mx < 0) { - mx=0; - limiter++; - } else if (mx >= VIDEOSIZE_X((fr)->request)) { - mx= VIDEOSIZE_X((fr)->request) - 1; - limiter++; - } - if (my < 0) { - my = 0; - limiter++; - } else if (my >= VIDEOSIZE_Y((fr)->request)) { - my = VIDEOSIZE_Y((fr)->request) - 1; - limiter++; - } - pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); - if (limiter) { - *pf++ = 0; - *pf++ = 0; - *pf++ = 0xFF; - } else { - *pf++ = (vb); - *pf++ = (vg); - *pf++ = (vr); - } -} - -#endif /* usbvideo_h */ diff --git a/drivers/staging/usbvideo/vicam.c b/drivers/staging/usbvideo/vicam.c deleted file mode 100644 index ecdb121297c9..000000000000 --- a/drivers/staging/usbvideo/vicam.c +++ /dev/null @@ -1,952 +0,0 @@ -/* - * USB ViCam WebCam driver - * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), - * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@ucw.cz), - * John Tyner (jtyner@cs.ucr.edu), - * Monroe Williams (monroe@pobox.com) - * - * Supports 3COM HomeConnect PC Digital WebCam - * Supports Compro PS39U WebCam - * - * 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. - * - * This source code is based heavily on the CPiA webcam driver which was - * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt - * - * Portions of this code were also copied from usbvideo.c - * - * Special thanks to the whole team at Sourceforge for help making - * this driver become a reality. Notably: - * Andy Armstrong who reverse engineered the color encoding and - * Pavel Machek and Chris Cheney who worked on reverse engineering the - * camera controls and wrote the first generation driver. - */ - -#include -#include -#include -#include "videodev.h" -#include -#include -#include -#include -#include -#include -#include -#include "usbvideo.h" - -// #define VICAM_DEBUG - -#ifdef VICAM_DEBUG -#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args) -#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" -#define DRIVER_DESC "ViCam WebCam Driver" - -/* Define these values to match your device */ -#define USB_VICAM_VENDOR_ID 0x04c1 -#define USB_VICAM_PRODUCT_ID 0x009d -#define USB_COMPRO_VENDOR_ID 0x0602 -#define USB_COMPRO_PRODUCT_ID 0x1001 - -#define VICAM_BYTES_PER_PIXEL 3 -#define VICAM_MAX_READ_SIZE (512*242+128) -#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) -#define VICAM_FRAMES 2 - -#define VICAM_HEADER_SIZE 64 - -/* rvmalloc / rvfree copied from usbvideo.c - * - * Not sure why these are not yet non-statics which I can reference through - * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime - * in the future. - * -*/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -struct vicam_camera { - u16 shutter_speed; // capture shutter speed - u16 gain; // capture gain - - u8 *raw_image; // raw data captured from the camera - u8 *framebuf; // processed data in RGB24 format - u8 *cntrlbuf; // area used to send control msgs - - struct video_device vdev; // v4l video device - struct usb_device *udev; // usb device - - /* guard against simultaneous accesses to the camera */ - struct mutex cam_lock; - - int is_initialized; - u8 open_count; - u8 bulkEndpoint; - int needsDummyRead; -}; - -static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); -static void vicam_disconnect(struct usb_interface *intf); -static void read_frame(struct vicam_camera *cam, int framenum); -static void vicam_decode_color(const u8 *, u8 *); - -static int __send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status; - - /* cp must be memory that has been allocated by kmalloc */ - - status = usb_control_msg(cam->udev, - usb_sndctrlpipe(cam->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - cp, size, 1000); - - status = min(status, 0); - - if (status < 0) { - printk(KERN_INFO "Failed sending control message, error %d.\n", - status); - } - - return status; -} - -static int send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status = -ENODEV; - mutex_lock(&cam->cam_lock); - if (cam->udev) { - status = __send_control_msg(cam, request, value, - index, cp, size); - } - mutex_unlock(&cam->cam_lock); - return status; -} -static int -initialize_camera(struct vicam_camera *cam) -{ - int err; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - - err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); - if (err) { - printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n", - err); - return err; - } - - for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len)); - - err = send_control_msg(cam, 0xff, 0, 0, - cam->cntrlbuf, be16_to_cpu(rec->len)); - if (err) - break; - } - - release_firmware(fw); - - return err; -} - -static int -set_camera_power(struct vicam_camera *cam, int state) -{ - int status; - - if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) - return status; - - if (state) { - send_control_msg(cam, 0x55, 1, 0, NULL, 0); - } - - return 0; -} - -static long -vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg) -{ - void __user *user_arg = (void __user *)arg; - struct vicam_camera *cam = file->private_data; - long retval = 0; - - if (!cam) - return -ENODEV; - - switch (ioctlnr) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability b; - - DBG("VIDIOCGCAP\n"); - memset(&b, 0, sizeof(b)); - strcpy(b.name, "ViCam-based Camera"); - b.type = VID_TYPE_CAPTURE; - b.channels = 1; - b.audios = 0; - b.maxwidth = 320; /* VIDEOSIZE_CIF */ - b.maxheight = 240; - b.minwidth = 320; /* VIDEOSIZE_48_48 */ - b.minheight = 240; - - if (copy_to_user(user_arg, &b, sizeof(b))) - retval = -EFAULT; - - break; - } - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel v; - - DBG("VIDIOCGCHAN\n"); - if (copy_from_user(&v, user_arg, sizeof(v))) { - retval = -EFAULT; - break; - } - if (v.channel != 0) { - retval = -EINVAL; - break; - } - - v.channel = 0; - strcpy(v.name, "Camera"); - v.tuners = 0; - v.flags = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = 0; - - if (copy_to_user(user_arg, &v, sizeof(v))) - retval = -EFAULT; - break; - } - - case VIDIOCSCHAN: - { - int v; - - if (copy_from_user(&v, user_arg, sizeof(v))) - retval = -EFAULT; - DBG("VIDIOCSCHAN %d\n", v); - - if (retval == 0 && v != 0) - retval = -EINVAL; - - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture vp; - DBG("VIDIOCGPICT\n"); - memset(&vp, 0, sizeof (struct video_picture)); - vp.brightness = cam->gain << 8; - vp.depth = 24; - vp.palette = VIDEO_PALETTE_RGB24; - if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) - retval = -EFAULT; - break; - } - - case VIDIOCSPICT: - { - struct video_picture vp; - - if (copy_from_user(&vp, user_arg, sizeof(vp))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, - vp.palette); - - cam->gain = vp.brightness >> 8; - - if (vp.depth != 24 - || vp.palette != VIDEO_PALETTE_RGB24) - retval = -EINVAL; - - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window vw; - vw.x = 0; - vw.y = 0; - vw.width = 320; - vw.height = 240; - vw.chromakey = 0; - vw.flags = 0; - vw.clips = NULL; - vw.clipcount = 0; - - DBG("VIDIOCGWIN\n"); - - if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) - retval = -EFAULT; - - // I'm not sure what the deal with a capture window is, it is very poorly described - // in the doc. So I won't support it now. - break; - } - - case VIDIOCSWIN: - { - - struct video_window vw; - - if (copy_from_user(&vw, user_arg, sizeof(vw))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); - - if ( vw.width != 320 || vw.height != 240 ) - retval = -EFAULT; - - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf vm; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(&vm, 0, sizeof (vm)); - vm.size = - VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; - vm.frames = VICAM_FRAMES; - for (i = 0; i < VICAM_FRAMES; i++) - vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; - - if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) - retval = -EFAULT; - - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - // int video_size; - - if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); - - if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) - retval = -EINVAL; - - // in theory right here we'd start the image capturing - // (fill in a bulk urb and submit it asynchronously) - // - // Instead we're going to do a total hack job for now and - // retrieve the frame in VIDIOCSYNC - - break; - } - - case VIDIOCSYNC: - { - int frame; - - if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { - retval = -EFAULT; - break; - } - DBG("VIDIOCSYNC: %d\n", frame); - - read_frame(cam, frame); - vicam_decode_color(cam->raw_image, - cam->framebuf + - frame * VICAM_MAX_FRAME_SIZE ); - - break; - } - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - retval = -EINVAL; - break; - - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - retval = -EINVAL; - break; - - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - return retval; -} - -static int -vicam_open(struct file *file) -{ - struct vicam_camera *cam = video_drvdata(file); - - DBG("open\n"); - - if (!cam) { - printk(KERN_ERR - "vicam video_device improperly initialized"); - return -EINVAL; - } - - /* cam_lock/open_count protects us from simultaneous opens - * ... for now. we probably shouldn't rely on this fact forever. - */ - - mutex_lock(&cam->cam_lock); - if (cam->open_count > 0) { - printk(KERN_INFO - "vicam_open called on already opened camera"); - mutex_unlock(&cam->cam_lock); - return -EBUSY; - } - - cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); - if (!cam->raw_image) { - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - if (!cam->framebuf) { - kfree(cam->raw_image); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!cam->cntrlbuf) { - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->needsDummyRead = 1; - cam->open_count++; - - file->private_data = cam; - mutex_unlock(&cam->cam_lock); - - - // First upload firmware, then turn the camera on - - if (!cam->is_initialized) { - initialize_camera(cam); - - cam->is_initialized = 1; - } - - set_camera_power(cam, 1); - - return 0; -} - -static int -vicam_close(struct file *file) -{ - struct vicam_camera *cam = file->private_data; - int open_count; - struct usb_device *udev; - - DBG("close\n"); - - /* it's not the end of the world if - * we fail to turn the camera off. - */ - - set_camera_power(cam, 0); - - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - kfree(cam->cntrlbuf); - - mutex_lock(&cam->cam_lock); - - cam->open_count--; - open_count = cam->open_count; - udev = cam->udev; - - mutex_unlock(&cam->cam_lock); - - if (!open_count && !udev) { - kfree(cam); - } - - return 0; -} - -static void vicam_decode_color(const u8 *data, u8 *rgb) -{ - /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB - * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) - */ - - int i, prevY, nextY; - - prevY = 512; - nextY = 512; - - data += VICAM_HEADER_SIZE; - - for( i = 0; i < 240; i++, data += 512 ) { - const int y = ( i * 242 ) / 240; - - int j, prevX, nextX; - int Y, Cr, Cb; - - if ( y == 242 - 1 ) { - nextY = -512; - } - - prevX = 1; - nextX = 1; - - for ( j = 0; j < 320; j++, rgb += 3 ) { - const int x = ( j * 512 ) / 320; - const u8 * const src = &data[x]; - - if ( x == 512 - 1 ) { - nextX = -1; - } - - Cr = ( src[prevX] - src[0] ) + - ( src[nextX] - src[0] ); - Cr /= 2; - - Cb = ( src[prevY] - src[prevX + prevY] ) + - ( src[prevY] - src[nextX + prevY] ) + - ( src[nextY] - src[prevX + nextY] ) + - ( src[nextY] - src[nextX + nextY] ); - Cb /= 4; - - Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); - - if ( i & 1 ) { - int Ct = Cr; - Cr = Cb; - Cb = Ct; - } - - if ( ( x ^ i ) & 1 ) { - Cr = -Cr; - Cb = -Cb; - } - - rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + - 500 ) / 900, 0, 255 ); - rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - - ( 813 * Cr ) ) + - 500 ) / 1000, 0, 255 ); - rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + - 500 ) / 1300, 0, 255 ); - - prevX = -1; - } - - prevY = -512; - } -} - -static void -read_frame(struct vicam_camera *cam, int framenum) -{ - unsigned char *request = cam->cntrlbuf; - int realShutter; - int n; - int actual_length; - - if (cam->needsDummyRead) { - cam->needsDummyRead = 0; - read_frame(cam, framenum); - } - - memset(request, 0, 16); - request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain - - request[1] = 0; // 512x242 capture - - request[2] = 0x90; // the function of these two bytes - request[3] = 0x07; // is not yet understood - - if (cam->shutter_speed > 60) { - // Short exposure - realShutter = - ((-15631900 / cam->shutter_speed) + 260533) / 1000; - request[4] = realShutter & 0xFF; - request[5] = (realShutter >> 8) & 0xFF; - request[6] = 0x03; - request[7] = 0x01; - } else { - // Long exposure - realShutter = 15600 / cam->shutter_speed - 1; - request[4] = 0; - request[5] = 0; - request[6] = realShutter & 0xFF; - request[7] = realShutter >> 8; - } - - // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 - request[8] = 0; - // bytes 9-15 do not seem to affect exposure or image quality - - mutex_lock(&cam->cam_lock); - - if (!cam->udev) { - goto done; - } - - n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); - - if (n < 0) { - printk(KERN_ERR - " Problem sending frame capture control message"); - goto done; - } - - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), - cam->raw_image, - 512 * 242 + 128, &actual_length, 10000); - - if (n < 0) { - printk(KERN_ERR "Problem during bulk read of frame data: %d\n", - n); - } - - done: - mutex_unlock(&cam->cam_lock); -} - -static ssize_t -vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) -{ - struct vicam_camera *cam = file->private_data; - - DBG("read %d bytes.\n", (int) count); - - if (*ppos >= VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - return 0; - } - - if (*ppos == 0) { - read_frame(cam, 0); - vicam_decode_color(cam->raw_image, - cam->framebuf + - 0 * VICAM_MAX_FRAME_SIZE); - } - - count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); - - if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { - count = -EFAULT; - } else { - *ppos += count; - } - - if (count == VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - } - - return count; -} - - -static int -vicam_mmap(struct file *file, struct vm_area_struct *vma) -{ - // TODO: allocate the raw frame buffer if necessary - unsigned long page, pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - struct vicam_camera *cam = file->private_data; - - if (!cam) - return -ENODEV; - - DBG("vicam_mmap: %ld\n", size); - - /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes - * to the size the application requested for mmap and it was screwing apps up. - if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) - return -EINVAL; - */ - - pos = (unsigned long)cam->framebuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -static const struct v4l2_file_operations vicam_fops = { - .owner = THIS_MODULE, - .open = vicam_open, - .release = vicam_close, - .read = vicam_read, - .mmap = vicam_mmap, - .ioctl = vicam_ioctl, -}; - -static struct video_device vicam_template = { - .name = "ViCam-based USB Camera", - .fops = &vicam_fops, - .release = video_device_release_empty, -}; - -/* table of devices that work with this driver */ -static struct usb_device_id vicam_table[] = { - {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, - {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, vicam_table); - -static struct usb_driver vicam_driver = { - .name = "vicam", - .probe = vicam_probe, - .disconnect = vicam_disconnect, - .id_table = vicam_table -}; - -/** - * vicam_probe - * @intf: the interface - * @id: the device id - * - * Called by the usb core when a new device is connected that it thinks - * this driver might be interested in. - */ -static int -vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int bulkEndpoint = 0; - const struct usb_host_interface *interface; - const struct usb_endpoint_descriptor *endpoint; - struct vicam_camera *cam; - - printk(KERN_INFO "ViCam based webcam connected\n"); - - interface = intf->cur_altsetting; - - DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", - interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); - endpoint = &interface->endpoint[0].desc; - - if (usb_endpoint_is_bulk_in(endpoint)) { - /* we found a bulk in endpoint */ - bulkEndpoint = endpoint->bEndpointAddress; - } else { - printk(KERN_ERR - "No bulk in endpoint was found ?! (this is bad)\n"); - } - - if ((cam = - kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "could not allocate kernel memory for vicam_camera struct\n"); - return -ENOMEM; - } - - - cam->shutter_speed = 15; - - mutex_init(&cam->cam_lock); - - memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->udev = dev; - cam->bulkEndpoint = bulkEndpoint; - - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { - kfree(cam); - printk(KERN_WARNING "video_register_device failed\n"); - return -EIO; - } - - printk(KERN_INFO "ViCam webcam driver now controlling device %s\n", - video_device_node_name(&cam->vdev)); - - usb_set_intfdata (intf, cam); - - return 0; -} - -static void -vicam_disconnect(struct usb_interface *intf) -{ - int open_count; - struct vicam_camera *cam = usb_get_intfdata (intf); - usb_set_intfdata (intf, NULL); - - /* we must unregister the device before taking its - * cam_lock. This is because the video open call - * holds the same lock as video unregister. if we - * unregister inside of the cam_lock and open also - * uses the cam_lock, we get deadlock. - */ - - video_unregister_device(&cam->vdev); - - /* stop the camera from being used */ - - mutex_lock(&cam->cam_lock); - - /* mark the camera as gone */ - - cam->udev = NULL; - - /* the only thing left to do is synchronize with - * our close/release function on who should release - * the camera memory. if there are any users using the - * camera, it's their job. if there are no users, - * it's ours. - */ - - open_count = cam->open_count; - - mutex_unlock(&cam->cam_lock); - - if (!open_count) { - kfree(cam); - } - - printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); -} - -/* - */ -static int __init -usb_vicam_init(void) -{ - int retval; - DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); - retval = usb_register(&vicam_driver); - if (retval) - printk(KERN_WARNING "usb_register failed!\n"); - return retval; -} - -static void __exit -usb_vicam_exit(void) -{ - DBG(KERN_INFO - "ViCam-based WebCam driver shutdown\n"); - - usb_deregister(&vicam_driver); -} - -module_init(usb_vicam_init); -module_exit(usb_vicam_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("vicam/firmware.fw"); diff --git a/drivers/staging/usbvideo/videodev.h b/drivers/staging/usbvideo/videodev.h deleted file mode 100644 index f11efbef1c05..000000000000 --- a/drivers/staging/usbvideo/videodev.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Video for Linux version 1 - OBSOLETE - * - * Header file for v4l1 drivers and applications, for - * Linux kernels 2.2.x or 2.4.x. - * - * Provides header for legacy drivers and applications - * - * See http://linuxtv.org for more info - * - */ -#ifndef __LINUX_VIDEODEV_H -#define __LINUX_VIDEODEV_H - -#include -#include -#include - -#define VID_TYPE_CAPTURE 1 /* Can capture */ -#define VID_TYPE_TUNER 2 /* Can tune */ -#define VID_TYPE_TELETEXT 4 /* Does teletext */ -#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ -#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ -#define VID_TYPE_CLIPPING 32 /* Can clip */ -#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ -#define VID_TYPE_SCALES 128 /* Scalable */ -#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ -#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ -#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ -#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ -#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ -#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ - -struct video_capability -{ - char name[32]; - int type; - int channels; /* Num channels */ - int audios; /* Num audio devices */ - int maxwidth; /* Supported width */ - int maxheight; /* And height */ - int minwidth; /* Supported width */ - int minheight; /* And height */ -}; - - -struct video_channel -{ - int channel; - char name[32]; - int tuners; - __u32 flags; -#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ -#define VIDEO_VC_AUDIO 2 /* Channel has audio */ - __u16 type; -#define VIDEO_TYPE_TV 1 -#define VIDEO_TYPE_CAMERA 2 - __u16 norm; /* Norm set by channel */ -}; - -struct video_tuner -{ - int tuner; - char name[32]; - unsigned long rangelow, rangehigh; /* Tuner range */ - __u32 flags; -#define VIDEO_TUNER_PAL 1 -#define VIDEO_TUNER_NTSC 2 -#define VIDEO_TUNER_SECAM 4 -#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ -#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ -#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ -#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ -#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ - __u16 mode; /* PAL/NTSC/SECAM/OTHER */ -#define VIDEO_MODE_PAL 0 -#define VIDEO_MODE_NTSC 1 -#define VIDEO_MODE_SECAM 2 -#define VIDEO_MODE_AUTO 3 - __u16 signal; /* Signal strength 16bit scale */ -}; - -struct video_picture -{ - __u16 brightness; - __u16 hue; - __u16 colour; - __u16 contrast; - __u16 whiteness; /* Black and white only */ - __u16 depth; /* Capture depth */ - __u16 palette; /* Palette in use */ -#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ -#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ -#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ -#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ -#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ -#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ -#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ -#define VIDEO_PALETTE_YUYV 8 -#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ -#define VIDEO_PALETTE_YUV420 10 -#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ -#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ -#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ -#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ -#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ -#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ -#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ -#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ -}; - -struct video_audio -{ - int audio; /* Audio channel */ - __u16 volume; /* If settable */ - __u16 bass, treble; - __u32 flags; -#define VIDEO_AUDIO_MUTE 1 -#define VIDEO_AUDIO_MUTABLE 2 -#define VIDEO_AUDIO_VOLUME 4 -#define VIDEO_AUDIO_BASS 8 -#define VIDEO_AUDIO_TREBLE 16 -#define VIDEO_AUDIO_BALANCE 32 - char name[16]; -#define VIDEO_SOUND_MONO 1 -#define VIDEO_SOUND_STEREO 2 -#define VIDEO_SOUND_LANG1 4 -#define VIDEO_SOUND_LANG2 8 - __u16 mode; - __u16 balance; /* Stereo balance */ - __u16 step; /* Step actual volume uses */ -}; - -struct video_clip -{ - __s32 x,y; - __s32 width, height; - struct video_clip *next; /* For user use/driver use only */ -}; - -struct video_window -{ - __u32 x,y; /* Position of window */ - __u32 width,height; /* Its size */ - __u32 chromakey; - __u32 flags; - struct video_clip __user *clips; /* Set only */ - int clipcount; -#define VIDEO_WINDOW_INTERLACE 1 -#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ -#define VIDEO_CLIP_BITMAP -1 -/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ -#define VIDEO_CLIPMAP_SIZE (128 * 625) -}; - -struct video_capture -{ - __u32 x,y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ -#define VIDEO_CAPTURE_ODD 0 /* Temporal */ -#define VIDEO_CAPTURE_EVEN 1 -}; - -struct video_buffer -{ - void *base; - int height,width; - int depth; - int bytesperline; -}; - -struct video_mmap -{ - unsigned int frame; /* Frame (0 - n) for double buffer */ - int height,width; - unsigned int format; /* should be VIDEO_PALETTE_* */ -}; - -struct video_key -{ - __u8 key[8]; - __u32 flags; -}; - -struct video_mbuf -{ - int size; /* Total memory to map */ - int frames; /* Frames */ - int offsets[VIDEO_MAX_FRAME]; -}; - -#define VIDEO_NO_UNIT (-1) - -struct video_unit -{ - int video; /* Video minor */ - int vbi; /* VBI minor */ - int radio; /* Radio minor */ - int audio; /* Audio minor */ - int teletext; /* Teletext minor */ -}; - -struct vbi_format { - __u32 sampling_rate; /* in Hz */ - __u32 samples_per_line; - __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ - __s32 start[2]; /* starting line for each frame */ - __u32 count[2]; /* count of lines for each frame */ - __u32 flags; -#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ -#define VBI_INTERLACED 2 /* lines are interlaced */ -}; - -/* video_info is biased towards hardware mpeg encode/decode */ -/* but it could apply generically to any hardware compressor/decompressor */ -struct video_info -{ - __u32 frame_count; /* frames output since decode/encode began */ - __u32 h_size; /* current unscaled horizontal size */ - __u32 v_size; /* current unscaled veritcal size */ - __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ - __u32 picture_type; /* current picture type */ - __u32 temporal_reference; /* current temporal reference */ - __u8 user_data[256]; /* user data last found in compressed stream */ - /* user_data[0] contains user data flags, user_data[1] has count */ -}; - -/* generic structure for setting playback modes */ -struct video_play_mode -{ - int mode; - int p1; - int p2; -}; - -/* for loading microcode / fpga programming */ -struct video_code -{ - char loadwhat[16]; /* name or tag of file being passed */ - int datasize; - __u8 *data; -}; - -#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ -#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ -#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ -#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ -#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ -#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ -#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ -#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ -#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ -#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ -#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ -#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ -#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ -#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ -#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ -#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ -#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ -#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ -#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ -#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ -#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ -#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ -#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ -#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ -#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ -#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ -#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ -#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ -#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ - - -#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ - -/* VIDIOCSWRITEMODE */ -#define VID_WRITE_MPEG_AUD 0 -#define VID_WRITE_MPEG_VID 1 -#define VID_WRITE_OSD 2 -#define VID_WRITE_TTX 3 -#define VID_WRITE_CC 4 -#define VID_WRITE_MJPEG 5 - -/* VIDIOCSPLAYMODE */ -#define VID_PLAY_VID_OUT_MODE 0 - /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ -#define VID_PLAY_GENLOCK 1 - /* p1: 0 = OFF, 1 = ON */ - /* p2: GENLOCK FINE DELAY value */ -#define VID_PLAY_NORMAL 2 -#define VID_PLAY_PAUSE 3 -#define VID_PLAY_SINGLE_FRAME 4 -#define VID_PLAY_FAST_FORWARD 5 -#define VID_PLAY_SLOW_MOTION 6 -#define VID_PLAY_IMMEDIATE_NORMAL 7 -#define VID_PLAY_SWITCH_CHANNELS 8 -#define VID_PLAY_FREEZE_FRAME 9 -#define VID_PLAY_STILL_MODE 10 -#define VID_PLAY_MASTER_MODE 11 - /* p1: see below */ -#define VID_PLAY_MASTER_NONE 1 -#define VID_PLAY_MASTER_VIDEO 2 -#define VID_PLAY_MASTER_AUDIO 3 -#define VID_PLAY_ACTIVE_SCANLINES 12 - /* p1 = first active; p2 = last active */ -#define VID_PLAY_RESET 13 -#define VID_PLAY_END_MARK 14 - -#endif /* __LINUX_VIDEODEV_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From b287db119edb92548b53f63f05fd593c43cba200 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 09:25:02 -0300 Subject: [media] se401: remove last V4L1 driver This driver is for obsolete hardware that the old maintainer didn't care (or not have the hardware anymore), and that no other developer could find any hardware to buy. The V4L1 API is no longer supported, and since nobody stepped in to convert them to V4L2 the decision was made to remove them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 15 - drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/se401/Kconfig | 13 - drivers/staging/se401/Makefile | 1 - drivers/staging/se401/TODO | 5 - drivers/staging/se401/se401.c | 1492 ---------------------------- drivers/staging/se401/se401.h | 236 ----- drivers/staging/se401/videodev.h | 318 ------ 9 files changed, 2083 deletions(-) delete mode 100644 drivers/staging/se401/Kconfig delete mode 100644 drivers/staging/se401/Makefile delete mode 100644 drivers/staging/se401/TODO delete mode 100644 drivers/staging/se401/se401.c delete mode 100644 drivers/staging/se401/se401.h delete mode 100644 drivers/staging/se401/videodev.h (limited to 'drivers/staging') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index b3f35e5f9c95..08e0df12df37 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -97,21 +97,6 @@ Who: Pavel Machek --------------------------- -What: Video4Linux obsolete drivers using V4L1 API -When: kernel 2.6.39 -Files: drivers/staging/se401/* drivers/staging/usbvideo/* -Check: drivers/staging/se401/se401.c drivers/staging/usbvideo/usbvideo.c -Why: There are some drivers still using V4L1 API, despite all efforts we've done - to migrate. Those drivers are for obsolete hardware that the old maintainer - didn't care (or not have the hardware anymore), and that no other developer - could find any hardware to buy. They probably have no practical usage today, - and people with such old hardware could probably keep using an older version - of the kernel. Those drivers will be moved to staging on 2.6.38 and, if nobody - cares enough to port and test them with V4L2 API, they'll be removed on 2.6.39. -Who: Mauro Carvalho Chehab - ---------------------------- - What: Video4Linux: Remove obsolete ioctl's When: kernel 2.6.39 Files: include/media/videodev2.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a3f340bb6b59..fa5c007e74c8 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -55,8 +55,6 @@ source "drivers/staging/cxd2099/Kconfig" source "drivers/staging/dabusb/Kconfig" -source "drivers/staging/se401/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 532331ceb12d..2bdd703ed208 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ -obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/se401/Kconfig b/drivers/staging/se401/Kconfig deleted file mode 100644 index b7f8222ad21b..000000000000 --- a/drivers/staging/se401/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config USB_SE401 - tristate "USB SE401 Camera support (DEPRECATED)" - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB - ---help--- - Say Y here if you want to connect this type of camera to your - computer's USB port. See - for more information and for a list of supported cameras. - - This driver uses the deprecated V4L1 API and will be removed in - 2.6.39, unless someone converts it to the V4L2 API. - - To compile this driver as a module, choose M here: the - module will be called se401. diff --git a/drivers/staging/se401/Makefile b/drivers/staging/se401/Makefile deleted file mode 100644 index b465d49783af..000000000000 --- a/drivers/staging/se401/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_SE401) += se401.o diff --git a/drivers/staging/se401/TODO b/drivers/staging/se401/TODO deleted file mode 100644 index 3b2c03836286..000000000000 --- a/drivers/staging/se401/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is an obsolete driver for some old webcams that still use V4L1 API. -As V4L1 support is being removed from kernel, if nobody take care on it, -the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/se401/se401.c b/drivers/staging/se401/se401.c deleted file mode 100644 index 41360d7c3e96..000000000000 --- a/drivers/staging/se401/se401.c +++ /dev/null @@ -1,1492 +0,0 @@ -/* - * Endpoints (formerly known as AOX) se401 USB Camera Driver - * - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) - * - * Still somewhat based on the Linux ov511 driver. - * - * 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. - * - * - * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on - * their chipset available and supporting me while writing this driver. - * - Jeroen Vreeken - */ - -static const char version[] = "0.24"; - -#include -#include -#include -#include -#include -#include -#include "se401.h" - -static int flickerless; -static int video_nr = -1; - -static struct usb_device_id device_table[] = { - { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ - { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ - { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ - { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ - { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ - { } -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -MODULE_AUTHOR("Jeroen Vreeken "); -MODULE_DESCRIPTION("SE401 USB Camera Driver"); -MODULE_LICENSE("GPL"); -module_param(flickerless, int, 0); -MODULE_PARM_DESC(flickerless, - "Net frequency to adjust exposure time to (0/50/60)"); -module_param(video_nr, int, 0); - -static struct usb_driver se401_driver; - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - - - -/**************************************************************************** - * - * se401 register read/write functions - * - ***************************************************************************/ - -static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, - unsigned short value, unsigned char *cp, int size) -{ - return usb_control_msg( - se401->dev, - set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), - req, - (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - 0, - cp, - size, - 1000 - ); -} - -static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, - unsigned short param) -{ - /* specs say that the selector (address) should go in the value field - and the param in index, but in the logs of the windows driver they do - this the other way around... - */ - return usb_control_msg( - se401->dev, - usb_sndctrlpipe(se401->dev, 0), - SE401_REQ_SET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - param, - selector, - NULL, - 0, - 1000 - ); -} - -static unsigned short se401_get_feature(struct usb_se401 *se401, - unsigned short selector) -{ - /* For 'set' the selecetor should be in index, not sure if the spec is - wrong here to.... - */ - unsigned char cp[2]; - usb_control_msg( - se401->dev, - usb_rcvctrlpipe(se401->dev, 0), - SE401_REQ_GET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - selector, - cp, - 2, - 1000 - ); - return cp[0]+cp[1]*256; -} - -/**************************************************************************** - * - * Camera control - * - ***************************************************************************/ - - -static int se401_send_pict(struct usb_se401 *se401) -{ - /* integration time low */ - se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h); - /* reset level value */ - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - /* red color gain */ - se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain); - /* green color gain */ - se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain); - /* blue color gain */ - se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain); - - return 0; -} - -static void se401_set_exposure(struct usb_se401 *se401, int brightness) -{ - int integration = brightness << 5; - - if (flickerless == 50) - integration = integration-integration % 106667; - if (flickerless == 60) - integration = integration-integration % 88889; - se401->brightness = integration >> 5; - se401->expose_h = (integration >> 16) & 0xff; - se401->expose_m = (integration >> 8) & 0xff; - se401->expose_l = integration & 0xff; -} - -static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) -{ - p->brightness = se401->brightness; - if (se401->enhance) - p->whiteness = 32768; - else - p->whiteness = 0; - - p->colour = 65535; - p->contrast = 65535; - p->hue = se401->rgain << 10; - p->palette = se401->palette; - p->depth = 3; /* rgb24 */ - return 0; -} - - -static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) -{ - if (p->palette != VIDEO_PALETTE_RGB24) - return 1; - se401->palette = p->palette; - if (p->hue != se401->hue) { - se401->rgain = p->hue >> 10; - se401->bgain = 0x40-(p->hue >> 10); - se401->hue = p->hue; - } - if (p->brightness != se401->brightness) - se401_set_exposure(se401, p->brightness); - - if (p->whiteness >= 32768) - se401->enhance = 1; - else - se401->enhance = 0; - se401_send_pict(se401); - se401_send_pict(se401); - return 0; -} - -/* - Hyundai have some really nice docs about this and other sensor related - stuff on their homepage: www.hei.co.kr -*/ -static void se401_auto_resetlevel(struct usb_se401 *se401) -{ - unsigned int ahrc, alrc; - int oldreset = se401->resetlevel; - - /* For some reason this normally read-only register doesn't get reset - to zero after reading them just once... - */ - se401_get_feature(se401, HV7131_REG_HIREFNOH); - se401_get_feature(se401, HV7131_REG_HIREFNOL); - se401_get_feature(se401, HV7131_REG_LOREFNOH); - se401_get_feature(se401, HV7131_REG_LOREFNOL); - ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + - se401_get_feature(se401, HV7131_REG_HIREFNOL); - alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + - se401_get_feature(se401, HV7131_REG_LOREFNOL); - - /* Not an exact science, but it seems to work pretty well... */ - if (alrc > 10) { - while (alrc >= 10 && se401->resetlevel < 63) { - se401->resetlevel++; - alrc /= 2; - } - } else if (ahrc > 20) { - while (ahrc >= 20 && se401->resetlevel > 0) { - se401->resetlevel--; - ahrc /= 2; - } - } - if (se401->resetlevel != oldreset) - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - - return; -} - -/* irq handler for snapshot button */ -static void se401_button_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int status; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, urb->status); - goto exit; - } - - if (urb->actual_length >= 2) - if (se401->button) - se401->buttonpressed = 1; -exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - err("%s - usb_submit_urb failed with result %d", - __func__, status); -} - -static void se401_video_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int length = urb->actual_length; - - /* ohoh... */ - if (!se401->streaming) - return; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - /* 0 sized packets happen if we are to fast, but sometimes the camera - keeps sending them forever... - */ - if (length && !urb->status) { - se401->nullpackets = 0; - switch (se401->scratch[se401->scratch_next].state) { - case BUFFER_READY: - case BUFFER_BUSY: - se401->dropped++; - break; - case BUFFER_UNUSED: - memcpy(se401->scratch[se401->scratch_next].data, - (unsigned char *)urb->transfer_buffer, length); - se401->scratch[se401->scratch_next].state - = BUFFER_READY; - se401->scratch[se401->scratch_next].offset - = se401->bayeroffset; - se401->scratch[se401->scratch_next].length = length; - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - se401->scratch_overflow = 0; - se401->scratch_next++; - if (se401->scratch_next >= SE401_NUMSCRATCH) - se401->scratch_next = 0; - break; - } - se401->bayeroffset += length; - if (se401->bayeroffset >= se401->cheight * se401->cwidth) - se401->bayeroffset = 0; - } else { - se401->nullpackets++; - if (se401->nullpackets > SE401_MAX_NULLPACKETS) - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - } - - /* Resubmit urb for new data */ - urb->status = 0; - urb->dev = se401->dev; - if (usb_submit_urb(urb, GFP_KERNEL)) - dev_info(&urb->dev->dev, "urb burned down\n"); - return; -} - -static void se401_send_size(struct usb_se401 *se401, int width, int height) -{ - int i = 0; - int mode = 0x03; /* No compression */ - int sendheight = height; - int sendwidth = width; - - /* JangGu compression can only be used with the camera supported sizes, - but bayer seems to work with any size that fits on the sensor. - We check if we can use compression with the current size with either - 4 or 16 times subcapturing, if not we use uncompressed bayer data - but this will result in cutouts of the maximum size.... - */ - while (i < se401->sizes && !(se401->width[i] == width && - se401->height[i] == height)) - i++; - while (i < se401->sizes) { - if (se401->width[i] == width * 2 && - se401->height[i] == height * 2) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x40; - } - if (se401->width[i] == width * 4 && - se401->height[i] == height * 4) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x42; - } - i++; - } - - se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); - se401_set_feature(se401, SE401_OPERATINGMODE, mode); - - if (mode == 0x03) - se401->format = FMT_BAYER; - else - se401->format = FMT_JANGGU; -} - -/* - In this function se401_send_pict is called several times, - for some reason (depending on the state of the sensor and the phase of - the moon :) doing this only in either place doesn't always work... -*/ -static int se401_start_stream(struct usb_se401 *se401) -{ - struct urb *urb; - int err = 0, i; - se401->streaming = 1; - - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* Set picture settings */ - /* windowed + pix intg */ - se401_set_feature(se401, HV7131_REG_MODE_B, 0x05); - se401_send_pict(se401); - - se401_send_size(se401, se401->cwidth, se401->cheight); - - se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, - 0, NULL, 0); - - /* Do some memory allocation */ - for (i = 0; i < SE401_NUMFRAMES; i++) { - se401->frame[i].data = se401->fbuf + i * se401->maxframesize; - se401->frame[i].curpix = 0; - } - for (i = 0; i < SE401_NUMSBUF; i++) { - se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->sbuf[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; - } - } - - se401->bayeroffset = 0; - se401->scratch_next = 0; - se401->scratch_use = 0; - se401->scratch_overflow = 0; - for (i = 0; i < SE401_NUMSCRATCH; i++) { - se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->scratch[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - goto nomem_sbuf; - } - se401->scratch[i].state = BUFFER_UNUSED; - } - - for (i = 0; i < SE401_NUMSBUF; i++) { - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - for (i = i - 1; i >= 0; i--) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - } - goto nomem_scratch; - } - - usb_fill_bulk_urb(urb, se401->dev, - usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), - se401->sbuf[i].data, SE401_PACKETSIZE, - se401_video_irq, - se401); - - se401->urb[i] = urb; - - err = usb_submit_urb(se401->urb[i], GFP_KERNEL); - if (err) - err("urb burned down"); - } - - se401->framecount = 0; - - return 0; - - nomem_scratch: - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - nomem_sbuf: - for (i = 0; i < SE401_NUMSBUF; i++) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; -} - -static int se401_stop_stream(struct usb_se401 *se401) -{ - int i; - - if (!se401->streaming || !se401->dev) - return 1; - - se401->streaming = 0; - - se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); - - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - - return 0; -} - -static int se401_set_size(struct usb_se401 *se401, int width, int height) -{ - int wasstreaming = se401->streaming; - /* Check to see if we need to change */ - if (se401->cwidth == width && se401->cheight == height) - return 0; - - /* Check for a valid mode */ - if (!width || !height) - return 1; - if ((width & 1) || (height & 1)) - return 1; - if (width > se401->width[se401->sizes-1]) - return 1; - if (height > se401->height[se401->sizes-1]) - return 1; - - /* Stop a current stream and start it again at the new size */ - if (wasstreaming) - se401_stop_stream(se401); - se401->cwidth = width; - se401->cheight = height; - if (wasstreaming) - se401_start_stream(se401); - return 0; -} - - -/**************************************************************************** - * - * Video Decoding - * - ***************************************************************************/ - -/* - This shouldn't really be done in a v4l driver.... - But it does make the image look a lot more usable. - Basically it lifts the dark pixels more than the light pixels. -*/ -static inline void enhance_picture(unsigned char *frame, int len) -{ - while (len--) { - *frame = (((*frame^255)*(*frame^255))/255)^255; - frame++; - } -} - -static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) -{ - struct se401_frame *frame = &se401->frame[se401->curframe]; - int linelength = se401->cwidth * 3; - - if (frame->curlinepix >= linelength) { - frame->curlinepix = 0; - frame->curline += linelength; - } - - /* First three are absolute, all others relative. - * Format is rgb from right to left (mirrorred image), - * we flip it to get bgr from left to right. */ - if (frame->curlinepix < 3) - *(frame->curline-frame->curlinepix) = 1 + data * 4; - else - *(frame->curline-frame->curlinepix) = - *(frame->curline-frame->curlinepix + 3) + data * 4; - frame->curlinepix++; -} - -static inline void decode_JangGu_vlc(struct usb_se401 *se401, - unsigned char *data, int bit_exp, int packetlength) -{ - int pos = 0; - int vlc_cod = 0; - int vlc_size = 0; - int vlc_data = 0; - int bit_cur; - int bit; - data += 4; - while (pos < packetlength) { - bit_cur = 8; - while (bit_cur && bit_exp) { - bit = ((*data) >> (bit_cur-1))&1; - if (!vlc_cod) { - if (bit) { - vlc_size++; - } else { - if (!vlc_size) - decode_JangGu_integrate(se401, 0); - else { - vlc_cod = 2; - vlc_data = 0; - } - } - } else { - if (vlc_cod == 2) { - if (!bit) - vlc_data = -(1 << vlc_size) + 1; - vlc_cod--; - } - vlc_size--; - vlc_data += bit << vlc_size; - if (!vlc_size) { - decode_JangGu_integrate(se401, vlc_data); - vlc_cod = 0; - } - } - bit_cur--; - bit_exp--; - } - pos++; - data++; - } -} - -static inline void decode_JangGu(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size; - int datapos = 0; - - /* New image? */ - if (!se401->frame[se401->curframe].curpix) { - se401->frame[se401->curframe].curlinepix = 0; - se401->frame[se401->curframe].curline = - se401->frame[se401->curframe].data+ - se401->cwidth * 3 - 1; - if (se401->frame[se401->curframe].grabstate == FRAME_READY) - se401->frame[se401->curframe].grabstate = FRAME_GRABBING; - se401->vlcdatapos = 0; - } - while (datapos < len) { - size = 1024 - se401->vlcdatapos; - if (size+datapos > len) - size = len-datapos; - memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); - se401->vlcdatapos += size; - packetlength = 0; - if (se401->vlcdatapos >= 4) { - bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8); - pix_exp = se401->vlcdata[1] + - ((se401->vlcdata[0] & 0x3f) << 8); - frameinfo = se401->vlcdata[0] & 0xc0; - packetlength = ((bit_exp + 47) >> 4) << 1; - if (packetlength > 1024) { - se401->vlcdatapos = 0; - datapos = len; - packetlength = 0; - se401->error++; - se401->frame[se401->curframe].curpix = 0; - } - } - if (packetlength && se401->vlcdatapos >= packetlength) { - decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, - packetlength); - se401->frame[se401->curframe].curpix += pix_exp * 3; - datapos += size-(se401->vlcdatapos-packetlength); - se401->vlcdatapos = 0; - if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) { - se401->frame[se401->curframe].grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - } - if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) - se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1); - } else - se401->error++; - se401->frame[se401->curframe].curpix = 0; - datapos = len; - } - } else - datapos += size; - } -} - -static inline void decode_bayer(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int offset = buffer->offset; - int datasize = se401->cwidth * se401->cheight; - struct se401_frame *frame = &se401->frame[se401->curframe]; - unsigned char *framedata = frame->data, *curline, *nextline; - int width = se401->cwidth; - int blineoffset = 0, bline; - int linelength = width * 3, i; - - - if (frame->curpix == 0) { - if (frame->grabstate == FRAME_READY) - frame->grabstate = FRAME_GRABBING; - - frame->curline = framedata + linelength; - frame->curlinepix = 0; - } - - if (offset != frame->curpix) { - /* Regard frame as lost :( */ - frame->curpix = 0; - se401->error++; - return; - } - - /* Check if we have to much data */ - if (frame->curpix + len > datasize) - len = datasize-frame->curpix; - - if (se401->cheight % 4) - blineoffset = 1; - bline = frame->curpix / se401->cwidth+blineoffset; - - curline = frame->curline; - nextline = curline + linelength; - if (nextline >= framedata+datasize * 3) - nextline = curline; - while (len) { - if (frame->curlinepix >= width) { - frame->curlinepix -= width; - bline = frame->curpix / width + blineoffset; - curline += linelength*2; - nextline += linelength*2; - if (curline >= framedata+datasize * 3) { - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - if (nextline >= framedata+datasize*3) - nextline = curline; - } - if (bline & 1) { - if (frame->curlinepix & 1) { - *(curline + 2) = *data; - *(curline - 1) = *data; - *(nextline + 2) = *data; - *(nextline - 1) = *data; - } else { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline-2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } - } else { - if (frame->curlinepix & 1) { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline - 2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } else { - *curline = *data; - *(curline - 3) = *data; - *nextline = *data; - *(nextline - 3) = *data; - } - } - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - frame->curline = curline; - - if (frame->curpix >= datasize) { - /* Fix the top line */ - framedata += linelength; - for (i = 0; i < linelength; i++) { - framedata--; - *framedata = *(framedata + linelength); - } - /* Fix the left side (green is already present) */ - for (i = 0; i < se401->cheight; i++) { - *framedata = *(framedata + 3); - *(framedata + 1) = *(framedata + 4); - *(framedata + 2) = *(framedata + 5); - framedata += linelength; - } - frame->curpix = 0; - frame->grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - if (se401->frame[(se401->curframe + 1) & - (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) { - se401->curframe = (se401->curframe+1) & - (SE401_NUMFRAMES-1); - } - } -} - -static int se401_newframe(struct usb_se401 *se401, int framenr) -{ - DECLARE_WAITQUEUE(wait, current); - int errors = 0; - - while (se401->streaming && - (se401->frame[framenr].grabstate == FRAME_READY || - se401->frame[framenr].grabstate == FRAME_GRABBING)) { - if (!se401->frame[framenr].curpix) - errors++; - - wait_interruptible( - se401->scratch[se401->scratch_use].state != BUFFER_READY, - &se401->wq, &wait); - if (se401->nullpackets > SE401_MAX_NULLPACKETS) { - se401->nullpackets = 0; - dev_info(&se401->dev->dev, - "too many null length packets, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } else { - if (se401->scratch[se401->scratch_use].state != - BUFFER_READY) { - se401->frame[framenr].grabstate = FRAME_ERROR; - return -EIO; - } - se401->scratch[se401->scratch_use].state = BUFFER_BUSY; - if (se401->format == FMT_JANGGU) - decode_JangGu(se401, - &se401->scratch[se401->scratch_use]); - else - decode_bayer(se401, - &se401->scratch[se401->scratch_use]); - - se401->scratch[se401->scratch_use].state = - BUFFER_UNUSED; - se401->scratch_use++; - if (se401->scratch_use >= SE401_NUMSCRATCH) - se401->scratch_use = 0; - if (errors > SE401_MAX_ERRORS) { - errors = 0; - dev_info(&se401->dev->dev, - "too many errors, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } - } - } - - if (se401->frame[framenr].grabstate == FRAME_DONE) - if (se401->enhance) - enhance_picture(se401->frame[framenr].data, - se401->cheight * se401->cwidth * 3); - return 0; -} - -static void usb_se401_remove_disconnected(struct usb_se401 *se401) -{ - int i; - - se401->dev = NULL; - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - - for (i = 0; i < SE401_NUMSCRATCH; i++) - kfree(se401->scratch[i].data); - - if (se401->inturb) { - usb_kill_urb(se401->inturb); - usb_free_urb(se401->inturb); - } - dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name); - - /* Free the memory */ - kfree(se401->width); - kfree(se401->height); - kfree(se401); -} - - - -/**************************************************************************** - * - * Video4Linux - * - ***************************************************************************/ - - -static int se401_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int err = 0; - - mutex_lock(&se401->lock); - if (se401->user) { - mutex_unlock(&se401->lock); - return -EBUSY; - } - se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); - if (se401->fbuf) - file->private_data = dev; - else - err = -ENOMEM; - se401->user = !err; - mutex_unlock(&se401->lock); - - return err; -} - -static int se401_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int i; - - rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); - if (se401->removed) { - dev_info(&se401->dev->dev, "device unregistered\n"); - usb_se401_remove_disconnected(se401); - } else { - for (i = 0; i < SE401_NUMFRAMES; i++) - se401->frame[i].grabstate = FRAME_UNUSED; - if (se401->streaming) - se401_stop_stream(se401); - se401->user = 0; - } - file->private_data = NULL; - return 0; -} - -static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)vdev; - - if (!se401->dev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, se401->camera_name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = se401->width[se401->sizes-1]; - b->maxheight = se401->height[se401->sizes-1]; - b->minwidth = se401->width[0]; - b->minheight = se401->height[0]; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - - se401_get_pict(se401, p); - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - - if (se401_set_pict(se401, p)) - return -EINVAL; - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (se401_set_size(se401, vw->width, vw->height)) - return -EINVAL; - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; /* FIXME */ - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = se401->cwidth; - vw->height = se401->cheight; - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = SE401_NUMFRAMES * se401->maxframesize; - vm->frames = SE401_NUMFRAMES; - for (i = 0; i < SE401_NUMFRAMES; i++) - vm->offsets[i] = se401->maxframesize * i; - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (vm->format != VIDEO_PALETTE_RGB24) - return -EINVAL; - if (vm->frame >= SE401_NUMFRAMES) - return -EINVAL; - if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) - return -EBUSY; - - /* Is this according to the v4l spec??? */ - if (se401_set_size(se401, vm->width, vm->height)) - return -EINVAL; - se401->frame[vm->frame].grabstate = FRAME_READY; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount % 20 == 1) - se401_auto_resetlevel(se401); - - return 0; - } - case VIDIOCSYNC: - { - int *frame = arg; - int ret = 0; - - if (*frame < 0 || *frame >= SE401_NUMFRAMES) - return -EINVAL; - - ret = se401_newframe(se401, *frame); - se401->frame[*frame].grabstate = FRAME_UNUSED; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - return -EINVAL; - case VIDIOCSFBUF: - return -EINVAL; - case VIDIOCGTUNER: - case VIDIOCSTUNER: - return -EINVAL; - case VIDIOCGFREQ: - case VIDIOCSFREQ: - return -EINVAL; - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; - } /* end switch */ - - return 0; -} - -static long se401_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, se401_do_ioctl); -} - -static ssize_t se401_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int realcount = count, ret = 0; - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - - - if (se401->dev == NULL) - return -EIO; - if (realcount > se401->cwidth*se401->cheight*3) - realcount = se401->cwidth*se401->cheight*3; - - /* Shouldn't happen: */ - if (se401->frame[0].grabstate == FRAME_GRABBING) - return -EBUSY; - se401->frame[0].grabstate = FRAME_READY; - se401->frame[1].grabstate = FRAME_UNUSED; - se401->curframe = 0; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount%20 == 1) - se401_auto_resetlevel(se401); - - ret = se401_newframe(se401, 0); - - se401->frame[0].grabstate = FRAME_UNUSED; - if (ret) - return ret; - if (copy_to_user(buf, se401->frame[0].data, realcount)) - return -EFAULT; - - return realcount; -} - -static int se401_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - mutex_lock(&se401->lock); - - if (se401->dev == NULL) { - mutex_unlock(&se401->lock); - return -EIO; - } - if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1))) { - mutex_unlock(&se401->lock); - return -EINVAL; - } - pos = (unsigned long)se401->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&se401->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - mutex_unlock(&se401->lock); - - return 0; -} - -static const struct v4l2_file_operations se401_fops = { - .owner = THIS_MODULE, - .open = se401_open, - .release = se401_close, - .read = se401_read, - .mmap = se401_mmap, - .ioctl = se401_ioctl, -}; -static struct video_device se401_template = { - .name = "se401 USB camera", - .fops = &se401_fops, - .release = video_device_release_empty, -}; - - - -/***************************/ -static int se401_init(struct usb_se401 *se401, int button) -{ - int i = 0, rc; - unsigned char cp[0x40]; - char temp[200]; - int slen; - - /* led on */ - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* get camera descriptor */ - rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, - cp, sizeof(cp)); - if (cp[1] != 0x41) { - err("Wrong descriptor type"); - return 1; - } - slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]); - - se401->sizes = cp[4] + cp[5] * 256; - se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->width) - return 1; - se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->height) { - kfree(se401->width); - return 1; - } - for (i = 0; i < se401->sizes; i++) { - se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256; - se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256; - } - slen += snprintf(temp + slen, 200 - slen, " Sizes:"); - for (i = 0; i < se401->sizes; i++) { - slen += snprintf(temp + slen, 200 - slen, - " %dx%d", se401->width[i], se401->height[i]); - } - dev_info(&se401->dev->dev, "%s\n", temp); - se401->maxframesize = se401->width[se401->sizes-1] * - se401->height[se401->sizes - 1] * 3; - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); - se401->cwidth = cp[0]+cp[1]*256; - rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); - se401->cheight = cp[0]+cp[1]*256; - - if (!(cp[2] & SE401_FORMAT_BAYER)) { - err("Bayer format not supported!"); - return 1; - } - /* set output mode (BAYER) */ - se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, - SE401_FORMAT_BAYER, NULL, 0); - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); - se401->brightness = cp[0]+cp[1]*256; - /* some default values */ - se401->resetlevel = 0x2d; - se401->rgain = 0x20; - se401->ggain = 0x20; - se401->bgain = 0x20; - se401_set_exposure(se401, 20000); - se401->palette = VIDEO_PALETTE_RGB24; - se401->enhance = 1; - se401->dropped = 0; - se401->error = 0; - se401->framecount = 0; - se401->readcount = 0; - - /* Start interrupt transfers for snapshot button */ - if (button) { - se401->inturb = usb_alloc_urb(0, GFP_KERNEL); - if (!se401->inturb) { - dev_info(&se401->dev->dev, - "Allocation of inturb failed\n"); - return 1; - } - usb_fill_int_urb(se401->inturb, se401->dev, - usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), - &se401->button, sizeof(se401->button), - se401_button_irq, - se401, - 8 - ); - if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { - dev_info(&se401->dev->dev, "int urb burned down\n"); - return 1; - } - } else - se401->inturb = NULL; - - /* Flash the led */ - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - - return 0; -} - -static int se401_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_interface_descriptor *interface; - struct usb_se401 *se401; - char *camera_name = NULL; - int button = 1; - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = &intf->cur_altsetting->desc; - - /* Is it an se401? */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && - le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { - camera_name = "Endpoints/Aox SE401"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && - le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { - camera_name = "Philips PCVC665K"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { - camera_name = "Kensington VideoCAM 67014"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { - camera_name = "Kensington VideoCAM 6701(5/7)"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { - camera_name = "Kensington VideoCAM 67016"; - button = 0; - } else - return -ENODEV; - - /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0x00) - return -ENODEV; - if (interface->bInterfaceSubClass != 0x00) - return -ENODEV; - - /* We found one */ - dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name); - - se401 = kzalloc(sizeof(*se401), GFP_KERNEL); - if (se401 == NULL) { - err("couldn't kmalloc se401 struct"); - return -ENOMEM; - } - - se401->dev = dev; - se401->iface = interface->bInterfaceNumber; - se401->camera_name = camera_name; - - dev_info(&intf->dev, "firmware version: %02x\n", - le16_to_cpu(dev->descriptor.bcdDevice) & 255); - - if (se401_init(se401, button)) { - kfree(se401); - return -EIO; - } - - memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); - memcpy(se401->vdev.name, se401->camera_name, - strlen(se401->camera_name)); - init_waitqueue_head(&se401->wq); - mutex_init(&se401->lock); - wmb(); - - if (video_register_device(&se401->vdev, - VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(se401); - err("video_register_device failed"); - return -EIO; - } - dev_info(&intf->dev, "registered new video device: %s\n", - video_device_node_name(&se401->vdev)); - - usb_set_intfdata(intf, se401); - return 0; -} - -static void se401_disconnect(struct usb_interface *intf) -{ - struct usb_se401 *se401 = usb_get_intfdata(intf); - - usb_set_intfdata(intf, NULL); - if (se401) { - video_unregister_device(&se401->vdev); - if (!se401->user) - usb_se401_remove_disconnected(se401); - else { - se401->frame[0].grabstate = FRAME_ERROR; - se401->frame[0].grabstate = FRAME_ERROR; - - se401->streaming = 0; - - wake_up_interruptible(&se401->wq); - se401->removed = 1; - } - } -} - -static struct usb_driver se401_driver = { - .name = "se401", - .id_table = device_table, - .probe = se401_probe, - .disconnect = se401_disconnect, -}; - - - -/**************************************************************************** - * - * Module routines - * - ***************************************************************************/ - -static int __init usb_se401_init(void) -{ - printk(KERN_INFO "SE401 usb camera driver version %s registering\n", - version); - if (flickerless) - if (flickerless != 50 && flickerless != 60) { - printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n"); - return -1; - } - return usb_register(&se401_driver); -} - -static void __exit usb_se401_exit(void) -{ - usb_deregister(&se401_driver); - printk(KERN_INFO "SE401 driver deregistered\frame"); -} - -module_init(usb_se401_init); -module_exit(usb_se401_exit); diff --git a/drivers/staging/se401/se401.h b/drivers/staging/se401/se401.h deleted file mode 100644 index 2758f4716c3d..000000000000 --- a/drivers/staging/se401/se401.h +++ /dev/null @@ -1,236 +0,0 @@ - -#ifndef __LINUX_se401_H -#define __LINUX_se401_H - -#include -#include "videodev.h" -#include -#include -#include - -#define se401_DEBUG /* Turn on debug messages */ - -#ifdef se401_DEBUG -# define PDEBUG(level, fmt, args...) \ -if (debug >= level) \ - info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) -#else -# define PDEBUG(level, fmt, args...) do {} while (0) -#endif - -/* An almost drop-in replacement for sleep_on_interruptible */ -#define wait_interruptible(test, queue, wait) \ -{ \ - add_wait_queue(queue, wait); \ - set_current_state(TASK_INTERRUPTIBLE); \ - if (test) \ - schedule(); \ - remove_wait_queue(queue, wait); \ - set_current_state(TASK_RUNNING); \ - if (signal_pending(current)) \ - break; \ -} - -#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 -#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 -#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 -#define SE401_REQ_CAPTURE_FRAME 0x43 -#define SE401_REQ_GET_BRT 0x44 -#define SE401_REQ_SET_BRT 0x45 -#define SE401_REQ_GET_WIDTH 0x4c -#define SE401_REQ_SET_WIDTH 0x4d -#define SE401_REQ_GET_HEIGHT 0x4e -#define SE401_REQ_SET_HEIGHT 0x4f -#define SE401_REQ_GET_OUTPUT_MODE 0x50 -#define SE401_REQ_SET_OUTPUT_MODE 0x51 -#define SE401_REQ_GET_EXT_FEATURE 0x52 -#define SE401_REQ_SET_EXT_FEATURE 0x53 -#define SE401_REQ_CAMERA_POWER 0x56 -#define SE401_REQ_LED_CONTROL 0x57 -#define SE401_REQ_BIOS 0xff - -#define SE401_BIOS_READ 0x07 - -#define SE401_FORMAT_BAYER 0x40 - -/* Hyundai hv7131b registers - 7121 and 7141 should be the same (haven't really checked...) */ -/* Mode registers: */ -#define HV7131_REG_MODE_A 0x00 -#define HV7131_REG_MODE_B 0x01 -#define HV7131_REG_MODE_C 0x02 -/* Frame registers: */ -#define HV7131_REG_FRSU 0x10 -#define HV7131_REG_FRSL 0x11 -#define HV7131_REG_FCSU 0x12 -#define HV7131_REG_FCSL 0x13 -#define HV7131_REG_FWHU 0x14 -#define HV7131_REG_FWHL 0x15 -#define HV7131_REG_FWWU 0x16 -#define HV7131_REG_FWWL 0x17 -/* Timing registers: */ -#define HV7131_REG_THBU 0x20 -#define HV7131_REG_THBL 0x21 -#define HV7131_REG_TVBU 0x22 -#define HV7131_REG_TVBL 0x23 -#define HV7131_REG_TITU 0x25 -#define HV7131_REG_TITM 0x26 -#define HV7131_REG_TITL 0x27 -#define HV7131_REG_TMCD 0x28 -/* Adjust Registers: */ -#define HV7131_REG_ARLV 0x30 -#define HV7131_REG_ARCG 0x31 -#define HV7131_REG_AGCG 0x32 -#define HV7131_REG_ABCG 0x33 -#define HV7131_REG_APBV 0x34 -#define HV7131_REG_ASLP 0x54 -/* Offset Registers: */ -#define HV7131_REG_OFSR 0x50 -#define HV7131_REG_OFSG 0x51 -#define HV7131_REG_OFSB 0x52 -/* REset level statistics registers: */ -#define HV7131_REG_LOREFNOH 0x57 -#define HV7131_REG_LOREFNOL 0x58 -#define HV7131_REG_HIREFNOH 0x59 -#define HV7131_REG_HIREFNOL 0x5a - -/* se401 registers */ -#define SE401_OPERATINGMODE 0x2000 - - -/* size of usb transfers */ -#define SE401_PACKETSIZE 4096 -/* number of queued bulk transfers to use, should be about 8 */ -#define SE401_NUMSBUF 1 -/* read the usb specs for this one :) */ -#define SE401_VIDEO_ENDPOINT 1 -#define SE401_BUTTON_ENDPOINT 2 -/* number of frames supported by the v4l part */ -#define SE401_NUMFRAMES 2 -/* scratch buffers for passing data to the decoders */ -#define SE401_NUMSCRATCH 32 -/* maximum amount of data in a JangGu packet */ -#define SE401_VLCDATALEN 1024 -/* number of nul sized packets to receive before kicking the camera */ -#define SE401_MAX_NULLPACKETS 4000 -/* number of decoding errors before kicking the camera */ -#define SE401_MAX_ERRORS 200 - -struct usb_device; - -struct se401_sbuf { - unsigned char *data; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -enum { - FMT_BAYER, - FMT_JANGGU, -}; - -enum { - BUFFER_UNUSED, - BUFFER_READY, - BUFFER_BUSY, - BUFFER_DONE, -}; - -struct se401_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -struct se401_frame { - unsigned char *data; /* Frame buffer */ - - volatile int grabstate; /* State of grabbing */ - - unsigned char *curline; - int curlinepix; - int curpix; -}; - -struct usb_se401 { - struct video_device vdev; - - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - - char *camera_name; - - int change; - int brightness; - int hue; - int rgain; - int ggain; - int bgain; - int expose_h; - int expose_m; - int expose_l; - int resetlevel; - - int enhance; - - int format; - int sizes; - int *width; - int *height; - int cwidth; /* current width */ - int cheight; /* current height */ - int palette; - int maxframesize; - int cframesize; /* current framesize */ - - struct mutex lock; - int user; /* user count for exclusive use */ - int removed; /* device disconnected */ - - int streaming; /* Are we streaming video? */ - - char *fbuf; /* Videodev buffer area */ - - struct urb *urb[SE401_NUMSBUF]; - struct urb *inturb; - - int button; - int buttonpressed; - - int curframe; /* Current receiving frame */ - struct se401_frame frame[SE401_NUMFRAMES]; - int readcount; - int framecount; - int error; - int dropped; - - int scratch_next; - int scratch_use; - int scratch_overflow; - struct se401_scratch scratch[SE401_NUMSCRATCH]; - - /* Decoder specific data: */ - unsigned char vlcdata[SE401_VLCDATALEN]; - int vlcdatapos; - int bayeroffset; - - struct se401_sbuf sbuf[SE401_NUMSBUF]; - - wait_queue_head_t wq; /* Processes waiting */ - - int nullpackets; -}; - - - -#endif - diff --git a/drivers/staging/se401/videodev.h b/drivers/staging/se401/videodev.h deleted file mode 100644 index f11efbef1c05..000000000000 --- a/drivers/staging/se401/videodev.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Video for Linux version 1 - OBSOLETE - * - * Header file for v4l1 drivers and applications, for - * Linux kernels 2.2.x or 2.4.x. - * - * Provides header for legacy drivers and applications - * - * See http://linuxtv.org for more info - * - */ -#ifndef __LINUX_VIDEODEV_H -#define __LINUX_VIDEODEV_H - -#include -#include -#include - -#define VID_TYPE_CAPTURE 1 /* Can capture */ -#define VID_TYPE_TUNER 2 /* Can tune */ -#define VID_TYPE_TELETEXT 4 /* Does teletext */ -#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ -#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ -#define VID_TYPE_CLIPPING 32 /* Can clip */ -#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ -#define VID_TYPE_SCALES 128 /* Scalable */ -#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ -#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ -#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ -#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ -#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ -#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ - -struct video_capability -{ - char name[32]; - int type; - int channels; /* Num channels */ - int audios; /* Num audio devices */ - int maxwidth; /* Supported width */ - int maxheight; /* And height */ - int minwidth; /* Supported width */ - int minheight; /* And height */ -}; - - -struct video_channel -{ - int channel; - char name[32]; - int tuners; - __u32 flags; -#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ -#define VIDEO_VC_AUDIO 2 /* Channel has audio */ - __u16 type; -#define VIDEO_TYPE_TV 1 -#define VIDEO_TYPE_CAMERA 2 - __u16 norm; /* Norm set by channel */ -}; - -struct video_tuner -{ - int tuner; - char name[32]; - unsigned long rangelow, rangehigh; /* Tuner range */ - __u32 flags; -#define VIDEO_TUNER_PAL 1 -#define VIDEO_TUNER_NTSC 2 -#define VIDEO_TUNER_SECAM 4 -#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ -#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ -#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ -#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ -#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ - __u16 mode; /* PAL/NTSC/SECAM/OTHER */ -#define VIDEO_MODE_PAL 0 -#define VIDEO_MODE_NTSC 1 -#define VIDEO_MODE_SECAM 2 -#define VIDEO_MODE_AUTO 3 - __u16 signal; /* Signal strength 16bit scale */ -}; - -struct video_picture -{ - __u16 brightness; - __u16 hue; - __u16 colour; - __u16 contrast; - __u16 whiteness; /* Black and white only */ - __u16 depth; /* Capture depth */ - __u16 palette; /* Palette in use */ -#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ -#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ -#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ -#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ -#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ -#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ -#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ -#define VIDEO_PALETTE_YUYV 8 -#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ -#define VIDEO_PALETTE_YUV420 10 -#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ -#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ -#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ -#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ -#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ -#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ -#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ -#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ -}; - -struct video_audio -{ - int audio; /* Audio channel */ - __u16 volume; /* If settable */ - __u16 bass, treble; - __u32 flags; -#define VIDEO_AUDIO_MUTE 1 -#define VIDEO_AUDIO_MUTABLE 2 -#define VIDEO_AUDIO_VOLUME 4 -#define VIDEO_AUDIO_BASS 8 -#define VIDEO_AUDIO_TREBLE 16 -#define VIDEO_AUDIO_BALANCE 32 - char name[16]; -#define VIDEO_SOUND_MONO 1 -#define VIDEO_SOUND_STEREO 2 -#define VIDEO_SOUND_LANG1 4 -#define VIDEO_SOUND_LANG2 8 - __u16 mode; - __u16 balance; /* Stereo balance */ - __u16 step; /* Step actual volume uses */ -}; - -struct video_clip -{ - __s32 x,y; - __s32 width, height; - struct video_clip *next; /* For user use/driver use only */ -}; - -struct video_window -{ - __u32 x,y; /* Position of window */ - __u32 width,height; /* Its size */ - __u32 chromakey; - __u32 flags; - struct video_clip __user *clips; /* Set only */ - int clipcount; -#define VIDEO_WINDOW_INTERLACE 1 -#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ -#define VIDEO_CLIP_BITMAP -1 -/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ -#define VIDEO_CLIPMAP_SIZE (128 * 625) -}; - -struct video_capture -{ - __u32 x,y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ -#define VIDEO_CAPTURE_ODD 0 /* Temporal */ -#define VIDEO_CAPTURE_EVEN 1 -}; - -struct video_buffer -{ - void *base; - int height,width; - int depth; - int bytesperline; -}; - -struct video_mmap -{ - unsigned int frame; /* Frame (0 - n) for double buffer */ - int height,width; - unsigned int format; /* should be VIDEO_PALETTE_* */ -}; - -struct video_key -{ - __u8 key[8]; - __u32 flags; -}; - -struct video_mbuf -{ - int size; /* Total memory to map */ - int frames; /* Frames */ - int offsets[VIDEO_MAX_FRAME]; -}; - -#define VIDEO_NO_UNIT (-1) - -struct video_unit -{ - int video; /* Video minor */ - int vbi; /* VBI minor */ - int radio; /* Radio minor */ - int audio; /* Audio minor */ - int teletext; /* Teletext minor */ -}; - -struct vbi_format { - __u32 sampling_rate; /* in Hz */ - __u32 samples_per_line; - __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ - __s32 start[2]; /* starting line for each frame */ - __u32 count[2]; /* count of lines for each frame */ - __u32 flags; -#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ -#define VBI_INTERLACED 2 /* lines are interlaced */ -}; - -/* video_info is biased towards hardware mpeg encode/decode */ -/* but it could apply generically to any hardware compressor/decompressor */ -struct video_info -{ - __u32 frame_count; /* frames output since decode/encode began */ - __u32 h_size; /* current unscaled horizontal size */ - __u32 v_size; /* current unscaled veritcal size */ - __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ - __u32 picture_type; /* current picture type */ - __u32 temporal_reference; /* current temporal reference */ - __u8 user_data[256]; /* user data last found in compressed stream */ - /* user_data[0] contains user data flags, user_data[1] has count */ -}; - -/* generic structure for setting playback modes */ -struct video_play_mode -{ - int mode; - int p1; - int p2; -}; - -/* for loading microcode / fpga programming */ -struct video_code -{ - char loadwhat[16]; /* name or tag of file being passed */ - int datasize; - __u8 *data; -}; - -#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ -#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ -#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ -#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ -#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ -#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ -#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ -#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ -#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ -#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ -#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ -#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ -#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ -#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ -#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ -#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ -#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ -#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ -#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ -#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ -#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ -#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ -#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ -#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ -#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ -#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ -#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ -#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ -#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ - - -#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ - -/* VIDIOCSWRITEMODE */ -#define VID_WRITE_MPEG_AUD 0 -#define VID_WRITE_MPEG_VID 1 -#define VID_WRITE_OSD 2 -#define VID_WRITE_TTX 3 -#define VID_WRITE_CC 4 -#define VID_WRITE_MJPEG 5 - -/* VIDIOCSPLAYMODE */ -#define VID_PLAY_VID_OUT_MODE 0 - /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ -#define VID_PLAY_GENLOCK 1 - /* p1: 0 = OFF, 1 = ON */ - /* p2: GENLOCK FINE DELAY value */ -#define VID_PLAY_NORMAL 2 -#define VID_PLAY_PAUSE 3 -#define VID_PLAY_SINGLE_FRAME 4 -#define VID_PLAY_FAST_FORWARD 5 -#define VID_PLAY_SLOW_MOTION 6 -#define VID_PLAY_IMMEDIATE_NORMAL 7 -#define VID_PLAY_SWITCH_CHANNELS 8 -#define VID_PLAY_FREEZE_FRAME 9 -#define VID_PLAY_STILL_MODE 10 -#define VID_PLAY_MASTER_MODE 11 - /* p1: see below */ -#define VID_PLAY_MASTER_NONE 1 -#define VID_PLAY_MASTER_VIDEO 2 -#define VID_PLAY_MASTER_AUDIO 3 -#define VID_PLAY_ACTIVE_SCANLINES 12 - /* p1 = first active; p2 = last active */ -#define VID_PLAY_RESET 13 -#define VID_PLAY_END_MARK 14 - -#endif /* __LINUX_VIDEODEV_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From dae86ccbc3c185aebfc396e8e668aa3d73d748d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 09:29:31 -0300 Subject: [media] dabusb: remove obsolete driver The dabusb driver was conceived as an experimental driver for a test device. The driver never supported any shipped product, and, while there were some updates for it in 2003, for an ancient product, those changes were never submitted upstream. Also, there's no DocBook for its API, nor any upstream discussion. So it was decided to remove it in 2.6.39. Future support for a DAB API should be discussed on the linux-media list first. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/dabusb/Kconfig | 14 - drivers/staging/dabusb/Makefile | 2 - drivers/staging/dabusb/TODO | 5 - drivers/staging/dabusb/dabusb.c | 914 ---------------------------------------- drivers/staging/dabusb/dabusb.h | 85 ---- 7 files changed, 1023 deletions(-) delete mode 100644 drivers/staging/dabusb/Kconfig delete mode 100644 drivers/staging/dabusb/Makefile delete mode 100644 drivers/staging/dabusb/TODO delete mode 100644 drivers/staging/dabusb/dabusb.c delete mode 100644 drivers/staging/dabusb/dabusb.h (limited to 'drivers/staging') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index fa5c007e74c8..a2742d8102c1 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -53,8 +53,6 @@ source "drivers/staging/tm6000/Kconfig" source "drivers/staging/cxd2099/Kconfig" -source "drivers/staging/dabusb/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 2bdd703ed208..4ddd3ce780e0 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ -obj-$(CONFIG_USB_DABUSB) += dabusb/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/dabusb/Kconfig b/drivers/staging/dabusb/Kconfig deleted file mode 100644 index 87bdc425d3c5..000000000000 --- a/drivers/staging/dabusb/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config USB_DABUSB - tristate "DABUSB driver" - depends on USB - ---help--- - A Digital Audio Broadcasting (DAB) Receiver for USB and Linux - brought to you by the DAB-Team - . This driver can be taken - as an example for URB-based bulk, control, and isochronous - transactions. URB's are explained in - . - - To compile this driver as a module, choose M here: the - module will be called dabusb. - diff --git a/drivers/staging/dabusb/Makefile b/drivers/staging/dabusb/Makefile deleted file mode 100644 index 2ff2f228e5f9..000000000000 --- a/drivers/staging/dabusb/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_USB_DABUSB) += dabusb.o - diff --git a/drivers/staging/dabusb/TODO b/drivers/staging/dabusb/TODO deleted file mode 100644 index f9c0314ea0c1..000000000000 --- a/drivers/staging/dabusb/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is a driver for an experimental sample developed in 2003. The driver -never supported any commercial product, nor had any known user. -If nobody takes care on it, the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/dabusb/dabusb.c b/drivers/staging/dabusb/dabusb.c deleted file mode 100644 index f3e25e91366d..000000000000 --- a/drivers/staging/dabusb/dabusb.c +++ /dev/null @@ -1,914 +0,0 @@ -/*****************************************************************************/ - -/* - * dabusb.c -- dab usb driver. - * - * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) - * - * 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. - * - * - * - * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $ - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dabusb.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.54" -#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de" -#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999" - -/* --------------------------------------------------------------------- */ - -#ifdef CONFIG_USB_DYNAMIC_MINORS -#define NRDABUSB 256 -#else -#define NRDABUSB 4 -#endif - -/*-------------------------------------------------------------------*/ - -static dabusb_t dabusb[NRDABUSB]; -static int buffers = 256; -static struct usb_driver dabusb_driver; - -/*-------------------------------------------------------------------*/ - -static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) -{ - unsigned long flags; - struct list_head *tmp; - int ret = 0; - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (src)) { - // no elements in source buffer - ret = -1; - goto err; - } - tmp = src->next; - list_move_tail (tmp, dst); - - err: spin_unlock_irqrestore (&s->lock, flags); - return ret; -} -/*-------------------------------------------------------------------*/ -#ifdef DEBUG -static void dump_urb (struct urb *urb) -{ - dbg("urb :%p", urb); - dbg("dev :%p", urb->dev); - dbg("pipe :%08X", urb->pipe); - dbg("status :%d", urb->status); - dbg("transfer_flags :%08X", urb->transfer_flags); - dbg("transfer_buffer :%p", urb->transfer_buffer); - dbg("transfer_buffer_length:%d", urb->transfer_buffer_length); - dbg("actual_length :%d", urb->actual_length); - dbg("setup_packet :%p", urb->setup_packet); - dbg("start_frame :%d", urb->start_frame); - dbg("number_of_packets :%d", urb->number_of_packets); - dbg("interval :%d", urb->interval); - dbg("error_count :%d", urb->error_count); - dbg("context :%p", urb->context); - dbg("complete :%p", urb->complete); -} -#endif -/*-------------------------------------------------------------------*/ -static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) -{ - unsigned long flags; - pbuff_t b; - - dbg("dabusb_cancel_queue"); - - spin_lock_irqsave (&s->lock, flags); - - list_for_each_entry(b, q, buff_list) { -#ifdef DEBUG - dump_urb(b->purb); -#endif - usb_unlink_urb (b->purb); - } - spin_unlock_irqrestore (&s->lock, flags); - return 0; -} -/*-------------------------------------------------------------------*/ -static int dabusb_free_queue (struct list_head *q) -{ - struct list_head *tmp; - struct list_head *p; - pbuff_t b; - - dbg("dabusb_free_queue"); - for (p = q->next; p != q;) { - b = list_entry (p, buff_t, buff_list); - -#ifdef DEBUG - dump_urb(b->purb); -#endif - kfree(b->purb->transfer_buffer); - usb_free_urb(b->purb); - tmp = p->next; - list_del (p); - kfree (b); - p = tmp; - } - - return 0; -} -/*-------------------------------------------------------------------*/ -static int dabusb_free_buffers (pdabusb_t s) -{ - unsigned long flags; - dbg("dabusb_free_buffers"); - - spin_lock_irqsave(&s->lock, flags); - - dabusb_free_queue (&s->free_buff_list); - dabusb_free_queue (&s->rec_buff_list); - - spin_unlock_irqrestore(&s->lock, flags); - - s->got_mem = 0; - return 0; -} -/*-------------------------------------------------------------------*/ -static void dabusb_iso_complete (struct urb *purb) -{ - pbuff_t b = purb->context; - pdabusb_t s = b->s; - int i; - int len; - int dst = 0; - void *buf = purb->transfer_buffer; - - dbg("dabusb_iso_complete"); - - // process if URB was not killed - if (purb->status != -ENOENT) { - unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); - int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); - for (i = 0; i < purb->number_of_packets; i++) - if (!purb->iso_frame_desc[i].status) { - len = purb->iso_frame_desc[i].actual_length; - if (len <= pipesize) { - memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); - dst += len; - } - else - dev_err(&purb->dev->dev, - "dabusb_iso_complete: invalid len %d\n", len); - } - else - dev_warn(&purb->dev->dev, "dabusb_iso_complete: corrupted packet status: %d\n", purb->iso_frame_desc[i].status); - if (dst != purb->actual_length) - dev_err(&purb->dev->dev, - "dst!=purb->actual_length:%d!=%d\n", - dst, purb->actual_length); - } - - if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { - s->overruns++; - dev_err(&purb->dev->dev, "overrun (%d)\n", s->overruns); - } - wake_up (&s->wait); -} -/*-------------------------------------------------------------------*/ -static int dabusb_alloc_buffers (pdabusb_t s) -{ - int transfer_len = 0; - pbuff_t b; - unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); - int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); - int packets = _ISOPIPESIZE / pipesize; - int transfer_buffer_length = packets * pipesize; - int i; - - dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", - pipesize, packets, transfer_buffer_length); - - while (transfer_len < (s->total_buffer_size << 10)) { - b = kzalloc(sizeof (buff_t), GFP_KERNEL); - if (!b) { - dev_err(&s->usbdev->dev, - "kzalloc(sizeof(buff_t))==NULL\n"); - goto err; - } - b->s = s; - b->purb = usb_alloc_urb(packets, GFP_KERNEL); - if (!b->purb) { - dev_err(&s->usbdev->dev, "usb_alloc_urb == NULL\n"); - kfree (b); - goto err; - } - - b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL); - if (!b->purb->transfer_buffer) { - kfree (b->purb); - kfree (b); - dev_err(&s->usbdev->dev, - "kmalloc(%d)==NULL\n", transfer_buffer_length); - goto err; - } - - b->purb->transfer_buffer_length = transfer_buffer_length; - b->purb->number_of_packets = packets; - b->purb->complete = dabusb_iso_complete; - b->purb->context = b; - b->purb->dev = s->usbdev; - b->purb->pipe = pipe; - b->purb->transfer_flags = URB_ISO_ASAP; - - for (i = 0; i < packets; i++) { - b->purb->iso_frame_desc[i].offset = i * pipesize; - b->purb->iso_frame_desc[i].length = pipesize; - } - - transfer_len += transfer_buffer_length; - list_add_tail (&b->buff_list, &s->free_buff_list); - } - s->got_mem = transfer_len; - - return 0; - - err: - dabusb_free_buffers (s); - return -ENOMEM; -} -/*-------------------------------------------------------------------*/ -static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) -{ - int ret; - unsigned int pipe; - int actual_length; - - dbg("dabusb_bulk"); - - if (!pb->pipe) - pipe = usb_rcvbulkpipe (s->usbdev, 2); - else - pipe = usb_sndbulkpipe (s->usbdev, 2); - - ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100); - if(ret<0) { - dev_err(&s->usbdev->dev, - "usb_bulk_msg failed(%d)\n", ret); - - if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - dev_err(&s->usbdev->dev, "set_interface failed\n"); - return -EINVAL; - } - - } - - if( ret == -EPIPE ) { - dev_warn(&s->usbdev->dev, "CLEAR_FEATURE request to remove STALL condition.\n"); - if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) - dev_err(&s->usbdev->dev, "request failed\n"); - } - - pb->size = actual_length; - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_writemem (pdabusb_t s, int pos, const unsigned char *data, - int len) -{ - int ret; - unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); - - if (!transfer_buffer) { - dev_err(&s->usbdev->dev, - "dabusb_writemem: kmalloc(%d) failed.\n", len); - return -ENOMEM; - } - - memcpy (transfer_buffer, data, len); - - ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300); - - kfree (transfer_buffer); - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) -{ - dbg("dabusb_8051_reset: %d",reset_bit); - return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); -} -/* --------------------------------------------------------------------- */ -static int dabusb_loadmem (pdabusb_t s, const char *fname) -{ - int ret; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - - dbg("Enter dabusb_loadmem (internal)"); - - ret = request_ihex_firmware(&fw, "dabusb/firmware.fw", &s->usbdev->dev); - if (ret) { - dev_err(&s->usbdev->dev, - "Failed to load \"dabusb/firmware.fw\": %d\n", ret); - goto out; - } - ret = dabusb_8051_reset (s, 1); - - for (rec = (const struct ihex_binrec *)fw->data; rec; - rec = ihex_next_binrec(rec)) { - dbg("dabusb_writemem: %04X %p %d)", be32_to_cpu(rec->addr), - rec->data, be16_to_cpu(rec->len)); - - ret = dabusb_writemem(s, be32_to_cpu(rec->addr), rec->data, - be16_to_cpu(rec->len)); - if (ret < 0) { - dev_err(&s->usbdev->dev, - "dabusb_writemem failed (%d %04X %p %d)\n", - ret, be32_to_cpu(rec->addr), - rec->data, be16_to_cpu(rec->len)); - break; - } - } - ret = dabusb_8051_reset (s, 0); - release_firmware(fw); - out: - dbg("dabusb_loadmem: exit"); - - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b) -{ - b->size = 4; - b->data[0] = 0x2a; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 0; - - dbg("dabusb_fpga_clear"); - - return dabusb_bulk (s, b); -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b) -{ - b->size = 4; - b->data[0] = 0x2c; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 0; - - dbg("dabusb_fpga_init"); - - return dabusb_bulk (s, b); -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_download (pdabusb_t s, const char *fname) -{ - pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); - const struct firmware *fw; - unsigned int blen, n; - int ret; - - dbg("Enter dabusb_fpga_download (internal)"); - - if (!b) { - dev_err(&s->usbdev->dev, - "kmalloc(sizeof(bulk_transfer_t))==NULL\n"); - return -ENOMEM; - } - - ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev); - if (ret) { - dev_err(&s->usbdev->dev, - "Failed to load \"dabusb/bitstream.bin\": %d\n", ret); - kfree(b); - return ret; - } - - b->pipe = 1; - ret = dabusb_fpga_clear (s, b); - mdelay (10); - blen = fw->data[73] + (fw->data[72] << 8); - - dbg("Bitstream len: %i", blen); - - b->data[0] = 0x2b; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 60; - - for (n = 0; n <= blen + 60; n += 60) { - // some cclks for startup - b->size = 64; - memcpy (b->data + 4, fw->data + 74 + n, 60); - ret = dabusb_bulk (s, b); - if (ret < 0) { - dev_err(&s->usbdev->dev, "dabusb_bulk failed.\n"); - break; - } - mdelay (1); - } - - ret = dabusb_fpga_init (s, b); - kfree (b); - release_firmware(fw); - - dbg("exit dabusb_fpga_download"); - - return ret; -} - -static int dabusb_stop (pdabusb_t s) -{ - dbg("dabusb_stop"); - - s->state = _stopped; - dabusb_cancel_queue (s, &s->rec_buff_list); - - dbg("pending_io: %d", s->pending_io.counter); - - s->pending_io.counter = 0; - return 0; -} - -static int dabusb_startrek (pdabusb_t s) -{ - if (!s->got_mem && s->state != _started) { - - dbg("dabusb_startrek"); - - if (dabusb_alloc_buffers (s) < 0) - return -ENOMEM; - dabusb_stop (s); - s->state = _started; - s->readptr = 0; - } - - if (!list_empty (&s->free_buff_list)) { - pbuff_t end; - int ret; - - while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) { - - dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list); - - end = list_entry (s->rec_buff_list.prev, buff_t, buff_list); - - ret = usb_submit_urb (end->purb, GFP_KERNEL); - if (ret) { - dev_err(&s->usbdev->dev, - "usb_submit_urb returned:%d\n", ret); - if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - dev_err(&s->usbdev->dev, - "startrek: dabusb_add_buf_tail failed\n"); - break; - } - else - atomic_inc (&s->pending_io); - } - dbg("pending_io: %d",s->pending_io.counter); - } - - return 0; -} - -static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - unsigned long flags; - unsigned ret = 0; - int rem; - int cnt; - pbuff_t b; - struct urb *purb = NULL; - - dbg("dabusb_read"); - - if (*ppos) - return -ESPIPE; - - if (s->remove_pending) - return -EIO; - - - if (!s->usbdev) - return -EIO; - - while (count > 0) { - dabusb_startrek (s); - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (&s->rec_buff_list)) { - - spin_unlock_irqrestore(&s->lock, flags); - - dev_err(&s->usbdev->dev, - "error: rec_buf_list is empty\n"); - goto err; - } - - b = list_entry (s->rec_buff_list.next, buff_t, buff_list); - purb = b->purb; - - spin_unlock_irqrestore(&s->lock, flags); - - if (purb->status == -EINPROGRESS) { - if (file->f_flags & O_NONBLOCK) // return nonblocking - { - if (!ret) - ret = -EAGAIN; - goto err; - } - - interruptible_sleep_on (&s->wait); - - if (signal_pending (current)) { - if (!ret) - ret = -ERESTARTSYS; - goto err; - } - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (&s->rec_buff_list)) { - spin_unlock_irqrestore(&s->lock, flags); - dev_err(&s->usbdev->dev, - "error: still no buffer available.\n"); - goto err; - } - spin_unlock_irqrestore(&s->lock, flags); - s->readptr = 0; - } - if (s->remove_pending) { - ret = -EIO; - goto err; - } - - rem = purb->actual_length - s->readptr; // set remaining bytes to copy - - if (count >= rem) - cnt = rem; - else - cnt = count; - - dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); - - if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { - dev_err(&s->usbdev->dev, "read: copy_to_user failed\n"); - if (!ret) - ret = -EFAULT; - goto err; - } - - s->readptr += cnt; - count -= cnt; - buf += cnt; - ret += cnt; - - if (s->readptr == purb->actual_length) { - // finished, take next buffer - if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - dev_err(&s->usbdev->dev, - "read: dabusb_add_buf_tail failed\n"); - s->readptr = 0; - } - } - err: //mutex_unlock(&s->mutex); - return ret; -} - -static int dabusb_open (struct inode *inode, struct file *file) -{ - int devnum = iminor(inode); - pdabusb_t s; - int r; - - if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) - return -EIO; - - s = &dabusb[devnum - DABUSB_MINOR]; - - dbg("dabusb_open"); - mutex_lock(&s->mutex); - - while (!s->usbdev || s->opened) { - mutex_unlock(&s->mutex); - - if (file->f_flags & O_NONBLOCK) - return -EBUSY; - msleep_interruptible(500); - - if (signal_pending (current)) - return -EAGAIN; - mutex_lock(&s->mutex); - } - if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - mutex_unlock(&s->mutex); - dev_err(&s->usbdev->dev, "set_interface failed\n"); - return -EINVAL; - } - s->opened = 1; - mutex_unlock(&s->mutex); - - file->f_pos = 0; - file->private_data = s; - - r = nonseekable_open(inode, file); - return r; -} - -static int dabusb_release (struct inode *inode, struct file *file) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - - dbg("dabusb_release"); - - mutex_lock(&s->mutex); - dabusb_stop (s); - dabusb_free_buffers (s); - mutex_unlock(&s->mutex); - - if (!s->remove_pending) { - if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) - dev_err(&s->usbdev->dev, "set_interface failed\n"); - } - else - wake_up (&s->remove_ok); - - s->opened = 0; - return 0; -} - -static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - pbulk_transfer_t pbulk; - int ret = 0; - int version = DABUSB_VERSION; - - dbg("dabusb_ioctl"); - - if (s->remove_pending) - return -EIO; - - mutex_lock(&s->mutex); - - if (!s->usbdev) { - mutex_unlock(&s->mutex); - return -EIO; - } - - switch (cmd) { - - case IOCTL_DAB_BULK: - pbulk = memdup_user((void __user *)arg, - sizeof(bulk_transfer_t)); - - if (IS_ERR(pbulk)) { - ret = PTR_ERR(pbulk); - break; - } - - ret=dabusb_bulk (s, pbulk); - if(ret==0) - if (copy_to_user((void __user *)arg, pbulk, - sizeof(bulk_transfer_t))) - ret = -EFAULT; - kfree (pbulk); - break; - - case IOCTL_DAB_OVERRUNS: - ret = put_user (s->overruns, (unsigned int __user *) arg); - break; - - case IOCTL_DAB_VERSION: - ret = put_user (version, (unsigned int __user *) arg); - break; - - default: - ret = -ENOIOCTLCMD; - break; - } - mutex_unlock(&s->mutex); - return ret; -} - -static const struct file_operations dabusb_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = dabusb_read, - .unlocked_ioctl = dabusb_ioctl, - .open = dabusb_open, - .release = dabusb_release, -}; - -static char *dabusb_devnode(struct device *dev, mode_t *mode) -{ - return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); -} - -static struct usb_class_driver dabusb_class = { - .name = "dabusb%d", - .devnode = dabusb_devnode, - .fops = &dabusb_fops, - .minor_base = DABUSB_MINOR, -}; - - -/* --------------------------------------------------------------------- */ -static int dabusb_probe (struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *usbdev = interface_to_usbdev(intf); - int retval; - pdabusb_t s; - - dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct), - intf->altsetting->desc.bInterfaceNumber); - - /* We don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF && - le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999) - return -ENODEV; - - - - s = &dabusb[intf->minor]; - - mutex_lock(&s->mutex); - s->remove_pending = 0; - s->usbdev = usbdev; - s->devnum = intf->minor; - - if (usb_reset_configuration (usbdev) < 0) { - dev_err(&intf->dev, "reset_configuration failed\n"); - goto reject; - } - if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) { - dabusb_loadmem (s, NULL); - goto reject; - } - else { - dabusb_fpga_download (s, NULL); - - if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { - dev_err(&intf->dev, "set_interface failed\n"); - goto reject; - } - } - dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber); - usb_set_intfdata (intf, s); - mutex_unlock(&s->mutex); - - retval = usb_register_dev(intf, &dabusb_class); - if (retval) { - usb_set_intfdata (intf, NULL); - return -ENOMEM; - } - - return 0; - - reject: - mutex_unlock(&s->mutex); - s->usbdev = NULL; - return -ENODEV; -} - -static void dabusb_disconnect (struct usb_interface *intf) -{ - wait_queue_t __wait; - pdabusb_t s = usb_get_intfdata (intf); - - dbg("dabusb_disconnect"); - - init_waitqueue_entry(&__wait, current); - - usb_set_intfdata (intf, NULL); - if (s) { - usb_deregister_dev (intf, &dabusb_class); - s->remove_pending = 1; - wake_up (&s->wait); - add_wait_queue(&s->remove_ok, &__wait); - set_current_state(TASK_UNINTERRUPTIBLE); - if (s->state == _started) - schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(&s->remove_ok, &__wait); - - s->usbdev = NULL; - s->overruns = 0; - } -} - -static struct usb_device_id dabusb_ids [] = { - // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */ - { USB_DEVICE(0x0547, 0x9999) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, dabusb_ids); - -static struct usb_driver dabusb_driver = { - .name = "dabusb", - .probe = dabusb_probe, - .disconnect = dabusb_disconnect, - .id_table = dabusb_ids, -}; - -/* --------------------------------------------------------------------- */ - -static int __init dabusb_init (void) -{ - int retval; - unsigned u; - - /* initialize struct */ - for (u = 0; u < NRDABUSB; u++) { - pdabusb_t s = &dabusb[u]; - memset (s, 0, sizeof (dabusb_t)); - mutex_init (&s->mutex); - s->usbdev = NULL; - s->total_buffer_size = buffers; - init_waitqueue_head (&s->wait); - init_waitqueue_head (&s->remove_ok); - spin_lock_init (&s->lock); - INIT_LIST_HEAD (&s->free_buff_list); - INIT_LIST_HEAD (&s->rec_buff_list); - } - - /* register misc device */ - retval = usb_register(&dabusb_driver); - if (retval) - goto out; - - dbg("dabusb_init: driver registered"); - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - -out: - return retval; -} - -static void __exit dabusb_cleanup (void) -{ - dbg("dabusb_cleanup"); - - usb_deregister (&dabusb_driver); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("dabusb/firmware.fw"); -MODULE_FIRMWARE("dabusb/bitstream.bin"); - -module_param(buffers, int, 0); -MODULE_PARM_DESC (buffers, "Number of buffers (default=256)"); - -module_init (dabusb_init); -module_exit (dabusb_cleanup); - -/* --------------------------------------------------------------------- */ diff --git a/drivers/staging/dabusb/dabusb.h b/drivers/staging/dabusb/dabusb.h deleted file mode 100644 index 00eb34c863eb..000000000000 --- a/drivers/staging/dabusb/dabusb.h +++ /dev/null @@ -1,85 +0,0 @@ -#define _BULK_DATA_LEN 64 -typedef struct -{ - unsigned char data[_BULK_DATA_LEN]; - unsigned int size; - unsigned int pipe; -}bulk_transfer_t,*pbulk_transfer_t; - -#define DABUSB_MINOR 240 /* some unassigned USB minor */ -#define DABUSB_VERSION 0x1000 -#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) -#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) -#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int) - -#ifdef __KERNEL__ - -typedef enum { _stopped=0, _started } driver_state_t; - -typedef struct -{ - struct mutex mutex; - struct usb_device *usbdev; - wait_queue_head_t wait; - wait_queue_head_t remove_ok; - spinlock_t lock; - atomic_t pending_io; - driver_state_t state; - int remove_pending; - int got_mem; - int total_buffer_size; - unsigned int overruns; - int readptr; - int opened; - int devnum; - struct list_head free_buff_list; - struct list_head rec_buff_list; -} dabusb_t,*pdabusb_t; - -typedef struct -{ - pdabusb_t s; - struct urb *purb; - struct list_head buff_list; -} buff_t,*pbuff_t; - -typedef struct -{ - wait_queue_head_t wait; -} bulk_completion_context_t, *pbulk_completion_context_t; - - -#define _DABUSB_IF 2 -#define _DABUSB_ISOPIPE 0x09 -#define _ISOPIPESIZE 16384 - -#define _BULK_DATA_LEN 64 -// Vendor specific request code for Anchor Upload/Download -// This one is implemented in the core -#define ANCHOR_LOAD_INTERNAL 0xA0 - -// EZ-USB Control and Status Register. Bit 0 controls 8051 reset -#define CPUCS_REG 0x7F92 -#define _TOTAL_BUFFERS 384 - -#define MAX_INTEL_HEX_RECORD_LENGTH 16 - -#ifndef _BYTE_DEFINED -#define _BYTE_DEFINED -typedef unsigned char BYTE; -#endif // !_BYTE_DEFINED - -#ifndef _WORD_DEFINED -#define _WORD_DEFINED -typedef unsigned short WORD; -#endif // !_WORD_DEFINED - -typedef struct _INTEL_HEX_RECORD -{ - BYTE Length; - WORD Address; - BYTE Type; - BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH]; -} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD; - -#endif -- cgit v1.2.3 From 7ee40aadabd59b6cab60835f0ef9cdbe385df438 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 10:10:38 -0300 Subject: [media] v4l: removal of old, obsolete ioctls Some ioctl's were defined wrong on 2.6.2 and 2.6.6, using the wrong type of R/W arguments. They were fixed, but the old ioctl names are still there, maintained to avoid breaking binary compatibility: There's no sense on preserving those forever, as it is very doubtful that someone would try to use a such old binary with a modern kernel. Removing them will allow us to remove some magic done at the V4L ioctl handler. Note that any application compiled with a videodev2.h from 2.6.7 or later will be using the correct ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 21 ----------------- drivers/media/video/v4l2-common.c | 1 - drivers/media/video/v4l2-compat-ioctl32.c | 15 ------------ drivers/media/video/v4l2-ioctl.c | 38 ------------------------------ drivers/staging/easycap/easycap_ioctl.c | 5 ---- include/linux/videodev2.h | 10 -------- 6 files changed, 90 deletions(-) (limited to 'drivers/staging') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 08e0df12df37..61fb823e5a95 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -97,27 +97,6 @@ Who: Pavel Machek --------------------------- -What: Video4Linux: Remove obsolete ioctl's -When: kernel 2.6.39 -Files: include/media/videodev2.h -Why: Some ioctl's were defined wrong on 2.6.2 and 2.6.6, using the wrong - type of R/W arguments. They were fixed, but the old ioctl names are - still there, maintained to avoid breaking binary compatibility: - #define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) - #define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) - #define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) - #define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) - #define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) - #define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) - There's no sense on preserving those forever, as it is very doubtful - that someone would try to use a such old binary with a modern kernel. - Removing them will allow us to remove some magic done at the V4L ioctl - handler. - -Who: Mauro Carvalho Chehab - ---------------------------- - What: sys_sysctl When: September 2010 Option: CONFIG_SYSCTL_SYSCALL diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 810eef43c216..940b5db3463e 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -59,7 +59,6 @@ #include #include #include -#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index c19208a07b48..7c2694738b31 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -14,7 +14,6 @@ */ #include -#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include @@ -678,9 +677,6 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) -#ifdef __OLD_VIDIOC_ -#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) -#endif #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) @@ -720,9 +716,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; -#endif case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; @@ -856,14 +849,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) return ret; switch (cmd) { -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY32_OLD: - case VIDIOC_S_PARM_OLD: - case VIDIOC_S_CTRL_OLD: - case VIDIOC_G_AUDIO_OLD: - case VIDIOC_G_AUDOUT_OLD: - case VIDIOC_CROPCAP_OLD: -#endif case VIDIOC_QUERYCAP: case VIDIOC_RESERVED: case VIDIOC_ENUM_FMT: diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 8360ed2d933a..7a720745c3fa 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -17,7 +17,6 @@ #include #include -#define __OLD_VIDIOC_ /* To allow fixing old calls */ #include #include @@ -297,37 +296,6 @@ EXPORT_SYMBOL(v4l_printk_ioctl); /* * helper function -- handles userspace copying for ioctl arguments - */ - -#ifdef __OLD_VIDIOC_ -static unsigned int -video_fix_command(unsigned int cmd) -{ - switch (cmd) { - case VIDIOC_OVERLAY_OLD: - cmd = VIDIOC_OVERLAY; - break; - case VIDIOC_S_PARM_OLD: - cmd = VIDIOC_S_PARM; - break; - case VIDIOC_S_CTRL_OLD: - cmd = VIDIOC_S_CTRL; - break; - case VIDIOC_G_AUDIO_OLD: - cmd = VIDIOC_G_AUDIO; - break; - case VIDIOC_G_AUDOUT_OLD: - cmd = VIDIOC_G_AUDOUT; - break; - case VIDIOC_CROPCAP_OLD: - cmd = VIDIOC_CROPCAP; - break; - } - return cmd; -} -#endif - -/* * Obsolete usercopy function - Should be removed soon */ long @@ -342,9 +310,6 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, size_t ctrls_size = 0; void __user *user_ptr = NULL; -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || cmd == VIDIOC_TRY_EXT_CTRLS); @@ -2379,9 +2344,6 @@ long video_ioctl2(struct file *file, void __user *user_ptr = NULL; void **kernel_ptr = NULL; -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif /* Copy arguments into temp kernel buffer */ if (_IOC_DIR(cmd) != _IOC_NONE) { if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c index 447953a4e80c..7ac43da4e252 100644 --- a/drivers/staging/easycap/easycap_ioctl.c +++ b/drivers/staging/easycap/easycap_ioctl.c @@ -1399,11 +1399,6 @@ case VIDIOC_G_CTRL: { break; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -#if defined(VIDIOC_S_CTRL_OLD) -case VIDIOC_S_CTRL_OLD: { - JOM(8, "VIDIOC_S_CTRL_OLD required at least for xawtv\n"); -} -#endif /*VIDIOC_S_CTRL_OLD*/ case VIDIOC_S_CTRL: { struct v4l2_control v4l2_control; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5122b265dde6..a94c4d5ac340 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1935,16 +1935,6 @@ struct v4l2_dbg_chip_ident { /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -#ifdef __OLD_VIDIOC_ -/* for compatibility, will go away some day */ -#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) -#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) -#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) -#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) -#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) -#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) -#endif - #define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ #endif /* __LINUX_VIDEODEV2_H */ -- cgit v1.2.3 From fa766c9be58bd872784b081074a7d15a3ce61c5f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:00:00 -0300 Subject: [media] Altera FPGA firmware download module It uses STAPL files and programs Altera FPGA through JTAG. Interface to JTAG must be provided from main device module, for example through cx23885 GPIO. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/altera-stapl/Kconfig | 8 + drivers/staging/altera-stapl/Makefile | 3 + drivers/staging/altera-stapl/altera-comp.c | 142 ++ drivers/staging/altera-stapl/altera-exprt.h | 33 + drivers/staging/altera-stapl/altera-jtag.c | 1020 +++++++++++ drivers/staging/altera-stapl/altera-jtag.h | 113 ++ drivers/staging/altera-stapl/altera-lpt.c | 70 + drivers/staging/altera-stapl/altera.c | 2527 +++++++++++++++++++++++++++ include/staging/altera.h | 49 + 11 files changed, 3968 insertions(+) create mode 100644 drivers/staging/altera-stapl/Kconfig create mode 100644 drivers/staging/altera-stapl/Makefile create mode 100644 drivers/staging/altera-stapl/altera-comp.c create mode 100644 drivers/staging/altera-stapl/altera-exprt.h create mode 100644 drivers/staging/altera-stapl/altera-jtag.c create mode 100644 drivers/staging/altera-stapl/altera-jtag.h create mode 100644 drivers/staging/altera-stapl/altera-lpt.c create mode 100644 drivers/staging/altera-stapl/altera.c create mode 100644 include/staging/altera.h (limited to 'drivers/staging') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a2742d8102c1..74a8b272154f 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -175,5 +175,7 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" +source "drivers/staging/altera-stapl/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 4ddd3ce780e0..9f50ec90f9d8 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -66,5 +66,6 @@ obj-$(CONFIG_BCM_WIMAX) += bcm/ obj-$(CONFIG_FT1000) += ft1000/ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ +obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ diff --git a/drivers/staging/altera-stapl/Kconfig b/drivers/staging/altera-stapl/Kconfig new file mode 100644 index 000000000000..7f01d8e93992 --- /dev/null +++ b/drivers/staging/altera-stapl/Kconfig @@ -0,0 +1,8 @@ +comment "Altera FPGA firmware download module" + +config ALTERA_STAPL + tristate "Altera FPGA firmware download module" + depends on I2C + default n + help + An Altera FPGA module. Say Y when you want to support this tool. diff --git a/drivers/staging/altera-stapl/Makefile b/drivers/staging/altera-stapl/Makefile new file mode 100644 index 000000000000..055f61ee781a --- /dev/null +++ b/drivers/staging/altera-stapl/Makefile @@ -0,0 +1,3 @@ +altera-stapl-objs = altera-lpt.o altera-jtag.o altera-comp.o altera.o + +obj-$(CONFIG_ALTERA_STAPL) += altera-stapl.o diff --git a/drivers/staging/altera-stapl/altera-comp.c b/drivers/staging/altera-stapl/altera-comp.c new file mode 100644 index 000000000000..49b103bedaaf --- /dev/null +++ b/drivers/staging/altera-stapl/altera-comp.c @@ -0,0 +1,142 @@ +/* + * altera-comp.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 "altera-exprt.h" + +#define SHORT_BITS 16 +#define CHAR_BITS 8 +#define DATA_BLOB_LENGTH 3 +#define MATCH_DATA_LENGTH 8192 +#define ALTERA_REQUEST_SIZE 1024 +#define ALTERA_BUFFER_SIZE (MATCH_DATA_LENGTH + ALTERA_REQUEST_SIZE) + +static u32 altera_bits_req(u32 n) +{ + u32 result = SHORT_BITS; + + if (n == 0) + result = 1; + else { + /* Look for the highest non-zero bit position */ + while ((n & (1 << (SHORT_BITS - 1))) == 0) { + n <<= 1; + --result; + } + } + + return result; +} + +static u32 altera_read_packed(u8 *buffer, u32 bits, u32 *bits_avail, + u32 *in_index) +{ + u32 result = 0; + u32 shift = 0; + u32 databyte = 0; + + while (bits > 0) { + databyte = buffer[*in_index]; + result |= (((databyte >> (CHAR_BITS - *bits_avail)) + & (0xff >> (CHAR_BITS - *bits_avail))) << shift); + + if (bits <= *bits_avail) { + result &= (0xffff >> (SHORT_BITS - (bits + shift))); + *bits_avail -= bits; + bits = 0; + } else { + ++(*in_index); + shift += *bits_avail; + bits -= *bits_avail; + *bits_avail = CHAR_BITS; + } + } + + return result; +} + +u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version) +{ + u32 i, j, data_length = 0L; + u32 offset, length; + u32 match_data_length = MATCH_DATA_LENGTH; + u32 bits_avail = CHAR_BITS; + u32 in_index = 0L; + + if (version > 0) + --match_data_length; + + for (i = 0; i < out_length; ++i) + out[i] = 0; + + /* Read number of bytes in data. */ + for (i = 0; i < sizeof(in_length); ++i) { + data_length = data_length | ( + altera_read_packed(in, + CHAR_BITS, + &bits_avail, + &in_index) << (i * CHAR_BITS)); + } + + if (data_length > out_length) { + data_length = 0L; + return data_length; + } + + i = 0; + while (i < data_length) { + /* A 0 bit indicates literal data. */ + if (altera_read_packed(in, 1, &bits_avail, + &in_index) == 0) { + for (j = 0; j < DATA_BLOB_LENGTH; ++j) { + if (i < data_length) { + out[i] = (u8)altera_read_packed(in, + CHAR_BITS, + &bits_avail, + &in_index); + i++; + } + } + } else { + /* A 1 bit indicates offset/length to follow. */ + offset = altera_read_packed(in, altera_bits_req((s16) + (i > match_data_length ? + match_data_length : i)), + &bits_avail, + &in_index); + length = altera_read_packed(in, CHAR_BITS, + &bits_avail, + &in_index); + for (j = 0; j < length; ++j) { + if (i < data_length) { + out[i] = out[i - offset]; + i++; + } + } + } + } + + return data_length; +} diff --git a/drivers/staging/altera-stapl/altera-exprt.h b/drivers/staging/altera-stapl/altera-exprt.h new file mode 100644 index 000000000000..39c38d84a670 --- /dev/null +++ b/drivers/staging/altera-stapl/altera-exprt.h @@ -0,0 +1,33 @@ +/* + * altera-exprt.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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. + */ + +#ifndef ALTERA_EXPRT_H +#define ALTERA_EXPRT_H + + +u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version); +int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo); + +#endif /* ALTERA_EXPRT_H */ diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/staging/altera-stapl/altera-jtag.c new file mode 100644 index 000000000000..6b633b179a7e --- /dev/null +++ b/drivers/staging/altera-stapl/altera-jtag.c @@ -0,0 +1,1020 @@ +/* + * altera-jtag.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 "altera-exprt.h" +#include "altera-jtag.h" + +#define alt_jtag_io(a, b, c)\ + astate->config->jtag_io(astate->config->dev, a, b, c); + +#define alt_malloc(a) kzalloc(a, GFP_KERNEL); + +/* + * This structure shows, for each JTAG state, which state is reached after + * a single TCK clock cycle with TMS high or TMS low, respectively. This + * describes all possible state transitions in the JTAG state machine. + */ +struct altera_jtag_machine { + enum altera_jtag_state tms_high; + enum altera_jtag_state tms_low; +}; + +static const struct altera_jtag_machine altera_transitions[] = { + /* RESET */ { RESET, IDLE }, + /* IDLE */ { DRSELECT, IDLE }, + /* DRSELECT */ { IRSELECT, DRCAPTURE }, + /* DRCAPTURE */ { DREXIT1, DRSHIFT }, + /* DRSHIFT */ { DREXIT1, DRSHIFT }, + /* DREXIT1 */ { DRUPDATE, DRPAUSE }, + /* DRPAUSE */ { DREXIT2, DRPAUSE }, + /* DREXIT2 */ { DRUPDATE, DRSHIFT }, + /* DRUPDATE */ { DRSELECT, IDLE }, + /* IRSELECT */ { RESET, IRCAPTURE }, + /* IRCAPTURE */ { IREXIT1, IRSHIFT }, + /* IRSHIFT */ { IREXIT1, IRSHIFT }, + /* IREXIT1 */ { IRUPDATE, IRPAUSE }, + /* IRPAUSE */ { IREXIT2, IRPAUSE }, + /* IREXIT2 */ { IRUPDATE, IRSHIFT }, + /* IRUPDATE */ { DRSELECT, IDLE } +}; + +/* + * This table contains the TMS value to be used to take the NEXT STEP on + * the path to the desired state. The array index is the current state, + * and the bit position is the desired endstate. To find out which state + * is used as the intermediate state, look up the TMS value in the + * altera_transitions[] table. + */ +static const u16 altera_jtag_path_map[16] = { + /* RST RTI SDRS CDR SDR E1DR PDR E2DR */ + 0x0001, 0xFFFD, 0xFE01, 0xFFE7, 0xFFEF, 0xFF0F, 0xFFBF, 0xFFFF, + /* UDR SIRS CIR SIR E1IR PIR E2IR UIR */ + 0xFEFD, 0x0001, 0xF3FF, 0xF7FF, 0x87FF, 0xDFFF, 0xFFFF, 0x7FFD +}; + +/* Flag bits for alt_jtag_io() function */ +#define TMS_HIGH 1 +#define TMS_LOW 0 +#define TDI_HIGH 1 +#define TDI_LOW 0 +#define READ_TDO 1 +#define IGNORE_TDO 0 + +int altera_jinit(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + + /* initial JTAG state is unknown */ + js->jtag_state = ILLEGAL_JTAG_STATE; + + /* initialize to default state */ + js->drstop_state = IDLE; + js->irstop_state = IDLE; + js->dr_pre = 0; + js->dr_post = 0; + js->ir_pre = 0; + js->ir_post = 0; + js->dr_length = 0; + js->ir_length = 0; + + js->dr_pre_data = NULL; + js->dr_post_data = NULL; + js->ir_pre_data = NULL; + js->ir_post_data = NULL; + js->dr_buffer = NULL; + js->ir_buffer = NULL; + + return 0; +} + +int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state) +{ + js->drstop_state = state; + + return 0; +} + +int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state) +{ + js->irstop_state = state; + + return 0; +} + +int altera_set_dr_pre(struct altera_jtag *js, + u32 count, u32 start_index, + u8 *preamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->dr_pre) { + kfree(js->dr_pre_data); + js->dr_pre_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->dr_pre_data == NULL) + status = -ENOMEM; + else + js->dr_pre = count; + } else + js->dr_pre = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (preamble_data == NULL) + js->dr_pre_data[i >> 3] |= (1 << (i & 7)); + else { + if (preamble_data[j >> 3] & (1 << (j & 7))) + js->dr_pre_data[i >> 3] |= + (1 << (i & 7)); + else + js->dr_pre_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->ir_pre) { + kfree(js->ir_pre_data); + js->ir_pre_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->ir_pre_data == NULL) + status = -ENOMEM; + else + js->ir_pre = count; + + } else + js->ir_pre = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + if (preamble_data == NULL) + js->ir_pre_data[i >> 3] |= (1 << (i & 7)); + else { + if (preamble_data[j >> 3] & (1 << (j & 7))) + js->ir_pre_data[i >> 3] |= + (1 << (i & 7)); + else + js->ir_pre_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->dr_post) { + kfree(js->dr_post_data); + js->dr_post_data = (u8 *)alt_malloc((count + 7) >> 3); + + if (js->dr_post_data == NULL) + status = -ENOMEM; + else + js->dr_post = count; + + } else + js->dr_post = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (postamble_data == NULL) + js->dr_post_data[i >> 3] |= (1 << (i & 7)); + else { + if (postamble_data[j >> 3] & (1 << (j & 7))) + js->dr_post_data[i >> 3] |= + (1 << (i & 7)); + else + js->dr_post_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->ir_post) { + kfree(js->ir_post_data); + js->ir_post_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->ir_post_data == NULL) + status = -ENOMEM; + else + js->ir_post = count; + + } else + js->ir_post = count; + + if (status != 0) + return status; + + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (postamble_data == NULL) + js->ir_post_data[i >> 3] |= (1 << (i & 7)); + else { + if (postamble_data[j >> 3] & (1 << (j & 7))) + js->ir_post_data[i >> 3] |= (1 << (i & 7)); + else + js->ir_post_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + + return status; +} + +static void altera_jreset_idle(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + int i; + /* Go to Test Logic Reset (no matter what the starting state may be) */ + for (i = 0; i < 5; ++i) + alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO); + + /* Now step to Run Test / Idle */ + alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO); + js->jtag_state = IDLE; +} + +int altera_goto_jstate(struct altera_state *astate, + enum altera_jtag_state state) +{ + struct altera_jtag *js = &astate->js; + int tms; + int count = 0; + int status = 0; + + if (js->jtag_state == ILLEGAL_JTAG_STATE) + /* initialize JTAG chain to known state */ + altera_jreset_idle(astate); + + if (js->jtag_state == state) { + /* + * We are already in the desired state. + * If it is a stable state, loop here. + * Otherwise do nothing (no clock cycles). + */ + if ((state == IDLE) || (state == DRSHIFT) || + (state == DRPAUSE) || (state == IRSHIFT) || + (state == IRPAUSE)) { + alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO); + } else if (state == RESET) + alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO); + + } else { + while ((js->jtag_state != state) && (count < 9)) { + /* Get TMS value to take a step toward desired state */ + tms = (altera_jtag_path_map[js->jtag_state] & + (1 << state)) + ? TMS_HIGH : TMS_LOW; + + /* Take a step */ + alt_jtag_io(tms, TDI_LOW, IGNORE_TDO); + + if (tms) + js->jtag_state = + altera_transitions[js->jtag_state].tms_high; + else + js->jtag_state = + altera_transitions[js->jtag_state].tms_low; + + ++count; + } + } + + if (js->jtag_state != state) + status = -EREMOTEIO; + + return status; +} + +int altera_wait_cycles(struct altera_state *astate, + s32 cycles, + enum altera_jtag_state wait_state) +{ + struct altera_jtag *js = &astate->js; + int tms; + s32 count; + int status = 0; + + if (js->jtag_state != wait_state) + status = altera_goto_jstate(astate, wait_state); + + if (status == 0) { + /* + * Set TMS high to loop in RESET state + * Set TMS low to loop in any other stable state + */ + tms = (wait_state == RESET) ? TMS_HIGH : TMS_LOW; + + for (count = 0L; count < cycles; count++) + alt_jtag_io(tms, TDI_LOW, IGNORE_TDO); + + } + + return status; +} + +int altera_wait_msecs(struct altera_state *astate, + s32 microseconds, enum altera_jtag_state wait_state) +/* + * Causes JTAG hardware to sit in the specified stable + * state for the specified duration of real time. If + * no JTAG operations have been performed yet, then only + * a delay is performed. This permits the WAIT USECS + * statement to be used in VECTOR programs without causing + * any JTAG operations. + * Returns 0 for success, else appropriate error code. + */ +{ + struct altera_jtag *js = &astate->js; + int status = 0; + + if ((js->jtag_state != ILLEGAL_JTAG_STATE) && + (js->jtag_state != wait_state)) + status = altera_goto_jstate(astate, wait_state); + + if (status == 0) + /* Wait for specified time interval */ + udelay(microseconds); + + return status; +} + +static void altera_concatenate_data(u8 *buffer, + u8 *preamble_data, + u32 preamble_count, + u8 *target_data, + u32 start_index, + u32 target_count, + u8 *postamble_data, + u32 postamble_count) +/* + * Copies preamble data, target data, and postamble data + * into one buffer for IR or DR scans. + */ +{ + u32 i, j, k; + + for (i = 0L; i < preamble_count; ++i) { + if (preamble_data[i >> 3L] & (1L << (i & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } + + j = start_index; + k = preamble_count + target_count; + for (; i < k; ++i, ++j) { + if (target_data[j >> 3L] & (1L << (j & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } + + j = 0L; + k = preamble_count + target_count + postamble_count; + for (; i < k; ++i, ++j) { + if (postamble_data[j >> 3L] & (1L << (j & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } +} + +static int alt_jtag_drscan(struct altera_state *astate, + int start_state, + int count, + u8 *tdi, + u8 *tdo) +{ + int i = 0; + int tdo_bit = 0; + int status = 1; + + /* First go to DRSHIFT state */ + switch (start_state) { + case 0: /* IDLE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + case 1: /* DRPAUSE */ + alt_jtag_io(1, 0, 0); /* DREXIT2 */ + alt_jtag_io(1, 0, 0); /* DRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + case 2: /* IRPAUSE */ + alt_jtag_io(1, 0, 0); /* IREXIT2 */ + alt_jtag_io(1, 0, 0); /* IRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + default: + status = 0; + } + + if (status) { + /* loop in the SHIFT-DR state */ + for (i = 0; i < count; i++) { + tdo_bit = alt_jtag_io( + (i == count - 1), + tdi[i >> 3] & (1 << (i & 7)), + (tdo != NULL)); + + if (tdo != NULL) { + if (tdo_bit) + tdo[i >> 3] |= (1 << (i & 7)); + else + tdo[i >> 3] &= ~(u32)(1 << (i & 7)); + + } + } + + alt_jtag_io(0, 0, 0); /* DRPAUSE */ + } + + return status; +} + +static int alt_jtag_irscan(struct altera_state *astate, + int start_state, + int count, + u8 *tdi, + u8 *tdo) +{ + int i = 0; + int tdo_bit = 0; + int status = 1; + + /* First go to IRSHIFT state */ + switch (start_state) { + case 0: /* IDLE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + case 1: /* DRPAUSE */ + alt_jtag_io(1, 0, 0); /* DREXIT2 */ + alt_jtag_io(1, 0, 0); /* DRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + case 2: /* IRPAUSE */ + alt_jtag_io(1, 0, 0); /* IREXIT2 */ + alt_jtag_io(1, 0, 0); /* IRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + default: + status = 0; + } + + if (status) { + /* loop in the SHIFT-IR state */ + for (i = 0; i < count; i++) { + tdo_bit = alt_jtag_io( + (i == count - 1), + tdi[i >> 3] & (1 << (i & 7)), + (tdo != NULL)); + if (tdo != NULL) { + if (tdo_bit) + tdo[i >> 3] |= (1 << (i & 7)); + else + tdo[i >> 3] &= ~(u32)(1 << (i & 7)); + + } + } + + alt_jtag_io(0, 0, 0); /* IRPAUSE */ + } + + return status; +} + +static void altera_extract_target_data(u8 *buffer, + u8 *target_data, + u32 start_index, + u32 preamble_count, + u32 target_count) +/* + * Copies target data from scan buffer, filtering out + * preamble and postamble data. + */ +{ + u32 i; + u32 j; + u32 k; + + j = preamble_count; + k = start_index + target_count; + for (i = start_index; i < k; ++i, ++j) { + if (buffer[j >> 3] & (1 << (j & 7))) + target_data[i >> 3] |= (1 << (i & 7)); + else + target_data[i >> 3] &= ~(u32)(1 << (i & 7)); + + } +} + +int altera_irscan(struct altera_state *astate, + u32 count, + u8 *tdi_data, + u32 start_index) +/* Shifts data into instruction register */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->ir_pre + count + js->ir_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->ir_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->ir_buffer); + js->ir_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->ir_buffer == NULL) + status = -ENOMEM; + else + js->ir_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, IR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->ir_buffer, + js->ir_pre_data, + js->ir_pre, + tdi_data, + start_index, + count, + js->ir_post_data, + js->ir_post); + /* Do the IRSCAN */ + alt_jtag_irscan(astate, + start_code, + shift_count, + js->ir_buffer, + NULL); + + /* alt_jtag_irscan() always ends in IRPAUSE state */ + js->jtag_state = IRPAUSE; + } + + if (status == 0) + if (js->irstop_state != IRPAUSE) + status = altera_goto_jstate(astate, js->irstop_state); + + + return status; +} + +int altera_swap_ir(struct altera_state *astate, + u32 count, + u8 *in_data, + u32 in_index, + u8 *out_data, + u32 out_index) +/* Shifts data into instruction register, capturing output data */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->ir_pre + count + js->ir_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->ir_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->ir_buffer); + js->ir_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->ir_buffer == NULL) + status = -ENOMEM; + else + js->ir_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, IR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->ir_buffer, + js->ir_pre_data, + js->ir_pre, + in_data, + in_index, + count, + js->ir_post_data, + js->ir_post); + + /* Do the IRSCAN */ + alt_jtag_irscan(astate, + start_code, + shift_count, + js->ir_buffer, + js->ir_buffer); + + /* alt_jtag_irscan() always ends in IRPAUSE state */ + js->jtag_state = IRPAUSE; + } + + if (status == 0) + if (js->irstop_state != IRPAUSE) + status = altera_goto_jstate(astate, js->irstop_state); + + + if (status == 0) + /* Now extract the returned data from the buffer */ + altera_extract_target_data(js->ir_buffer, + out_data, out_index, + js->ir_pre, count); + + return status; +} + +int altera_drscan(struct altera_state *astate, + u32 count, + u8 *tdi_data, + u32 start_index) +/* Shifts data into data register (ignoring output data) */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->dr_pre + count + js->dr_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->dr_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->dr_buffer); + js->dr_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->dr_buffer == NULL) + status = -ENOMEM; + else + js->dr_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, DR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->dr_buffer, + js->dr_pre_data, + js->dr_pre, + tdi_data, + start_index, + count, + js->dr_post_data, + js->dr_post); + /* Do the DRSCAN */ + alt_jtag_drscan(astate, start_code, shift_count, + js->dr_buffer, NULL); + /* alt_jtag_drscan() always ends in DRPAUSE state */ + js->jtag_state = DRPAUSE; + } + + if (status == 0) + if (js->drstop_state != DRPAUSE) + status = altera_goto_jstate(astate, js->drstop_state); + + return status; +} + +int altera_swap_dr(struct altera_state *astate, u32 count, + u8 *in_data, u32 in_index, + u8 *out_data, u32 out_index) +/* Shifts data into data register, capturing output data */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->dr_pre + count + js->dr_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->dr_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->dr_buffer); + js->dr_buffer = (u8 *)alt_malloc(alloc_chars); + + if (js->dr_buffer == NULL) + status = -ENOMEM; + else + js->dr_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, DR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->dr_buffer, + js->dr_pre_data, + js->dr_pre, + in_data, + in_index, + count, + js->dr_post_data, + js->dr_post); + + /* Do the DRSCAN */ + alt_jtag_drscan(astate, + start_code, + shift_count, + js->dr_buffer, + js->dr_buffer); + + /* alt_jtag_drscan() always ends in DRPAUSE state */ + js->jtag_state = DRPAUSE; + } + + if (status == 0) + if (js->drstop_state != DRPAUSE) + status = altera_goto_jstate(astate, js->drstop_state); + + if (status == 0) + /* Now extract the returned data from the buffer */ + altera_extract_target_data(js->dr_buffer, + out_data, + out_index, + js->dr_pre, + count); + + return status; +} + +void altera_free_buffers(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + /* If the JTAG interface was used, reset it to TLR */ + if (js->jtag_state != ILLEGAL_JTAG_STATE) + altera_jreset_idle(astate); + + kfree(js->dr_pre_data); + js->dr_pre_data = NULL; + + kfree(js->dr_post_data); + js->dr_post_data = NULL; + + kfree(js->dr_buffer); + js->dr_buffer = NULL; + + kfree(js->ir_pre_data); + js->ir_pre_data = NULL; + + kfree(js->ir_post_data); + js->ir_post_data = NULL; + + kfree(js->ir_buffer); + js->ir_buffer = NULL; +} diff --git a/drivers/staging/altera-stapl/altera-jtag.h b/drivers/staging/altera-stapl/altera-jtag.h new file mode 100644 index 000000000000..2f97e36a2fbc --- /dev/null +++ b/drivers/staging/altera-stapl/altera-jtag.h @@ -0,0 +1,113 @@ +/* + * altera-jtag.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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. + */ + +#ifndef ALTERA_JTAG_H +#define ALTERA_JTAG_H + +/* Function Prototypes */ +enum altera_jtag_state { + ILLEGAL_JTAG_STATE = -1, + RESET = 0, + IDLE = 1, + DRSELECT = 2, + DRCAPTURE = 3, + DRSHIFT = 4, + DREXIT1 = 5, + DRPAUSE = 6, + DREXIT2 = 7, + DRUPDATE = 8, + IRSELECT = 9, + IRCAPTURE = 10, + IRSHIFT = 11, + IREXIT1 = 12, + IRPAUSE = 13, + IREXIT2 = 14, + IRUPDATE = 15 + +}; + +struct altera_jtag { + /* Global variable to store the current JTAG state */ + enum altera_jtag_state jtag_state; + + /* Store current stop-state for DR and IR scan commands */ + enum altera_jtag_state drstop_state; + enum altera_jtag_state irstop_state; + + /* Store current padding values */ + u32 dr_pre; + u32 dr_post; + u32 ir_pre; + u32 ir_post; + u32 dr_length; + u32 ir_length; + u8 *dr_pre_data; + u8 *dr_post_data; + u8 *ir_pre_data; + u8 *ir_post_data; + u8 *dr_buffer; + u8 *ir_buffer; +}; + +#define ALTERA_STACK_SIZE 128 +#define ALTERA_MESSAGE_LENGTH 1024 + +struct altera_state { + struct altera_config *config; + struct altera_jtag js; + char msg_buff[ALTERA_MESSAGE_LENGTH + 1]; + long stack[ALTERA_STACK_SIZE]; +}; + +int altera_jinit(struct altera_state *astate); +int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state); +int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state); +int altera_set_dr_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data); +int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data); +int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data); +int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data); +int altera_goto_jstate(struct altera_state *astate, + enum altera_jtag_state state); +int altera_wait_cycles(struct altera_state *astate, s32 cycles, + enum altera_jtag_state wait_state); +int altera_wait_msecs(struct altera_state *astate, s32 microseconds, + enum altera_jtag_state wait_state); +int altera_irscan(struct altera_state *astate, u32 count, + u8 *tdi_data, u32 start_index); +int altera_swap_ir(struct altera_state *astate, + u32 count, u8 *in_data, + u32 in_index, u8 *out_data, + u32 out_index); +int altera_drscan(struct altera_state *astate, u32 count, + u8 *tdi_data, u32 start_index); +int altera_swap_dr(struct altera_state *astate, u32 count, + u8 *in_data, u32 in_index, + u8 *out_data, u32 out_index); +void altera_free_buffers(struct altera_state *astate); +#endif /* ALTERA_JTAG_H */ diff --git a/drivers/staging/altera-stapl/altera-lpt.c b/drivers/staging/altera-stapl/altera-lpt.c new file mode 100644 index 000000000000..91456a03612d --- /dev/null +++ b/drivers/staging/altera-stapl/altera-lpt.c @@ -0,0 +1,70 @@ +/* + * altera-lpt.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Abylay Ospan + * + * 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 "altera-exprt.h" + +static int lpt_hardware_initialized; + +static void byteblaster_write(int port, int data) +{ + outb((u8)data, (u16)(port + 0x378)); +}; + +static int byteblaster_read(int port) +{ + int data = 0; + data = inb((u16)(port + 0x378)); + return data & 0xff; +}; + +int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo) +{ + int data = 0; + int tdo = 0; + int initial_lpt_ctrl = 0; + + if (!lpt_hardware_initialized) { + initial_lpt_ctrl = byteblaster_read(2); + byteblaster_write(2, (initial_lpt_ctrl | 0x02) & 0xdf); + lpt_hardware_initialized = 1; + } + + data = ((tdi ? 0x40 : 0) | (tms ? 0x02 : 0)); + + byteblaster_write(0, data); + + if (read_tdo) { + tdo = byteblaster_read(1); + tdo = ((tdo & 0x80) ? 0 : 1); + } + + byteblaster_write(0, data | 0x01); + + byteblaster_write(0, data); + + return tdo; +} diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/staging/altera-stapl/altera.c new file mode 100644 index 000000000000..05aad351b120 --- /dev/null +++ b/drivers/staging/altera-stapl/altera.c @@ -0,0 +1,2527 @@ +/* + * altera.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * 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 "altera-exprt.h" +#include "altera-jtag.h" + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debugging information"); + +MODULE_DESCRIPTION("altera FPGA kernel module"); +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_LICENSE("GPL"); + +#define dprintk(args...) \ + if (debug) { \ + printk(KERN_DEBUG args); \ + } + +enum altera_fpga_opcode { + OP_NOP = 0, + OP_DUP, + OP_SWP, + OP_ADD, + OP_SUB, + OP_MULT, + OP_DIV, + OP_MOD, + OP_SHL, + OP_SHR, + OP_NOT, + OP_AND, + OP_OR, + OP_XOR, + OP_INV, + OP_GT, + OP_LT, + OP_RET, + OP_CMPS, + OP_PINT, + OP_PRNT, + OP_DSS, + OP_DSSC, + OP_ISS, + OP_ISSC, + OP_DPR = 0x1c, + OP_DPRL, + OP_DPO, + OP_DPOL, + OP_IPR, + OP_IPRL, + OP_IPO, + OP_IPOL, + OP_PCHR, + OP_EXIT, + OP_EQU, + OP_POPT, + OP_ABS = 0x2c, + OP_BCH0, + OP_PSH0 = 0x2f, + OP_PSHL = 0x40, + OP_PSHV, + OP_JMP, + OP_CALL, + OP_NEXT, + OP_PSTR, + OP_SINT = 0x47, + OP_ST, + OP_ISTP, + OP_DSTP, + OP_SWPN, + OP_DUPN, + OP_POPV, + OP_POPE, + OP_POPA, + OP_JMPZ, + OP_DS, + OP_IS, + OP_DPRA, + OP_DPOA, + OP_IPRA, + OP_IPOA, + OP_EXPT, + OP_PSHE, + OP_PSHA, + OP_DYNA, + OP_EXPV = 0x5c, + OP_COPY = 0x80, + OP_REVA, + OP_DSC, + OP_ISC, + OP_WAIT, + OP_VS, + OP_CMPA = 0xc0, + OP_VSC, +}; + +struct altera_procinfo { + char *name; + u8 attrs; + struct altera_procinfo *next; +}; + +/* This function checks if enough parameters are available on the stack. */ +static int altera_check_stack(int stack_ptr, int count, int *status) +{ + if (stack_ptr < count) { + *status = -EOVERFLOW; + return 0; + } + + return 1; +} + +static void altera_export_int(char *key, s32 value) +{ + dprintk("Export: key = \"%s\", value = %d\n", key, value); +} + +#define HEX_LINE_CHARS 72 +#define HEX_LINE_BITS (HEX_LINE_CHARS * 4) + +static void altera_export_bool_array(char *key, u8 *data, s32 count) +{ + char string[HEX_LINE_CHARS + 1]; + s32 i, offset; + u32 size, line, lines, linebits, value, j, k; + + if (count > HEX_LINE_BITS) { + dprintk("Export: key = \"%s\", %d bits, value = HEX\n", + key, count); + lines = (count + (HEX_LINE_BITS - 1)) / HEX_LINE_BITS; + + for (line = 0; line < lines; ++line) { + if (line < (lines - 1)) { + linebits = HEX_LINE_BITS; + size = HEX_LINE_CHARS; + offset = count - ((line + 1) * HEX_LINE_BITS); + } else { + linebits = + count - ((lines - 1) * HEX_LINE_BITS); + size = (linebits + 3) / 4; + offset = 0L; + } + + string[size] = '\0'; + j = size - 1; + value = 0; + + for (k = 0; k < linebits; ++k) { + i = k + offset; + if (data[i >> 3] & (1 << (i & 7))) + value |= (1 << (i & 3)); + if ((i & 3) == 3) { + sprintf(&string[j], "%1x", value); + value = 0; + --j; + } + } + if ((k & 3) > 0) + sprintf(&string[j], "%1x", value); + + dprintk("%s\n", string); + } + + } else { + size = (count + 3) / 4; + string[size] = '\0'; + j = size - 1; + value = 0; + + for (i = 0; i < count; ++i) { + if (data[i >> 3] & (1 << (i & 7))) + value |= (1 << (i & 3)); + if ((i & 3) == 3) { + sprintf(&string[j], "%1x", value); + value = 0; + --j; + } + } + if ((i & 3) > 0) + sprintf(&string[j], "%1x", value); + + dprintk("Export: key = \"%s\", %d bits, value = HEX %s\n", + key, count, string); + } +} + +static int altera_execute(struct altera_state *astate, + u8 *p, + s32 program_size, + s32 *error_address, + int *exit_code, + int *format_version) +{ + struct altera_config *aconf = astate->config; + char *msg_buff = astate->msg_buff; + long *stack = astate->stack; + int status = 0; + u32 first_word = 0L; + u32 action_table = 0L; + u32 proc_table = 0L; + u32 str_table = 0L; + u32 sym_table = 0L; + u32 data_sect = 0L; + u32 code_sect = 0L; + u32 debug_sect = 0L; + u32 action_count = 0L; + u32 proc_count = 0L; + u32 sym_count = 0L; + long *vars = NULL; + s32 *var_size = NULL; + char *attrs = NULL; + u8 *proc_attributes = NULL; + u32 pc; + u32 opcode_address; + u32 args[3]; + u32 opcode; + u32 name_id; + u8 charbuf[4]; + long long_tmp; + u32 variable_id; + u8 *charptr_tmp; + u8 *charptr_tmp2; + long *longptr_tmp; + int version = 0; + int delta = 0; + int stack_ptr = 0; + u32 arg_count; + int done = 0; + int bad_opcode = 0; + u32 count; + u32 index; + u32 index2; + s32 long_count; + s32 long_idx; + s32 long_idx2; + u32 i; + u32 j; + u32 uncomp_size; + u32 offset; + u32 value; + int current_proc = 0; + int reverse; + + char *name; + + dprintk("%s\n", __func__); + + /* Read header information */ + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + *format_version = version + 1; + delta = version * 8; + + action_table = get_unaligned_be32(&p[4]); + proc_table = get_unaligned_be32(&p[8]); + str_table = get_unaligned_be32(&p[4 + delta]); + sym_table = get_unaligned_be32(&p[16 + delta]); + data_sect = get_unaligned_be32(&p[20 + delta]); + code_sect = get_unaligned_be32(&p[24 + delta]); + debug_sect = get_unaligned_be32(&p[28 + delta]); + action_count = get_unaligned_be32(&p[40 + delta]); + proc_count = get_unaligned_be32(&p[44 + delta]); + sym_count = get_unaligned_be32(&p[48 + (2 * delta)]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) { + done = 1; + status = -EIO; + goto exit_done; + } + + if (sym_count <= 0) + goto exit_done; + + vars = kzalloc(sym_count * sizeof(long), GFP_KERNEL); + + if (vars == NULL) + status = -ENOMEM; + + if (status == 0) { + var_size = kzalloc(sym_count * sizeof(s32), GFP_KERNEL); + + if (var_size == NULL) + status = -ENOMEM; + } + + if (status == 0) { + attrs = kzalloc(sym_count, GFP_KERNEL); + + if (attrs == NULL) + status = -ENOMEM; + } + + if ((status == 0) && (version > 0)) { + proc_attributes = kzalloc(proc_count, GFP_KERNEL); + + if (proc_attributes == NULL) + status = -ENOMEM; + } + + if (status != 0) + goto exit_done; + + delta = version * 2; + + for (i = 0; i < sym_count; ++i) { + offset = (sym_table + ((11 + delta) * i)); + + value = get_unaligned_be32(&p[offset + 3 + delta]); + + attrs[i] = p[offset]; + + /* + * use bit 7 of attribute byte to indicate that + * this buffer was dynamically allocated + * and should be freed later + */ + attrs[i] &= 0x7f; + + var_size[i] = get_unaligned_be32(&p[offset + 7 + delta]); + + /* + * Attribute bits: + * bit 0: 0 = read-only, 1 = read-write + * bit 1: 0 = not compressed, 1 = compressed + * bit 2: 0 = not initialized, 1 = initialized + * bit 3: 0 = scalar, 1 = array + * bit 4: 0 = Boolean, 1 = integer + * bit 5: 0 = declared variable, + * 1 = compiler created temporary variable + */ + + if ((attrs[i] & 0x0c) == 0x04) + /* initialized scalar variable */ + vars[i] = value; + else if ((attrs[i] & 0x1e) == 0x0e) { + /* initialized compressed Boolean array */ + uncomp_size = get_unaligned_le32(&p[data_sect + value]); + + /* allocate a buffer for the uncompressed data */ + vars[i] = (long)kzalloc(uncomp_size, GFP_KERNEL); + if (vars[i] == 0L) + status = -ENOMEM; + else { + /* set flag so buffer will be freed later */ + attrs[i] |= 0x80; + + /* uncompress the data */ + if (altera_shrink(&p[data_sect + value], + var_size[i], + (u8 *)vars[i], + uncomp_size, + version) != uncomp_size) + /* decompression failed */ + status = -EIO; + else + var_size[i] = uncomp_size * 8L; + + } + } else if ((attrs[i] & 0x1e) == 0x0c) { + /* initialized Boolean array */ + vars[i] = value + data_sect + (long)p; + } else if ((attrs[i] & 0x1c) == 0x1c) { + /* initialized integer array */ + vars[i] = value + data_sect; + } else if ((attrs[i] & 0x0c) == 0x08) { + /* uninitialized array */ + + /* flag attrs so that memory is freed */ + attrs[i] |= 0x80; + + if (var_size[i] > 0) { + u32 size; + + if (attrs[i] & 0x10) + /* integer array */ + size = (var_size[i] * sizeof(s32)); + else + /* Boolean array */ + size = ((var_size[i] + 7L) / 8L); + + vars[i] = (long)kzalloc(size, GFP_KERNEL); + + if (vars[i] == 0) { + status = -ENOMEM; + } else { + /* zero out memory */ + for (j = 0; j < size; ++j) + ((u8 *)(vars[i]))[j] = 0; + + } + } else + vars[i] = 0; + + } else + vars[i] = 0; + + } + +exit_done: + if (status != 0) + done = 1; + + altera_jinit(astate); + + pc = code_sect; + msg_buff[0] = '\0'; + + /* + * For JBC version 2, we will execute the procedures corresponding to + * the selected ACTION + */ + if (version > 0) { + if (aconf->action == NULL) { + status = -EINVAL; + done = 1; + } else { + int action_found = 0; + for (i = 0; (i < action_count) && !action_found; ++i) { + name_id = get_unaligned_be32(&p[action_table + + (12 * i)]); + + name = &p[str_table + name_id]; + + if (strnicmp(aconf->action, name, strlen(name)) == 0) { + action_found = 1; + current_proc = + get_unaligned_be32(&p[action_table + + (12 * i) + 8]); + } + } + + if (!action_found) { + status = -EINVAL; + done = 1; + } + } + + if (status == 0) { + int first_time = 1; + i = current_proc; + while ((i != 0) || first_time) { + first_time = 0; + /* check procedure attribute byte */ + proc_attributes[i] = + (p[proc_table + + (13 * i) + 8] & + 0x03); + + /* + * BIT0 - OPTIONAL + * BIT1 - RECOMMENDED + * BIT6 - FORCED OFF + * BIT7 - FORCED ON + */ + + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + } + + /* + * Set current_proc to the first procedure + * to be executed + */ + i = current_proc; + while ((i != 0) && + ((proc_attributes[i] == 1) || + ((proc_attributes[i] & 0xc0) == 0x40))) { + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + } + + if ((i != 0) || ((i == 0) && (current_proc == 0) && + ((proc_attributes[0] != 1) && + ((proc_attributes[0] & 0xc0) != 0x40)))) { + current_proc = i; + pc = code_sect + + get_unaligned_be32(&p[proc_table + + (13 * i) + 9]); + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + } else + /* there are no procedures to execute! */ + done = 1; + + } + } + + msg_buff[0] = '\0'; + + while (!done) { + opcode = (p[pc] & 0xff); + opcode_address = pc; + ++pc; + + if (debug > 1) + printk("opcode: %02x\n", opcode); + + arg_count = (opcode >> 6) & 3; + for (i = 0; i < arg_count; ++i) { + args[i] = get_unaligned_be32(&p[pc]); + pc += 4; + } + + switch (opcode) { + case OP_NOP: + break; + case OP_DUP: + if (altera_check_stack(stack_ptr, 1, &status)) { + stack[stack_ptr] = stack[stack_ptr - 1]; + ++stack_ptr; + } + break; + case OP_SWP: + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + break; + case OP_ADD: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] += stack[stack_ptr]; + } + break; + case OP_SUB: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] -= stack[stack_ptr]; + } + break; + case OP_MULT: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] *= stack[stack_ptr]; + } + break; + case OP_DIV: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] /= stack[stack_ptr]; + } + break; + case OP_MOD: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] %= stack[stack_ptr]; + } + break; + case OP_SHL: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] <<= stack[stack_ptr]; + } + break; + case OP_SHR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] >>= stack[stack_ptr]; + } + break; + case OP_NOT: + if (altera_check_stack(stack_ptr, 1, &status)) + stack[stack_ptr - 1] ^= (-1L); + + break; + case OP_AND: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] &= stack[stack_ptr]; + } + break; + case OP_OR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] |= stack[stack_ptr]; + } + break; + case OP_XOR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] ^= stack[stack_ptr]; + } + break; + case OP_INV: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + stack[stack_ptr - 1] = stack[stack_ptr - 1] ? 0L : 1L; + break; + case OP_GT: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] > stack[stack_ptr]) ? + 1L : 0L; + + break; + case OP_LT: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] < stack[stack_ptr]) ? + 1L : 0L; + + break; + case OP_RET: + if ((version > 0) && (stack_ptr == 0)) { + /* + * We completed one of the main procedures + * of an ACTION. + * Find the next procedure + * to be executed and jump to it. + * If there are no more procedures, then EXIT. + */ + i = get_unaligned_be32(&p[proc_table + + (13 * current_proc) + 4]); + while ((i != 0) && + ((proc_attributes[i] == 1) || + ((proc_attributes[i] & 0xc0) == 0x40))) + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + + if (i == 0) { + /* no procedures to execute! */ + done = 1; + *exit_code = 0; /* success */ + } else { + current_proc = i; + pc = code_sect + get_unaligned_be32( + &p[proc_table + + (13 * i) + 9]); + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + + } else + if (altera_check_stack(stack_ptr, 1, &status)) { + pc = stack[--stack_ptr] + code_sect; + if ((pc <= code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + + } + + break; + case OP_CMPS: + /* + * Array short compare + * ...stack 0 is source 1 value + * ...stack 1 is source 2 value + * ...stack 2 is mask value + * ...stack 3 is count + */ + if (altera_check_stack(stack_ptr, 4, &status)) { + s32 a = stack[--stack_ptr]; + s32 b = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + + if ((count < 1) || (count > 32)) + status = -ERANGE; + else { + long_tmp &= ((-1L) >> (32 - count)); + + stack[stack_ptr - 1] = + ((a & long_tmp) == (b & long_tmp)) + ? 1L : 0L; + } + } + break; + case OP_PINT: + /* + * PRINT add integer + * ...stack 0 is integer value + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + sprintf(&msg_buff[strlen(msg_buff)], + "%ld", stack[--stack_ptr]); + break; + case OP_PRNT: + /* PRINT finish */ + if (debug) + printk(msg_buff, "\n"); + + msg_buff[0] = '\0'; + break; + case OP_DSS: + /* + * DRSCAN short + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_drscan(astate, count, charbuf, 0); + break; + case OP_DSSC: + /* + * DRSCAN short with capture + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_swap_dr(astate, count, charbuf, + 0, charbuf, 0); + stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]); + break; + case OP_ISS: + /* + * IRSCAN short + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_irscan(astate, count, charbuf, 0); + break; + case OP_ISSC: + /* + * IRSCAN short with capture + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_swap_ir(astate, count, charbuf, + 0, charbuf, 0); + stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]); + break; + case OP_DPR: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + count = stack[--stack_ptr]; + status = altera_set_dr_pre(&astate->js, count, 0, NULL); + break; + case OP_DPRL: + /* + * DRPRE with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_dr_pre(&astate->js, count, 0, + charbuf); + break; + case OP_DPO: + /* + * DRPOST + * ...stack 0 is count + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_dr_post(&astate->js, count, + 0, NULL); + } + break; + case OP_DPOL: + /* + * DRPOST with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_dr_post(&astate->js, count, 0, + charbuf); + break; + case OP_IPR: + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_ir_pre(&astate->js, count, + 0, NULL); + } + break; + case OP_IPRL: + /* + * IRPRE with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (altera_check_stack(stack_ptr, 2, &status)) { + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_ir_pre(&astate->js, count, + 0, charbuf); + } + break; + case OP_IPO: + /* + * IRPOST + * ...stack 0 is count + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_ir_post(&astate->js, count, + 0, NULL); + } + break; + case OP_IPOL: + /* + * IRPOST with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_ir_post(&astate->js, count, 0, + charbuf); + break; + case OP_PCHR: + if (altera_check_stack(stack_ptr, 1, &status)) { + u8 ch; + count = strlen(msg_buff); + ch = (char) stack[--stack_ptr]; + if ((ch < 1) || (ch > 127)) { + /* + * character code out of range + * instead of flagging an error, + * force the value to 127 + */ + ch = 127; + } + msg_buff[count] = ch; + msg_buff[count + 1] = '\0'; + } + break; + case OP_EXIT: + if (altera_check_stack(stack_ptr, 1, &status)) + *exit_code = stack[--stack_ptr]; + + done = 1; + break; + case OP_EQU: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] == stack[stack_ptr]) ? + 1L : 0L; + break; + case OP_POPT: + if (altera_check_stack(stack_ptr, 1, &status)) + --stack_ptr; + + break; + case OP_ABS: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + if (stack[stack_ptr - 1] < 0) + stack[stack_ptr - 1] = 0 - stack[stack_ptr - 1]; + + break; + case OP_BCH0: + /* + * Batch operation 0 + * SWP + * SWPN 7 + * SWP + * SWPN 6 + * DUPN 8 + * SWPN 2 + * SWP + * DUPN 6 + * DUPN 6 + */ + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWPN 7 */ + index = 7 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* DUPN 8 */ + index = 8 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + + /* SWPN 2 */ + index = 2 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* DUPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + + /* DUPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + break; + case OP_PSH0: + stack[stack_ptr++] = 0; + break; + case OP_PSHL: + stack[stack_ptr++] = (s32) args[0]; + break; + case OP_PSHV: + stack[stack_ptr++] = vars[args[0]]; + break; + case OP_JMP: + pc = args[0] + code_sect; + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + break; + case OP_CALL: + stack[stack_ptr++] = pc; + pc = args[0] + code_sect; + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + break; + case OP_NEXT: + /* + * Process FOR / NEXT loop + * ...argument 0 is variable ID + * ...stack 0 is step value + * ...stack 1 is end value + * ...stack 2 is top address + */ + if (altera_check_stack(stack_ptr, 3, &status)) { + s32 step = stack[stack_ptr - 1]; + s32 end = stack[stack_ptr - 2]; + s32 top = stack[stack_ptr - 3]; + s32 iterator = vars[args[0]]; + int break_out = 0; + + if (step < 0) { + if (iterator <= end) + break_out = 1; + } else if (iterator >= end) + break_out = 1; + + if (break_out) { + stack_ptr -= 3; + } else { + vars[args[0]] = iterator + step; + pc = top + code_sect; + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + } + break; + case OP_PSTR: + /* + * PRINT add string + * ...argument 0 is string ID + */ + count = strlen(msg_buff); + strlcpy(&msg_buff[count], + &p[str_table + args[0]], + ALTERA_MESSAGE_LENGTH - count); + break; + case OP_SINT: + /* + * STATE intermediate state + * ...argument 0 is state code + */ + status = altera_goto_jstate(astate, args[0]); + break; + case OP_ST: + /* + * STATE final state + * ...argument 0 is state code + */ + status = altera_goto_jstate(astate, args[0]); + break; + case OP_ISTP: + /* + * IRSTOP state + * ...argument 0 is state code + */ + status = altera_set_irstop(&astate->js, args[0]); + break; + case OP_DSTP: + /* + * DRSTOP state + * ...argument 0 is state code + */ + status = altera_set_drstop(&astate->js, args[0]); + break; + + case OP_SWPN: + /* + * Exchange top with Nth stack value + * ...argument 0 is 0-based stack entry + * to swap with top element + */ + index = (args[0]) + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + break; + case OP_DUPN: + /* + * Duplicate Nth stack value + * ...argument 0 is 0-based stack entry to duplicate + */ + index = (args[0]) + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + break; + case OP_POPV: + /* + * Pop stack into scalar variable + * ...argument 0 is variable ID + * ...stack 0 is value + */ + if (altera_check_stack(stack_ptr, 1, &status)) + vars[args[0]] = stack[--stack_ptr]; + + break; + case OP_POPE: + /* + * Pop stack into integer array element + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is value + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + variable_id = args[0]; + + /* + * If variable is read-only, + * convert to writable array + */ + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x1c)) { + /* Allocate a writable buffer for this array */ + count = var_size[variable_id]; + long_tmp = vars[variable_id]; + longptr_tmp = kzalloc(count * sizeof(long), + GFP_KERNEL); + vars[variable_id] = (long)longptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* copy previous contents into buffer */ + for (i = 0; i < count; ++i) { + longptr_tmp[i] = + get_unaligned_be32(&p[long_tmp]); + long_tmp += sizeof(long); + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + /* check that variable is a writable integer array */ + if ((attrs[variable_id] & 0x1c) != 0x18) + status = -ERANGE; + else { + longptr_tmp = (long *)vars[variable_id]; + + /* pop the array index */ + index = stack[--stack_ptr]; + + /* pop the value and store it into the array */ + longptr_tmp[index] = stack[--stack_ptr]; + } + + break; + case OP_POPA: + /* + * Pop stack into Boolean array + * ...argument 0 is variable ID + * ...stack 0 is count + * ...stack 1 is array index + * ...stack 2 is value + */ + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + variable_id = args[0]; + + /* + * If variable is read-only, + * convert to writable array + */ + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; + long_idx < long_tmp; + ++long_idx) { + charptr_tmp[long_idx] = 0; + } + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) { + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + } + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + /* + * check that variable is + * a writable Boolean array + */ + if ((attrs[variable_id] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + charptr_tmp = (u8 *)vars[variable_id]; + + /* pop the count (number of bits to copy) */ + long_count = stack[--stack_ptr]; + + /* pop the array index */ + long_idx = stack[--stack_ptr]; + + reverse = 0; + + if (version > 0) { + /* + * stack 0 = array right index + * stack 1 = array left index + */ + + if (long_idx > long_count) { + reverse = 1; + long_tmp = long_count; + long_count = 1 + long_idx - + long_count; + long_idx = long_tmp; + + /* reverse POPA is not supported */ + status = -ERANGE; + break; + } else + long_count = 1 + long_count - + long_idx; + + } + + /* pop the data */ + long_tmp = stack[--stack_ptr]; + + if (long_count < 1) { + status = -ERANGE; + break; + } + + for (i = 0; i < long_count; ++i) { + if (long_tmp & (1L << (s32) i)) + charptr_tmp[long_idx >> 3L] |= + (1L << (long_idx & 7L)); + else + charptr_tmp[long_idx >> 3L] &= + ~(1L << (long_idx & 7L)); + + ++long_idx; + } + + break; + case OP_JMPZ: + /* + * Pop stack and branch if zero + * ...argument 0 is address + * ...stack 0 is condition value + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + if (stack[--stack_ptr] == 0) { + pc = args[0] + code_sect; + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + } + break; + case OP_DS: + case OP_IS: + /* + * DRSCAN + * IRSCAN + * ...argument 0 is scan data variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_idx = stack[--stack_ptr]; + long_count = stack[--stack_ptr]; + reverse = 0; + if (version > 0) { + /* + * stack 0 = array right index + * stack 1 = array left index + * stack 2 = count + */ + long_tmp = long_count; + long_count = stack[--stack_ptr]; + + if (long_idx > long_tmp) { + reverse = 1; + long_idx = long_tmp; + } + } + + charptr_tmp = (u8 *)vars[args[0]]; + + if (reverse) { + /* + * allocate a buffer + * and reverse the data order + */ + charptr_tmp2 = charptr_tmp; + charptr_tmp = kzalloc((long_count >> 3) + 1, + GFP_KERNEL); + if (charptr_tmp == NULL) { + status = -ENOMEM; + break; + } + + long_tmp = long_idx + long_count - 1; + long_idx2 = 0; + while (long_idx2 < long_count) { + if (charptr_tmp2[long_tmp >> 3] & + (1 << (long_tmp & 7))) + charptr_tmp[long_idx2 >> 3] |= + (1 << (long_idx2 & 7)); + else + charptr_tmp[long_idx2 >> 3] &= + ~(1 << (long_idx2 & 7)); + + --long_tmp; + ++long_idx2; + } + } + + if (opcode == 0x51) /* DS */ + status = altera_drscan(astate, long_count, + charptr_tmp, long_idx); + else /* IS */ + status = altera_irscan(astate, long_count, + charptr_tmp, long_idx); + + if (reverse) + kfree(charptr_tmp); + + break; + case OP_DPRA: + /* + * DRPRE with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_dr_pre(&astate->js, count, index, + charptr_tmp); + break; + case OP_DPOA: + /* + * DRPOST with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_dr_post(&astate->js, count, index, + charptr_tmp); + break; + case OP_IPRA: + /* + * IRPRE with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_ir_pre(&astate->js, count, index, + charptr_tmp); + + break; + case OP_IPOA: + /* + * IRPOST with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_ir_post(&astate->js, count, index, + charptr_tmp); + + break; + case OP_EXPT: + /* + * EXPORT + * ...argument 0 is string ID + * ...stack 0 is integer expression + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + name = &p[str_table + args[0]]; + long_tmp = stack[--stack_ptr]; + altera_export_int(name, long_tmp); + } + break; + case OP_PSHE: + /* + * Push integer array element + * ...argument 0 is variable ID + * ...stack 0 is array index + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + variable_id = args[0]; + index = stack[stack_ptr - 1]; + + /* check variable type */ + if ((attrs[variable_id] & 0x1f) == 0x19) { + /* writable integer array */ + longptr_tmp = (long *)vars[variable_id]; + stack[stack_ptr - 1] = longptr_tmp[index]; + } else if ((attrs[variable_id] & 0x1f) == 0x1c) { + /* read-only integer array */ + long_tmp = vars[variable_id] + + (index * sizeof(long)); + stack[stack_ptr - 1] = + get_unaligned_be32(&p[long_tmp]); + } else + status = -ERANGE; + + break; + case OP_PSHA: + /* + * Push Boolean array + * ...argument 0 is variable ID + * ...stack 0 is count + * ...stack 1 is array index + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + variable_id = args[0]; + + /* check that variable is a Boolean array */ + if ((attrs[variable_id] & 0x18) != 0x08) { + status = -ERANGE; + break; + } + + charptr_tmp = (u8 *)vars[variable_id]; + + /* pop the count (number of bits to copy) */ + count = stack[--stack_ptr]; + + /* pop the array index */ + index = stack[stack_ptr - 1]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + if ((count < 1) || (count > 32)) { + status = -ERANGE; + break; + } + + long_tmp = 0L; + + for (i = 0; i < count; ++i) + if (charptr_tmp[(i + index) >> 3] & + (1 << ((i + index) & 7))) + long_tmp |= (1L << i); + + stack[stack_ptr - 1] = long_tmp; + + break; + case OP_DYNA: + /* + * Dynamically change size of array + * ...argument 0 is variable ID + * ...stack 0 is new size + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + variable_id = args[0]; + long_tmp = stack[--stack_ptr]; + + if (long_tmp > var_size[variable_id]) { + var_size[variable_id] = long_tmp; + + if (attrs[variable_id] & 0x10) + /* allocate integer array */ + long_tmp *= sizeof(long); + else + /* allocate Boolean array */ + long_tmp = (long_tmp + 7) >> 3; + + /* + * If the buffer was previously allocated, + * free it + */ + if (attrs[variable_id] & 0x80) { + kfree((void *)vars[variable_id]); + vars[variable_id] = 0; + } + + /* + * Allocate a new buffer + * of the requested size + */ + vars[variable_id] = (long) + kzalloc(long_tmp, GFP_KERNEL); + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* + * Set the attribute bit to indicate that + * this buffer was dynamically allocated and + * should be freed later + */ + attrs[variable_id] |= 0x80; + + /* zero out memory */ + count = ((var_size[variable_id] + 7L) / + 8L); + charptr_tmp = (u8 *)(vars[variable_id]); + for (index = 0; index < count; ++index) + charptr_tmp[index] = 0; + + } + + break; + case OP_EXPV: + /* + * Export Boolean array + * ...argument 0 is string ID + * ...stack 0 is variable ID + * ...stack 1 is array right index + * ...stack 2 is array left index + */ + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + if (version == 0) { + /* EXPV is not supported in JBC 1.0 */ + bad_opcode = 1; + break; + } + name = &p[str_table + args[0]]; + variable_id = stack[--stack_ptr]; + long_idx = stack[--stack_ptr];/* right indx */ + long_idx2 = stack[--stack_ptr];/* left indx */ + + if (long_idx > long_idx2) { + /* reverse indices not supported */ + status = -ERANGE; + break; + } + + long_count = 1 + long_idx2 - long_idx; + + charptr_tmp = (u8 *)vars[variable_id]; + charptr_tmp2 = NULL; + + if ((long_idx & 7L) != 0) { + s32 k = long_idx; + charptr_tmp2 = + kzalloc(((long_count + 7L) / 8L), + GFP_KERNEL); + if (charptr_tmp2 == NULL) { + status = -ENOMEM; + break; + } + + for (i = 0; i < long_count; ++i) { + if (charptr_tmp[k >> 3] & + (1 << (k & 7))) + charptr_tmp2[i >> 3] |= + (1 << (i & 7)); + else + charptr_tmp2[i >> 3] &= + ~(1 << (i & 7)); + + ++k; + } + charptr_tmp = charptr_tmp2; + + } else if (long_idx != 0) + charptr_tmp = &charptr_tmp[long_idx >> 3]; + + altera_export_bool_array(name, charptr_tmp, + long_count); + + /* free allocated buffer */ + if ((long_idx & 7L) != 0) + kfree(charptr_tmp2); + + break; + case OP_COPY: { + /* + * Array copy + * ...argument 0 is dest ID + * ...argument 1 is source ID + * ...stack 0 is count + * ...stack 1 is dest index + * ...stack 2 is source index + */ + s32 copy_count; + s32 copy_index; + s32 copy_index2; + s32 destleft; + s32 src_count; + s32 dest_count; + int src_reverse = 0; + int dest_reverse = 0; + + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + + copy_count = stack[--stack_ptr]; + copy_index = stack[--stack_ptr]; + copy_index2 = stack[--stack_ptr]; + reverse = 0; + + if (version > 0) { + /* + * stack 0 = source right index + * stack 1 = source left index + * stack 2 = destination right index + * stack 3 = destination left index + */ + destleft = stack[--stack_ptr]; + + if (copy_count > copy_index) { + src_reverse = 1; + reverse = 1; + src_count = 1 + copy_count - copy_index; + /* copy_index = source start index */ + } else { + src_count = 1 + copy_index - copy_count; + /* source start index */ + copy_index = copy_count; + } + + if (copy_index2 > destleft) { + dest_reverse = 1; + reverse = !reverse; + dest_count = 1 + copy_index2 - destleft; + /* destination start index */ + copy_index2 = destleft; + } else + dest_count = 1 + destleft - copy_index2; + + copy_count = (src_count < dest_count) ? + src_count : dest_count; + + if ((src_reverse || dest_reverse) && + (src_count != dest_count)) + /* + * If either the source or destination + * is reversed, we can't tolerate + * a length mismatch, because we + * "left justify" arrays when copying. + * This won't work correctly + * with reversed arrays. + */ + status = -ERANGE; + + } + + count = copy_count; + index = copy_index; + index2 = copy_index2; + + /* + * If destination is a read-only array, + * allocate a buffer and convert it to a writable array + */ + variable_id = args[1]; + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; long_idx < long_tmp; + ++long_idx) + charptr_tmp[long_idx] = 0; + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + + } + + /* + set bit 7 - buffer was dynamically allocated */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + } + + charptr_tmp = (u8 *)vars[args[1]]; + charptr_tmp2 = (u8 *)vars[args[0]]; + + /* check if destination is a writable Boolean array */ + if ((attrs[args[1]] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + if (count < 1) { + status = -ERANGE; + break; + } + + if (reverse) + index2 += (count - 1); + + for (i = 0; i < count; ++i) { + if (charptr_tmp2[index >> 3] & + (1 << (index & 7))) + charptr_tmp[index2 >> 3] |= + (1 << (index2 & 7)); + else + charptr_tmp[index2 >> 3] &= + ~(1 << (index2 & 7)); + + ++index; + if (reverse) + --index2; + else + ++index2; + } + + break; + } + case OP_DSC: + case OP_ISC: { + /* + * DRSCAN with capture + * IRSCAN with capture + * ...argument 0 is scan data variable ID + * ...argument 1 is capture variable ID + * ...stack 0 is capture index + * ...stack 1 is scan data index + * ...stack 2 is count + */ + s32 scan_right, scan_left; + s32 capture_count = 0; + s32 scan_count = 0; + s32 capture_index; + s32 scan_index; + + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + + capture_index = stack[--stack_ptr]; + scan_index = stack[--stack_ptr]; + + if (version > 0) { + /* + * stack 0 = capture right index + * stack 1 = capture left index + * stack 2 = scan right index + * stack 3 = scan left index + * stack 4 = count + */ + scan_right = stack[--stack_ptr]; + scan_left = stack[--stack_ptr]; + capture_count = 1 + scan_index - capture_index; + scan_count = 1 + scan_left - scan_right; + scan_index = scan_right; + } + + long_count = stack[--stack_ptr]; + /* + * If capture array is read-only, allocate a buffer + * and convert it to a writable array + */ + variable_id = args[1]; + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; long_idx < long_tmp; + ++long_idx) + charptr_tmp[long_idx] = 0; + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + charptr_tmp = (u8 *)vars[args[0]]; + charptr_tmp2 = (u8 *)vars[args[1]]; + + if ((version > 0) && + ((long_count > capture_count) || + (long_count > scan_count))) { + status = -ERANGE; + break; + } + + /* + * check that capture array + * is a writable Boolean array + */ + if ((attrs[args[1]] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + if (status == 0) { + if (opcode == 0x82) /* DSC */ + status = altera_swap_dr(astate, + long_count, + charptr_tmp, + scan_index, + charptr_tmp2, + capture_index); + else /* ISC */ + status = altera_swap_ir(astate, + long_count, + charptr_tmp, + scan_index, + charptr_tmp2, + capture_index); + + } + + break; + } + case OP_WAIT: + /* + * WAIT + * ...argument 0 is wait state + * ...argument 1 is end state + * ...stack 0 is cycles + * ...stack 1 is microseconds + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + + if (long_tmp != 0L) + status = altera_wait_cycles(astate, long_tmp, + args[0]); + + long_tmp = stack[--stack_ptr]; + + if ((status == 0) && (long_tmp != 0L)) + status = altera_wait_msecs(astate, + long_tmp, + args[0]); + + if ((status == 0) && (args[1] != args[0])) + status = altera_goto_jstate(astate, + args[1]); + + if (version > 0) { + --stack_ptr; /* throw away MAX cycles */ + --stack_ptr; /* throw away MAX microseconds */ + } + break; + case OP_CMPA: { + /* + * Array compare + * ...argument 0 is source 1 ID + * ...argument 1 is source 2 ID + * ...argument 2 is mask ID + * ...stack 0 is source 1 index + * ...stack 1 is source 2 index + * ...stack 2 is mask index + * ...stack 3 is count + */ + s32 a, b; + u8 *source1 = (u8 *)vars[args[0]]; + u8 *source2 = (u8 *)vars[args[1]]; + u8 *mask = (u8 *)vars[args[2]]; + u32 index1; + u32 index2; + u32 mask_index; + + if (!altera_check_stack(stack_ptr, 4, &status)) + break; + + index1 = stack[--stack_ptr]; + index2 = stack[--stack_ptr]; + mask_index = stack[--stack_ptr]; + long_count = stack[--stack_ptr]; + + if (version > 0) { + /* + * stack 0 = source 1 right index + * stack 1 = source 1 left index + * stack 2 = source 2 right index + * stack 3 = source 2 left index + * stack 4 = mask right index + * stack 5 = mask left index + */ + s32 mask_right = stack[--stack_ptr]; + s32 mask_left = stack[--stack_ptr]; + /* source 1 count */ + a = 1 + index2 - index1; + /* source 2 count */ + b = 1 + long_count - mask_index; + a = (a < b) ? a : b; + /* mask count */ + b = 1 + mask_left - mask_right; + a = (a < b) ? a : b; + /* source 2 start index */ + index2 = mask_index; + /* mask start index */ + mask_index = mask_right; + long_count = a; + } + + long_tmp = 1L; + + if (long_count < 1) + status = -ERANGE; + else { + count = long_count; + + for (i = 0; i < count; ++i) { + if (mask[mask_index >> 3] & + (1 << (mask_index & 7))) { + a = source1[index1 >> 3] & + (1 << (index1 & 7)) + ? 1 : 0; + b = source2[index2 >> 3] & + (1 << (index2 & 7)) + ? 1 : 0; + + if (a != b) /* failure */ + long_tmp = 0L; + } + ++index1; + ++index2; + ++mask_index; + } + } + + stack[stack_ptr++] = long_tmp; + + break; + } + default: + /* Unrecognized opcode -- ERROR! */ + bad_opcode = 1; + break; + } + + if (bad_opcode) + status = -ENOSYS; + + if ((stack_ptr < 0) || (stack_ptr >= ALTERA_STACK_SIZE)) + status = -EOVERFLOW; + + if (status != 0) { + done = 1; + *error_address = (s32)(opcode_address - code_sect); + } + } + + altera_free_buffers(astate); + + /* Free all dynamically allocated arrays */ + if ((attrs != NULL) && (vars != NULL)) + for (i = 0; i < sym_count; ++i) + if (attrs[i] & 0x80) + kfree((void *)vars[i]); + + kfree(vars); + kfree(var_size); + kfree(attrs); + kfree(proc_attributes); + + return status; +} + +static int altera_get_note(u8 *p, s32 program_size, + s32 *offset, char *key, char *value, int length) +/* + * Gets key and value of NOTE fields in the JBC file. + * Can be called in two modes: if offset pointer is NULL, + * then the function searches for note fields which match + * the key string provided. If offset is not NULL, then + * the function finds the next note field of any key, + * starting at the offset specified by the offset pointer. + * Returns 0 for success, else appropriate error code + */ +{ + int status = -ENODATA; + u32 note_strings = 0L; + u32 note_table = 0L; + u32 note_count = 0L; + u32 first_word = 0L; + int version = 0; + int delta = 0; + char *key_ptr; + char *value_ptr; + int i; + + /* Read header information */ + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + delta = version * 8; + + note_strings = get_unaligned_be32(&p[8 + delta]); + note_table = get_unaligned_be32(&p[12 + delta]); + note_count = get_unaligned_be32(&p[44 + (2 * delta)]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) + return -EIO; + + if (note_count <= 0L) + return status; + + if (offset == NULL) { + /* + * We will search for the first note with a specific key, + * and return only the value + */ + for (i = 0; (i < note_count) && + (status != 0); ++i) { + key_ptr = &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i)])]; + if ((strnicmp(key, key_ptr, strlen(key_ptr)) == 0) && + (key != NULL)) { + status = 0; + + value_ptr = &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i) + 4])]; + + if (value != NULL) + strlcpy(value, value_ptr, length); + + } + } + } else { + /* + * We will search for the next note, regardless of the key, + * and return both the value and the key + */ + + i = *offset; + + if ((i >= 0) && (i < note_count)) { + status = 0; + + if (key != NULL) + strlcpy(key, &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i)])], + length); + + if (value != NULL) + strlcpy(value, &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i) + 4])], + length); + + *offset = i + 1; + } + } + + return status; +} + +static int altera_check_crc(u8 *p, s32 program_size) +{ + int status = 0; + u16 local_expected = 0, + local_actual = 0, + shift_reg = 0xffff; + int bit, feedback; + u8 databyte; + u32 i; + u32 crc_section = 0L; + u32 first_word = 0L; + int version = 0; + int delta = 0; + + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + delta = version * 8; + + crc_section = get_unaligned_be32(&p[32 + delta]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) + status = -EIO; + + if (crc_section >= program_size) + status = -EIO; + + if (status == 0) { + local_expected = (u16)get_unaligned_be16(&p[crc_section]); + + for (i = 0; i < crc_section; ++i) { + databyte = p[i]; + for (bit = 0; bit < 8; bit++) { + feedback = (databyte ^ shift_reg) & 0x01; + shift_reg >>= 1; + if (feedback) + shift_reg ^= 0x8408; + + databyte >>= 1; + } + } + + local_actual = (u16)~shift_reg; + + if (local_expected != local_actual) + status = -EILSEQ; + + } + + if (debug || status) { + switch (status) { + case 0: + printk(KERN_INFO "%s: CRC matched: %04x\n", __func__, + local_actual); + break; + case -EILSEQ: + printk(KERN_ERR "%s: CRC mismatch: expected %04x, " + "actual %04x\n", __func__, local_expected, + local_actual); + break; + case -ENODATA: + printk(KERN_ERR "%s: expected CRC not found, " + "actual CRC = %04x\n", __func__, + local_actual); + break; + case -EIO: + printk(KERN_ERR "%s: error: format isn't " + "recognized.\n", __func__); + break; + default: + printk(KERN_ERR "%s: CRC function returned error " + "code %d\n", __func__, status); + break; + } + } + + return status; +} + +static int altera_get_file_info(u8 *p, + s32 program_size, + int *format_version, + int *action_count, + int *procedure_count) +{ + int status = -EIO; + u32 first_word = 0; + int version = 0; + + if (program_size <= 52L) + return status; + + first_word = get_unaligned_be32(&p[0]); + + if ((first_word == 0x4A414D00L) || (first_word == 0x4A414D01L)) { + status = 0; + + version = (first_word & 1L); + *format_version = version + 1; + + if (version > 0) { + *action_count = get_unaligned_be32(&p[48]); + *procedure_count = get_unaligned_be32(&p[52]); + } + } + + return status; +} + +static int altera_get_act_info(u8 *p, + s32 program_size, + int index, + char **name, + char **description, + struct altera_procinfo **proc_list) +{ + int status = -EIO; + struct altera_procinfo *procptr = NULL; + struct altera_procinfo *tmpptr = NULL; + u32 first_word = 0L; + u32 action_table = 0L; + u32 proc_table = 0L; + u32 str_table = 0L; + u32 note_strings = 0L; + u32 action_count = 0L; + u32 proc_count = 0L; + u32 act_name_id = 0L; + u32 act_desc_id = 0L; + u32 act_proc_id = 0L; + u32 act_proc_name = 0L; + u8 act_proc_attribute = 0; + + if (program_size <= 52L) + return status; + /* Read header information */ + first_word = get_unaligned_be32(&p[0]); + + if (first_word != 0x4A414D01L) + return status; + + action_table = get_unaligned_be32(&p[4]); + proc_table = get_unaligned_be32(&p[8]); + str_table = get_unaligned_be32(&p[12]); + note_strings = get_unaligned_be32(&p[16]); + action_count = get_unaligned_be32(&p[48]); + proc_count = get_unaligned_be32(&p[52]); + + if (index >= action_count) + return status; + + act_name_id = get_unaligned_be32(&p[action_table + (12 * index)]); + act_desc_id = get_unaligned_be32(&p[action_table + (12 * index) + 4]); + act_proc_id = get_unaligned_be32(&p[action_table + (12 * index) + 8]); + + *name = &p[str_table + act_name_id]; + + if (act_desc_id < (note_strings - str_table)) + *description = &p[str_table + act_desc_id]; + + do { + act_proc_name = get_unaligned_be32( + &p[proc_table + (13 * act_proc_id)]); + act_proc_attribute = + (p[proc_table + (13 * act_proc_id) + 8] & 0x03); + + procptr = (struct altera_procinfo *) + kzalloc(sizeof(struct altera_procinfo), + GFP_KERNEL); + + if (procptr == NULL) + status = -ENOMEM; + else { + procptr->name = &p[str_table + act_proc_name]; + procptr->attrs = act_proc_attribute; + procptr->next = NULL; + + /* add record to end of linked list */ + if (*proc_list == NULL) + *proc_list = procptr; + else { + tmpptr = *proc_list; + while (tmpptr->next != NULL) + tmpptr = tmpptr->next; + tmpptr->next = procptr; + } + } + + act_proc_id = get_unaligned_be32( + &p[proc_table + (13 * act_proc_id) + 4]); + } while ((act_proc_id != 0) && (act_proc_id < proc_count)); + + return status; +} + +int altera_init(struct altera_config *config, const struct firmware *fw) +{ + struct altera_state *astate = NULL; + struct altera_procinfo *proc_list = NULL; + struct altera_procinfo *procptr = NULL; + char *key = NULL; + char *value = NULL; + char *action_name = NULL; + char *description = NULL; + int exec_result = 0; + int exit_code = 0; + int format_version = 0; + int action_count = 0; + int procedure_count = 0; + int index = 0; + s32 offset = 0L; + s32 error_address = 0L; + + key = kzalloc(33 * sizeof(char), GFP_KERNEL); + if (!key) + return -ENOMEM; + value = kzalloc(257 * sizeof(char), GFP_KERNEL); + if (!value) + return -ENOMEM; + astate = kzalloc(sizeof(struct altera_state), GFP_KERNEL); + if (!astate) + return -ENOMEM; + + astate->config = config; + if (!astate->config->jtag_io) { + dprintk(KERN_INFO "%s: using byteblaster!\n", __func__); + astate->config->jtag_io = netup_jtag_io_lpt; + } + + altera_check_crc((u8 *)fw->data, fw->size); + + if (debug) { + altera_get_file_info((u8 *)fw->data, fw->size, &format_version, + &action_count, &procedure_count); + printk(KERN_INFO "%s: File format is %s ByteCode format\n", + __func__, (format_version == 2) ? "Jam STAPL" : + "pre-standardized Jam 1.1"); + while (altera_get_note((u8 *)fw->data, fw->size, + &offset, key, value, 256) == 0) + printk(KERN_INFO "%s: NOTE \"%s\" = \"%s\"\n", + __func__, key, value); + } + + if (debug && (format_version == 2) && (action_count > 0)) { + printk(KERN_INFO "%s: Actions available:\n", __func__); + for (index = 0; index < action_count; ++index) { + altera_get_act_info((u8 *)fw->data, fw->size, + index, &action_name, + &description, + &proc_list); + + if (description == NULL) + printk(KERN_INFO "%s: %s\n", + __func__, + action_name); + else + printk(KERN_INFO "%s: %s \"%s\"\n", + __func__, + action_name, + description); + + procptr = proc_list; + while (procptr != NULL) { + if (procptr->attrs != 0) + printk(KERN_INFO "%s: %s (%s)\n", + __func__, + procptr->name, + (procptr->attrs == 1) ? + "optional" : "recommended"); + + proc_list = procptr->next; + kfree(procptr); + procptr = proc_list; + } + } + + printk(KERN_INFO "\n"); + } + + exec_result = altera_execute(astate, (u8 *)fw->data, fw->size, + &error_address, &exit_code, &format_version); + + if (exit_code) + exec_result = -EREMOTEIO; + + if ((format_version == 2) && (exec_result == -EINVAL)) { + if (astate->config->action == NULL) + printk(KERN_ERR "%s: error: no action specified for " + "Jam STAPL file.\nprogram terminated.\n", + __func__); + else + printk(KERN_ERR "%s: error: action \"%s\"" + " is not supported " + "for this Jam STAPL file.\n" + "Program terminated.\n", __func__, + astate->config->action); + + } else if (exec_result) + printk(KERN_ERR "%s: error %d\n", __func__, exec_result); + + kfree(key); + kfree(value); + kfree(astate); + + return 0; +} +EXPORT_SYMBOL(altera_init); diff --git a/include/staging/altera.h b/include/staging/altera.h new file mode 100644 index 000000000000..94c0c6181daf --- /dev/null +++ b/include/staging/altera.h @@ -0,0 +1,49 @@ +/* + * altera.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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. + */ + +#ifndef _ALTERA_H_ +#define _ALTERA_H_ + +struct altera_config { + void *dev; + u8 *action; + int (*jtag_io) (void *dev, int tms, int tdi, int tdo); +}; + +#if defined(CONFIG_ALTERA_STAPL) || \ + (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE)) + +extern int altera_init(struct altera_config *config, const struct firmware *fw); +#else + +static inline int altera_init(struct altera_config *config, + const struct firmware *fw) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} +#endif /* CONFIG_ALTERA_STAPL */ + +#endif /* _ALTERA_H_ */ -- cgit v1.2.3 From 1f65dd365831ff6836eeaf66c9d7bf1ae46ac4d2 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Wed, 2 Feb 2011 12:15:52 -0300 Subject: [media] tm6000: add new TV cards of Beholder Add two new TV cards of Beholder. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-cards.c | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 22a222241cc4..bd99ca7b9f28 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -50,6 +50,8 @@ #define TM6010_BOARD_BEHOLD_VOYAGER 11 #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 #define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 #define TM6000_MAXBOARDS 16 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; @@ -303,6 +305,40 @@ struct tm6000_board tm6000_boards[] = { .dvb_led = TM6010_GPIO_5, .ir = TM6010_GPIO_0, }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, } }; @@ -325,6 +361,8 @@ struct usb_device_id tm6000_id_table[] = { { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, { }, }; @@ -346,6 +384,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); break; @@ -362,6 +402,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); break; @@ -520,6 +562,7 @@ int tm6000_cards_setup(struct tm6000_core *dev) msleep(15); break; case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: /* Power led on (blue) */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); msleep(15); @@ -530,6 +573,7 @@ int tm6000_cards_setup(struct tm6000_core *dev) msleep(15); break; case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: /* Power led on (blue) */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); msleep(15); @@ -955,6 +999,8 @@ static void tm6000_usb_disconnect(struct usb_interface *interface) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: /* Power led off */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); -- cgit v1.2.3 From 6ca000479fdb80b9059b69c67c82f8dc0d5f5d47 Mon Sep 17 00:00:00 2001 From: Holger Nelson Date: Thu, 3 Feb 2011 01:59:29 -0300 Subject: [media] tm6000: Add support for Terratec Grabster AV 150/250 MX This patch adds support for Terratec Grabster AV 150/250 MX. For now it is only possible to use composite input as switching inputs does not work. [mchehab@redhat.com: Fix merge conflicts and simplify board entries] Signed-off-by: Holger Nelson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-cards.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index bd99ca7b9f28..290037eecd61 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -52,6 +52,7 @@ #define TM6010_BOARD_TWINHAN_TU501 13 #define TM6010_BOARD_BEHOLD_WANDER_LITE 14 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 #define TM6000_MAXBOARDS 16 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; @@ -283,6 +284,11 @@ struct tm6000_board tm6000_boards[] = { }, .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + }, [TM6010_BOARD_TWINHAN_TU501] = { .name = "Twinhan TU501(704D1)", .tuner_type = TUNER_XC2028, /* has a XC3028 */ @@ -339,7 +345,7 @@ struct tm6000_board tm6000_boards[] = { .tuner_reset = TM6010_GPIO_0, .power_led = TM6010_GPIO_6, }, - } + }, }; /* table of devices that work with this driver */ @@ -357,6 +363,7 @@ struct usb_device_id tm6000_id_table[] = { { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, -- cgit v1.2.3 From 8aff8ba95155dff265de5ea6f5d5d92caa7bc728 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 17 Feb 2011 22:11:05 -0300 Subject: [media] tm6000: add radio support to the driver Changes: Add function tm6000_set_reg_mask for change some bits in regs. Very usefull, simplify some code with this function. Add control mute Add control volume Add control audio input MUX Add support radio Radio works well. TV works too Known bugs: The programm gnomeradio can't set freq for radio, it use old v4l API. Audio over USB works via arecord. The programm mplayer can set freq but no any audio mplayer -v -rawaudio rate=48000 radio://105.2/capture driver=v4l2:alsa:adevice=hw.1,0:amode=1:audiorate=48000:forceaudio:immediatemode=0 When start watch TV very shortly after radio the kernel crashed hardly. Didn't stop all USB URBs, need some time for stop. [mchehab@redhat.com: fix merge conflicts] Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-alsa.c | 13 +- drivers/staging/tm6000/tm6000-cards.c | 11 +- drivers/staging/tm6000/tm6000-core.c | 260 +++++++++++++++++++++++---- drivers/staging/tm6000/tm6000-stds.c | 33 ++-- drivers/staging/tm6000/tm6000-video.c | 327 ++++++++++++++++++++++++++++++---- drivers/staging/tm6000/tm6000.h | 23 ++- 6 files changed, 566 insertions(+), 101 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c index 184cc505ed86..acb03172a887 100644 --- a/drivers/staging/tm6000/tm6000-alsa.c +++ b/drivers/staging/tm6000/tm6000-alsa.c @@ -76,14 +76,11 @@ MODULE_PARM_DESC(debug, "enable debug messages"); static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) { struct tm6000_core *core = chip->core; - int val; dprintk(1, "Starting audio DMA\n"); /* Enables audio */ - val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); - val |= 0x20; - tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40); tm6000_set_audio_bitrate(core, 48000); @@ -98,13 +95,11 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) { struct tm6000_core *core = chip->core; - int val; + dprintk(1, "Stopping audio DMA\n"); - /* Enables audio */ - val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); - val &= ~0x20; - tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40); tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0); diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 290037eecd61..a356ba7aa77c 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -66,6 +66,8 @@ struct tm6000_board { char *name; struct tm6000_capabilities caps; + enum tm6000_inaudio aradio; + enum tm6000_inaudio avideo; enum tm6000_devtype type; /* variant of the chipset */ int tuner_type; /* type of the tuner */ @@ -230,6 +232,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_addr = 0xc2 >> 1, .demod_addr = 0x1e >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 1, @@ -248,6 +252,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_type = TUNER_XC5000, .tuner_addr = 0xc2 >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 0, @@ -693,13 +699,12 @@ static void tm6000_config_tuner(struct tm6000_core *dev) struct xc5000_config ctl = { .i2c_address = dev->tuner_addr, .if_khz = 4570, - .radio_input = XC5000_RADIO_FM1, + .radio_input = XC5000_RADIO_FM1_MONO, }; xc5000_cfg.tuner = TUNER_XC5000; xc5000_cfg.priv = &ctl; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc5000_cfg); } @@ -732,6 +737,8 @@ static int tm6000_init_dev(struct tm6000_core *dev) dev->caps = tm6000_boards[dev->model].caps; + dev->avideo = tm6000_boards[dev->model].avideo; + dev->aradio = tm6000_boards[dev->model].aradio; /* initialize hardware */ rc = tm6000_init(dev); if (rc < 0) diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c index 5162cd4a8288..778e53413afb 100644 --- a/drivers/staging/tm6000/tm6000-core.c +++ b/drivers/staging/tm6000/tm6000-core.c @@ -116,6 +116,29 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) } EXPORT_SYMBOL_GPL(tm6000_get_reg); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == index) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) { int rc; @@ -245,17 +268,12 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) struct v4l2_frequency f; if (dev->dev_type == TM6010) { - int val; - /* Enable video */ - val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0); - val |= 0x60; - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); - val = tm6000_get_reg(dev, - TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0); - val &= ~0x40; - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val); + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, + 0x60, 0x60); + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc); } else { @@ -471,6 +489,14 @@ struct reg_init tm6010_init_tab[] = { { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00}, + { TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80}, + { TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a}, + { TM6010_REQ08_R0D_A_AMD_THRES, 0x40}, + { TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64}, + { TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20}, + { TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe}, + { TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01}, { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, { TM6010_REQ07_R3F_RESET, 0x01 }, @@ -591,51 +617,213 @@ int tm6000_init(struct tm6000_core *dev) int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) { - int val; + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + /* enable I2S, if we use sif or external I2S device */ if (dev->dev_type == TM6010) { - val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0); - if (val < 0) - return val; - val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */ - val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val); + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); if (val < 0) return val; - } - /* different reg's to set audio bitrate */ - if (dev->dev_type == TM6010) { - val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x0); + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); if (val < 0) return val; } else { - val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0); + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); if (val < 0) return val; } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); - val &= 0x0f; /* Preserve the audio input control bits */ - switch (bitrate) { - case 44100: - val |= 0xd0; - dev->audio_bitrate = bitrate; +int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0 = 0x03; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + areg_f0 = 0x03; + break; + case TM6000_AIP_LINE1: + areg_f0 = 0x00; + break; + case TM6000_AIP_LINE2: + areg_f0 = 0x08; + break; + default: + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + } else { + /* Audio setting, default LINE1 */ + u8 areg_eb = 0x00; + + switch (ainp) { + case TM6000_AIP_LINE1: + areg_eb = 0x00; + break; + case TM6000_AIP_LINE2: + areg_eb = 0x04; + break; + default: + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_input); + +void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_inaudio ainp; + + if (dev->radio) + ainp = dev->aradio; + else + ainp = dev->avideo; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + return -EINVAL; + } break; - case 48000: - val |= 0x60; - dev->audio_bitrate = bitrate; + case TM6000_AIP_LINE1: + case TM6000_AIP_LINE2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; break; } - if (dev->dev_type == TM6010) - val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - val); - else - val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val); + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_tvaudio_set_mute); + +void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; - return val; + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); } -EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_inaudio ainp; + + if (dev->radio) { + ainp = dev->aradio; + vol += 8; /* Offset to 0 dB */ + } else + ainp = dev->avideo; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + break; + case TM6000_AIP_LINE1: + case TM6000_AIP_LINE2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(tm6000_set_volume); static LIST_HEAD(tm6000_devlist); static DEFINE_MUTEX(tm6000_devlist_mutex); diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c index cc7b8664fc20..a4c07e5a6f1d 100644 --- a/drivers/staging/tm6000/tm6000-stds.c +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -952,6 +952,22 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, uint8_t mono_flag = 0; /* No mono */ uint8_t nicam_flag = 0; /* No NICAM */ + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + return 0; + } + switch (std) { #if 0 case DK_MONO: @@ -984,20 +1000,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, case EIAJ: areg_05 = 0x02; break; - case FM_RADIO: - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); - tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); - tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - return 0; - break; case I_NICAM: areg_05 = 0x08; nicam_flag = 1; @@ -1010,6 +1012,9 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, areg_05 = 0x0a; nicam_flag = 1; break; + default: + /* do nothink */ + break; } #if 0 diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index eb9b9f1bc138..b5503409ca55 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -53,11 +53,17 @@ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ /* Debug level */ int tm6000_debug; EXPORT_SYMBOL_GPL(tm6000_debug); +static const struct v4l2_queryctrl no_ctrl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + /* supported controls */ static struct v4l2_queryctrl tm6000_qctrl[] = { { @@ -96,9 +102,26 @@ static struct v4l2_queryctrl tm6000_qctrl[] = { .step = 0x1, .default_value = 0, .flags = 0, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = -15, + .maximum = 15, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, } }; +static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; static struct tm6000_fmt format[] = { @@ -117,6 +140,16 @@ static struct tm6000_fmt format[] = { } }; +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CTRLS; i++) + if (tm6000_qctrl[i].id == id) + return tm6000_qctrl+i; + return NULL; +} + /* ------------------------------------------------------------------ * DMA and thread functions * ------------------------------------------------------------------ @@ -199,13 +232,17 @@ static int copy_streams(u8 *data, unsigned long len, char *voutp = NULL; unsigned int linewidth; - /* get video buffer */ - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return 0; + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } for (ptr = data; ptr < endp;) { if (!dev->isoc_ctl.cmd) { @@ -257,29 +294,31 @@ static int copy_streams(u8 *data, unsigned long len, */ switch (cmd) { case TM6000_URB_MSG_VIDEO: - if ((dev->isoc_ctl.vfield != field) && - (field == 1)) { + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { /* Announces that a new buffer * were filled */ - buffer_filled(dev, dma_q, vbuf); - dprintk(dev, V4L2_DEBUG_ISOC, + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return rc; - memset(voutp, 0, vbuf->vb.size); - } - linewidth = vbuf->vb.width << 1; - pos = ((line << 1) - field - 1) * linewidth + - block * TM6000_URB_MSG_LEN; - /* Don't allow to write out of the buffer */ - if (pos + size > vbuf->vb.size) - cmd = TM6000_URB_MSG_ERR; - dev->isoc_ctl.vfield = field; + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } break; case TM6000_URB_MSG_VBI: break; @@ -537,7 +576,7 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) /* * Allocate URBs and start IRQ */ -static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) +static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; @@ -566,11 +605,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) dev->isoc_ctl.max_pkt_size = size; - max_packets = (framesize + size - 1) / size; - - if (max_packets > TM6000_MAX_ISO_PACKETS) - max_packets = TM6000_MAX_ISO_PACKETS; - + max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; dev->isoc_ctl.num_bufs = num_bufs; @@ -746,7 +781,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = tm6000_prepare_isoc(dev, buf->vb.size); + rc = tm6000_prepare_isoc(dev); if (rc < 0) goto fail; @@ -1143,6 +1178,12 @@ static int vidioc_g_ctrl(struct file *file, void *priv, case V4L2_CID_HUE: val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); return 0; + case V4L2_CID_AUDIO_MUTE: + val = dev->ctl_mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + val = dev->ctl_volume; + return 0; default: return -EINVAL; } @@ -1174,6 +1215,14 @@ static int vidioc_s_ctrl(struct file *file, void *priv, case V4L2_CID_HUE: tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); return 0; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; } return -EINVAL; } @@ -1221,7 +1270,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; - f->type = V4L2_TUNER_ANALOG_TV; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); @@ -1235,13 +1284,14 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; dev->freq = f->frequency; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); @@ -1249,6 +1299,122 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + strcpy(cap->driver, "tm6000"); + strlcpy(cap->card, dev->name, sizeof(dev->name)); + sprintf(cap->bus_info, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + cap->version = dev->dev_type; + cap->capabilities = V4L2_CAP_TUNER; + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + if ((dev->aradio == TM6000_AIP_LINE1) || + (dev->aradio == TM6000_AIP_LINE2)) { + t->rxsubchans = V4L2_TUNER_SUB_MONO; + } + else { + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + } + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + return 0; +} + +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + + return 0; +} + /* ------------------------------------------------------------------ File operations for the device ------------------------------------------------------------------*/ @@ -1260,6 +1426,7 @@ static int tm6000_open(struct file *file) struct tm6000_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int i, rc; + int radio = 0; printk(KERN_INFO "tm6000: open called (dev=%s)\n", video_device_node_name(vdev)); @@ -1267,6 +1434,17 @@ static int tm6000_open(struct file *file) dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", video_device_node_name(vdev)); + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } /* If more than one user, mutex should be added */ dev->users++; @@ -1284,8 +1462,9 @@ static int tm6000_open(struct file *file) file->private_data = fh; fh->dev = dev; - - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->radio = radio; + dev->radio = radio; + fh->type = type; dev->fourcc = format[0].fourcc; fh->fmt = format_by_fourcc(dev->fourcc); @@ -1322,6 +1501,19 @@ static int tm6000_open(struct file *file) V4L2_FIELD_INTERLACED, sizeof(struct tm6000_buffer), fh, &dev->lock); + if (fh->radio) { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + tm6000_set_audio_input(dev, dev->aradio); + tm6000_set_volume(dev, dev->ctl_volume); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + else { + tm6000_set_audio_input(dev, dev->avideo); + tm6000_set_volume(dev, dev->ctl_volume); + } + return 0; } @@ -1445,6 +1637,36 @@ static struct video_device tm6000_template = { .current_norm = V4L2_STD_NTSC_M, }; +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + +struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + /* ----------------------------------------------------------------- * Initialization and module stuff * ------------------------------------------------------------------ @@ -1499,6 +1721,25 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "%s: registered device %s\n", dev->name, video_device_node_name(dev->vfd)); + dev->radio_dev = vdev_init(dev, &tm6000_radio_template, + "radio"); + if (!dev->radio_dev) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->radio_dev)); + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); return ret; } @@ -1507,6 +1748,14 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + return 0; } diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h index bf11eeec92c7..ccd120fe1340 100644 --- a/drivers/staging/tm6000/tm6000.h +++ b/drivers/staging/tm6000/tm6000.h @@ -53,6 +53,14 @@ enum tm6000_devtype { TM6010, }; +enum tm6000_inaudio { + TM6000_AIP_UNK = 0, + TM6000_AIP_SIF1, + TM6000_AIP_SIF2, + TM6000_AIP_LINE1, + TM6000_AIP_LINE2, +}; + /* ------------------------------------------------------------------ * Basic structures * ------------------------------------------------------------------ @@ -174,6 +182,8 @@ struct tm6000_core { char *ir_codes; + __u8 radio; + /* Demodulator configuration */ int demod_addr; /* demodulator address */ @@ -194,6 +204,7 @@ struct tm6000_core { bool is_res_read; struct video_device *vfd; + struct video_device *radio_dev; struct tm6000_dmaqueue vidq; struct v4l2_device v4l2_dev; @@ -203,6 +214,9 @@ struct tm6000_core { enum tm6000_mode mode; + int ctl_mute; /* audio */ + int ctl_volume; + /* DVB-T support */ struct tm6000_dvb *dvb; @@ -210,7 +224,8 @@ struct tm6000_core { struct snd_tm6000_card *adev; struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ atomic_t stream_started; /* stream should be running if true */ - + enum tm6000_inaudio avideo; + enum tm6000_inaudio aradio; struct tm6000_IR *ir; @@ -248,6 +263,7 @@ struct tm6000_ops { struct tm6000_fh { struct tm6000_core *dev; + unsigned int radio; /* video capture */ struct tm6000_fmt *fmt; @@ -276,12 +292,17 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); int tm6000_init(struct tm6000_core *dev); int tm6000_init_analog_mode(struct tm6000_core *dev); int tm6000_init_digital_mode(struct tm6000_core *dev); int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); int tm6000_v4l2_register(struct tm6000_core *dev); int tm6000_v4l2_unregister(struct tm6000_core *dev); -- cgit v1.2.3 From dc46b8c8e3d7e6c7c7ddb78fc2137a5462e98679 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Tue, 1 Mar 2011 01:55:11 -0300 Subject: [media] tm6000: add audio conf for new cards Add configuration of an audio for our new TV cards. With my best regards, Dmitry. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-cards.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index a356ba7aa77c..88144a12745d 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -324,6 +324,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_addr = 0xc2 >> 1, .demod_addr = 0x1e >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 1, @@ -341,6 +343,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_type = TUNER_XC5000, .tuner_addr = 0xc2 >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 0, -- cgit v1.2.3 From 0cd301f19a91b3e1fb4130dcd9106777003ed4ed Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2011 17:15:15 -0300 Subject: [media] staging/cx25721: serialize access to devlist Out of the three files accessing the device list, one uses a mutex, one uses the BKL and one does not have any locking. That is of course pointless, so let's make all of them use the same mutex, and get rid of one more BKL user. Signed-off-by: Arnd Bergmann Signed-off-by: Palash Bandyopadhyay Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/cx25821/Kconfig | 1 - drivers/staging/cx25821/cx25821-alsa.c | 2 ++ drivers/staging/cx25821/cx25821-core.c | 16 +++++++--------- drivers/staging/cx25821/cx25821-video.c | 9 ++++----- drivers/staging/cx25821/cx25821.h | 3 ++- 5 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/cx25821/Kconfig b/drivers/staging/cx25821/Kconfig index b2656957aa8f..5f6b54213713 100644 --- a/drivers/staging/cx25821/Kconfig +++ b/drivers/staging/cx25821/Kconfig @@ -1,7 +1,6 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" depends on DVB_CORE && VIDEO_DEV && PCI && I2C - depends on BKL # please fix select I2C_ALGOBIT select VIDEO_BTCX select VIDEO_TVEEPROM diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c index 160f6693aa33..ebdba7c65bc5 100644 --- a/drivers/staging/cx25821/cx25821-alsa.c +++ b/drivers/staging/cx25821/cx25821-alsa.c @@ -770,10 +770,12 @@ static int cx25821_alsa_init(void) struct cx25821_dev *dev = NULL; struct list_head *list; + mutex_lock(&cx25821_devlist_mutex); list_for_each(list, &cx25821_devlist) { dev = list_entry(list, struct cx25821_dev, devlist); cx25821_audio_initdev(dev); } + mutex_unlock(&cx25821_devlist_mutex); if (dev == NULL) pr_info("ERROR ALSA: no cx25821 cards found\n"); diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c index a216b620b718..523ac5e16c1b 100644 --- a/drivers/staging/cx25821/cx25821-core.c +++ b/drivers/staging/cx25821/cx25821-core.c @@ -33,9 +33,6 @@ MODULE_DESCRIPTION("Driver for Athena cards"); MODULE_AUTHOR("Shu Lin - Hiep Huynh"); MODULE_LICENSE("GPL"); -struct list_head cx25821_devlist; -EXPORT_SYMBOL(cx25821_devlist); - static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); @@ -46,8 +43,10 @@ MODULE_PARM_DESC(card, "card type"); static unsigned int cx25821_devcount; -static DEFINE_MUTEX(devlist); +DEFINE_MUTEX(cx25821_devlist_mutex); +EXPORT_SYMBOL(cx25821_devlist_mutex); LIST_HEAD(cx25821_devlist); +EXPORT_SYMBOL(cx25821_devlist); struct sram_channel cx25821_sram_channels[] = { [SRAM_CH00] = { @@ -911,9 +910,9 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->nr = ++cx25821_devcount; sprintf(dev->name, "cx25821[%d]", dev->nr); - mutex_lock(&devlist); + mutex_lock(&cx25821_devlist_mutex); list_add_tail(&dev->devlist, &cx25821_devlist); - mutex_unlock(&devlist); + mutex_unlock(&cx25821_devlist_mutex); strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown"); strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821"); @@ -1465,9 +1464,9 @@ static void __devexit cx25821_finidev(struct pci_dev *pci_dev) if (pci_dev->irq) free_irq(pci_dev->irq, dev); - mutex_lock(&devlist); + mutex_lock(&cx25821_devlist_mutex); list_del(&dev->devlist); - mutex_unlock(&devlist); + mutex_unlock(&cx25821_devlist_mutex); cx25821_dev_unregister(dev); v4l2_device_unregister(v4l2_dev); @@ -1501,7 +1500,6 @@ static struct pci_driver cx25821_pci_driver = { static int __init cx25821_init(void) { - INIT_LIST_HEAD(&cx25821_devlist); pr_info("driver version %d.%d.%d loaded\n", (CX25821_VERSION_CODE >> 16) & 0xff, (CX25821_VERSION_CODE >> 8) & 0xff, diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c index 0d8d75670516..ab05392386e8 100644 --- a/drivers/staging/cx25821/cx25821-video.c +++ b/drivers/staging/cx25821/cx25821-video.c @@ -27,7 +27,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "cx25821-video.h" -#include MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); MODULE_AUTHOR("Hiep Huynh "); @@ -815,7 +814,7 @@ static int video_open(struct file *file) if (NULL == fh) return -ENOMEM; - lock_kernel(); + mutex_lock(&cx25821_devlist_mutex); list_for_each(list, &cx25821_devlist) { @@ -832,8 +831,8 @@ static int video_open(struct file *file) } if (NULL == dev) { - unlock_kernel(); - return -ENODEV; + mutex_unlock(&cx25821_devlist_mutex); + return -ENODEV; } file->private_data = fh; @@ -862,7 +861,7 @@ static int video_open(struct file *file) sizeof(struct cx25821_buffer), fh, NULL); dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); + mutex_unlock(&cx25821_devlist_mutex); return 0; } diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/staging/cx25821/cx25821.h index 55115235f7f6..6230243e2ccb 100644 --- a/drivers/staging/cx25821/cx25821.h +++ b/drivers/staging/cx25821/cx25821.h @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -445,6 +444,8 @@ static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) extern struct list_head cx25821_devlist; +extern struct mutex cx25821_devlist_mutex; + extern struct cx25821_board cx25821_boards[]; extern struct cx25821_subid cx25821_subids[]; -- cgit v1.2.3 From 812738e11158e9b92c4f0e9278de48377cfa3cc8 Mon Sep 17 00:00:00 2001 From: "Juan J. Garcia de Soria" Date: Wed, 16 Mar 2011 17:14:53 -0300 Subject: [media] lirc: remove staging lirc_it87 and lirc_ite8709 drivers Remove older drivers lirc_it87 and lirc_ite8709 from the LIRC staging area, since they're now superceded by ite-cir. Signed-off-by: Juan J. Garcia de Soria Tested-by: Stephan Raue Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/Kconfig | 12 - drivers/staging/lirc/Makefile | 2 - drivers/staging/lirc/lirc_it87.c | 1027 ----------------------------------- drivers/staging/lirc/lirc_it87.h | 116 ---- drivers/staging/lirc/lirc_ite8709.c | 542 ------------------ 5 files changed, 1699 deletions(-) delete mode 100644 drivers/staging/lirc/lirc_it87.c delete mode 100644 drivers/staging/lirc/lirc_it87.h delete mode 100644 drivers/staging/lirc/lirc_ite8709.c (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig index cdaff5903a8f..526ec0fc2f04 100644 --- a/drivers/staging/lirc/Kconfig +++ b/drivers/staging/lirc/Kconfig @@ -32,18 +32,6 @@ config LIRC_IMON Current generation iMON devices use the input layer imon driver. -config LIRC_IT87 - tristate "ITE IT87XX CIR Port Receiver" - depends on LIRC && PNP - help - Driver for the ITE IT87xx IR Receiver - -config LIRC_ITE8709 - tristate "ITE8709 CIR Port Receiver" - depends on LIRC && PNP - help - Driver for the ITE8709 IR Receiver - config LIRC_PARALLEL tristate "Homebrew Parallel Port Receiver" depends on LIRC && PARPORT diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile index 94af218d8373..d76b0fa2af53 100644 --- a/drivers/staging/lirc/Makefile +++ b/drivers/staging/lirc/Makefile @@ -6,8 +6,6 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o obj-$(CONFIG_LIRC_IMON) += lirc_imon.o -obj-$(CONFIG_LIRC_IT87) += lirc_it87.o -obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c deleted file mode 100644 index 5938616f3e8f..000000000000 --- a/drivers/staging/lirc/lirc_it87.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * LIRC driver for ITE IT8712/IT8705 CIR port - * - * Copyright (C) 2001 Hans-Gunter Lutke Uphues - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * - * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based - * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula - * - * Attention: Sendmode only tested with debugging logs - * - * 2001/02/27 Christoph Bartelmus : - * reimplemented read function - * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix, - * based on work of the following member of the Outertrack Digimatrix - * Forum: Art103 - * 2009/12/24 James Edwards implemeted support - * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the - * chip identifies as 18. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "lirc_it87.h" - -#ifdef LIRC_IT87_DIGIMATRIX -static int digimatrix = 1; -static int it87_freq = 36; /* kHz */ -static int irq = 9; -#else -static int digimatrix; -static int it87_freq = 38; /* kHz */ -static int irq = IT87_CIR_DEFAULT_IRQ; -#endif - -static unsigned long it87_bits_in_byte_out; -static unsigned long it87_send_counter; -static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN; - -#define RBUF_LEN 1024 - -#define LIRC_DRIVER_NAME "lirc_it87" - -/* timeout for sequences in jiffies (=5/100s) */ -/* must be longer than TIME_CONST */ -#define IT87_TIMEOUT (HZ*5/100) - -/* module parameters */ -static int debug; -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - -static int io = IT87_CIR_DEFAULT_IOBASE; -/* receiver demodulator default: off */ -static int it87_enable_demodulator; - -static int timer_enabled; -static DEFINE_SPINLOCK(timer_lock); -static struct timer_list timerlist; -/* time of last signal change detected */ -static struct timeval last_tv = {0, 0}; -/* time of last UART data ready interrupt */ -static struct timeval last_intr_tv = {0, 0}; -static int last_value; - -static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); - -static DEFINE_SPINLOCK(hardware_lock); -static DEFINE_SPINLOCK(dev_lock); -static bool device_open; - -static int rx_buf[RBUF_LEN]; -unsigned int rx_tail, rx_head; - -static struct pnp_driver it87_pnp_driver; - -/* SECTION: Prototypes */ - -/* Communication with user-space */ -static int lirc_open(struct inode *inode, struct file *file); -static int lirc_close(struct inode *inode, struct file *file); -static unsigned int lirc_poll(struct file *file, poll_table *wait); -static ssize_t lirc_read(struct file *file, char *buf, - size_t count, loff_t *ppos); -static ssize_t lirc_write(struct file *file, const char *buf, - size_t n, loff_t *pos); -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -static void add_read_queue(int flag, unsigned long val); -static int init_chrdev(void); -static void drop_chrdev(void); -/* Hardware */ -static irqreturn_t it87_interrupt(int irq, void *dev_id); -static void send_space(unsigned long len); -static void send_pulse(unsigned long len); -static void init_send(void); -static void terminate_send(unsigned long len); -static int init_hardware(void); -static void drop_hardware(void); -/* Initialisation */ -static int init_port(void); -static void drop_port(void); - - -/* SECTION: Communication with user-space */ - -static int lirc_open(struct inode *inode, struct file *file) -{ - spin_lock(&dev_lock); - if (device_open) { - spin_unlock(&dev_lock); - return -EBUSY; - } - device_open = true; - spin_unlock(&dev_lock); - return 0; -} - - -static int lirc_close(struct inode *inode, struct file *file) -{ - spin_lock(&dev_lock); - device_open = false; - spin_unlock(&dev_lock); - return 0; -} - - -static unsigned int lirc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lirc_read_queue, wait); - if (rx_head != rx_tail) - return POLLIN | POLLRDNORM; - return 0; -} - - -static ssize_t lirc_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - int n = 0; - int retval = 0; - - while (n < count) { - if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) { - retval = -EAGAIN; - break; - } - retval = wait_event_interruptible(lirc_read_queue, - rx_head != rx_tail); - if (retval) - break; - - if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head), - sizeof(int))) { - retval = -EFAULT; - break; - } - rx_head = (rx_head + 1) & (RBUF_LEN - 1); - n += sizeof(int); - } - if (n) - return n; - return retval; -} - - -static ssize_t lirc_write(struct file *file, const char *buf, - size_t n, loff_t *pos) -{ - int i = 0; - int *tx_buf; - - if (n % sizeof(int)) - return -EINVAL; - tx_buf = memdup_user(buf, n); - if (IS_ERR(tx_buf)) - return PTR_ERR(tx_buf); - n /= sizeof(int); - init_send(); - while (1) { - if (i >= n) - break; - if (tx_buf[i]) - send_pulse(tx_buf[i]); - i++; - if (i >= n) - break; - if (tx_buf[i]) - send_space(tx_buf[i]); - i++; - } - terminate_send(tx_buf[i - 1]); - kfree(tx_buf); - return n; -} - - -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int retval = 0; - __u32 value = 0; - unsigned long hw_flags; - - if (cmd == LIRC_GET_FEATURES) - value = LIRC_CAN_SEND_PULSE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_REC_MODE2; - else if (cmd == LIRC_GET_SEND_MODE) - value = LIRC_MODE_PULSE; - else if (cmd == LIRC_GET_REC_MODE) - value = LIRC_MODE_MODE2; - - switch (cmd) { - case LIRC_GET_FEATURES: - case LIRC_GET_SEND_MODE: - case LIRC_GET_REC_MODE: - retval = put_user(value, (__u32 *) arg); - break; - - case LIRC_SET_SEND_MODE: - case LIRC_SET_REC_MODE: - retval = get_user(value, (__u32 *) arg); - break; - - case LIRC_SET_SEND_CARRIER: - retval = get_user(value, (__u32 *) arg); - if (retval) - return retval; - value /= 1000; - if (value > IT87_CIR_FREQ_MAX || - value < IT87_CIR_FREQ_MIN) - return -EINVAL; - - it87_freq = value; - - spin_lock_irqsave(&hardware_lock, hw_flags); - outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) | - (it87_freq - IT87_CIR_FREQ_MIN) << 3), - io + IT87_CIR_TCR2); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - dprintk("demodulation frequency: %d kHz\n", it87_freq); - - break; - - default: - retval = -EINVAL; - } - - if (retval) - return retval; - - if (cmd == LIRC_SET_REC_MODE) { - if (value != LIRC_MODE_MODE2) - retval = -ENOSYS; - } else if (cmd == LIRC_SET_SEND_MODE) { - if (value != LIRC_MODE_PULSE) - retval = -ENOSYS; - } - return retval; -} - -static void add_read_queue(int flag, unsigned long val) -{ - unsigned int new_rx_tail; - int newval; - - dprintk("add flag %d with val %lu\n", flag, val); - - newval = val & PULSE_MASK; - - /* - * statistically, pulses are ~TIME_CONST/2 too long. we could - * maybe make this more exact, but this is good enough - */ - if (flag) { - /* pulse */ - if (newval > TIME_CONST / 2) - newval -= TIME_CONST / 2; - else /* should not ever happen */ - newval = 1; - newval |= PULSE_BIT; - } else - newval += TIME_CONST / 2; - new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); - if (new_rx_tail == rx_head) { - dprintk("Buffer overrun.\n"); - return; - } - rx_buf[rx_tail] = newval; - rx_tail = new_rx_tail; - wake_up_interruptible(&lirc_read_queue); -} - - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .read = lirc_read, - .write = lirc_write, - .poll = lirc_poll, - .unlocked_ioctl = lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lirc_ioctl, -#endif - .open = lirc_open, - .release = lirc_close, - .llseek = noop_llseek, -}; - -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ -} - -static struct lirc_driver driver = { - .name = LIRC_DRIVER_NAME, - .minor = -1, - .code_length = 1, - .sample_rate = 0, - .data = NULL, - .add_to_buf = NULL, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .dev = NULL, - .owner = THIS_MODULE, -}; - - -static int init_chrdev(void) -{ - driver.minor = lirc_register_driver(&driver); - - if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); - return -EIO; - } - return 0; -} - - -static void drop_chrdev(void) -{ - lirc_unregister_driver(driver.minor); -} - - -/* SECTION: Hardware */ -static long delta(struct timeval *tv1, struct timeval *tv2) -{ - unsigned long deltv; - - deltv = tv2->tv_sec - tv1->tv_sec; - if (deltv > 15) - deltv = 0xFFFFFF; - else - deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec; - return deltv; -} - -static void it87_timeout(unsigned long data) -{ - unsigned long flags; - - /* avoid interference with interrupt */ - spin_lock_irqsave(&timer_lock, flags); - - if (digimatrix) { - /* We have timed out. Disable the RX mechanism. */ - - outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) | - IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR); - if (it87_RXEN_mask) - outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - dprintk(" TIMEOUT\n"); - timer_enabled = 0; - - /* fifo clear */ - outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR, - io+IT87_CIR_TCR1); - - } else { - /* - * if last received signal was a pulse, but receiving stopped - * within the 9 bit frame, we need to finish this pulse and - * simulate a signal change to from pulse to space. Otherwise - * upper layers will receive two sequences next time. - */ - - if (last_value) { - unsigned long pulse_end; - - /* determine 'virtual' pulse end: */ - pulse_end = delta(&last_tv, &last_intr_tv); - dprintk("timeout add %d for %lu usec\n", - last_value, pulse_end); - add_read_queue(last_value, pulse_end); - last_value = 0; - last_tv = last_intr_tv; - } - } - spin_unlock_irqrestore(&timer_lock, flags); -} - -static irqreturn_t it87_interrupt(int irq, void *dev_id) -{ - unsigned char data; - struct timeval curr_tv; - static unsigned long deltv; - unsigned long deltintrtv; - unsigned long flags, hw_flags; - int iir, lsr; - int fifo = 0; - static char lastbit; - char bit; - - /* Bit duration in microseconds */ - const unsigned long bit_duration = 1000000ul / - (115200 / IT87_CIR_BAUDRATE_DIVISOR); - - - iir = inb(io + IT87_CIR_IIR); - - switch (iir & IT87_CIR_IIR_IID) { - case 0x4: - case 0x6: - lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | - IT87_CIR_RSR_RXFBC); - fifo = lsr & IT87_CIR_RSR_RXFBC; - dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr); - - /* avoid interference with timer */ - spin_lock_irqsave(&timer_lock, flags); - spin_lock_irqsave(&hardware_lock, hw_flags); - if (digimatrix) { - static unsigned long acc_pulse; - static unsigned long acc_space; - - do { - data = inb(io + IT87_CIR_DR); - data = ~data; - fifo--; - if (data != 0x00) { - if (timer_enabled) - del_timer(&timerlist); - /* - * start timer for end of - * sequence detection - */ - timerlist.expires = jiffies + - IT87_TIMEOUT; - add_timer(&timerlist); - timer_enabled = 1; - } - /* Loop through */ - for (bit = 0; bit < 8; ++bit) { - if ((data >> bit) & 1) { - ++acc_pulse; - if (lastbit == 0) { - add_read_queue(0, - acc_space * - bit_duration); - acc_space = 0; - } - } else { - ++acc_space; - if (lastbit == 1) { - add_read_queue(1, - acc_pulse * - bit_duration); - acc_pulse = 0; - } - } - lastbit = (data >> bit) & 1; - } - - } while (fifo != 0); - } else { /* Normal Operation */ - do { - del_timer(&timerlist); - data = inb(io + IT87_CIR_DR); - - dprintk("data=%02x\n", data); - do_gettimeofday(&curr_tv); - deltv = delta(&last_tv, &curr_tv); - deltintrtv = delta(&last_intr_tv, &curr_tv); - - dprintk("t %lu , d %d\n", - deltintrtv, (int)data); - - /* - * if nothing came in last 2 cycles, - * it was gap - */ - if (deltintrtv > TIME_CONST * 2) { - if (last_value) { - dprintk("GAP\n"); - - /* simulate signal change */ - add_read_queue(last_value, - deltv - - deltintrtv); - last_value = 0; - last_tv.tv_sec = - last_intr_tv.tv_sec; - last_tv.tv_usec = - last_intr_tv.tv_usec; - deltv = deltintrtv; - } - } - data = 1; - if (data ^ last_value) { - /* - * deltintrtv > 2*TIME_CONST, - * remember ? the other case is - * timeout - */ - add_read_queue(last_value, - deltv-TIME_CONST); - last_value = data; - last_tv = curr_tv; - if (last_tv.tv_usec >= TIME_CONST) - last_tv.tv_usec -= TIME_CONST; - else { - last_tv.tv_sec--; - last_tv.tv_usec += 1000000 - - TIME_CONST; - } - } - last_intr_tv = curr_tv; - if (data) { - /* - * start timer for end of - * sequence detection - */ - timerlist.expires = - jiffies + IT87_TIMEOUT; - add_timer(&timerlist); - } - outb((inb(io + IT87_CIR_RCR) & - ~IT87_CIR_RCR_RXEN) | - IT87_CIR_RCR_RXACT, - io + IT87_CIR_RCR); - if (it87_RXEN_mask) - outb(inb(io + IT87_CIR_RCR) | - IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - fifo--; - } while (fifo != 0); - } - spin_unlock_irqrestore(&hardware_lock, hw_flags); - spin_unlock_irqrestore(&timer_lock, flags); - - return IRQ_RETVAL(IRQ_HANDLED); - - default: - /* not our irq */ - dprintk("unknown IRQ (shouldn't happen) !!\n"); - return IRQ_RETVAL(IRQ_NONE); - } -} - - -static void send_it87(unsigned long len, unsigned long stime, - unsigned char send_byte, unsigned int count_bits) -{ - long count = len / stime; - long time_left = 0; - static unsigned char byte_out; - unsigned long hw_flags; - - dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte); - - time_left = (long)len - (long)count * (long)stime; - count += ((2 * time_left) / stime); - while (count) { - long i = 0; - for (i = 0; i < count_bits; i++) { - byte_out = (byte_out << 1) | (send_byte & 1); - it87_bits_in_byte_out++; - } - if (it87_bits_in_byte_out == 8) { - dprintk("out=0x%x, tsr_txfbc: 0x%x\n", - byte_out, - inb(io + IT87_CIR_TSR) & - IT87_CIR_TSR_TXFBC); - - while ((inb(io + IT87_CIR_TSR) & - IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE) - ; - - spin_lock_irqsave(&hardware_lock, hw_flags); - outb(byte_out, io + IT87_CIR_DR); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - - it87_bits_in_byte_out = 0; - it87_send_counter++; - byte_out = 0; - } - count--; - } -} - - -/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */ - -static void send_space(unsigned long len) -{ - send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR); -} - -static void send_pulse(unsigned long len) -{ - send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR); -} - - -static void init_send() -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - /* RXEN=0: receiver disable */ - it87_RXEN_mask = 0; - outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - spin_unlock_irqrestore(&hardware_lock, flags); - it87_bits_in_byte_out = 0; - it87_send_counter = 0; -} - - -static void terminate_send(unsigned long len) -{ - unsigned long flags; - unsigned long last = 0; - - last = it87_send_counter; - /* make sure all necessary data has been sent */ - while (last == it87_send_counter) - send_space(len); - /* wait until all data sent */ - while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0) - ; - /* then re-enable receiver */ - spin_lock_irqsave(&hardware_lock, flags); - it87_RXEN_mask = IT87_CIR_RCR_RXEN; - outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - spin_unlock_irqrestore(&hardware_lock, flags); -} - - -static int init_hardware(void) -{ - unsigned long flags; - unsigned char it87_rcr = 0; - - spin_lock_irqsave(&hardware_lock, flags); - /* init cir-port */ - /* enable r/w-access to Baudrate-Register */ - outb(IT87_CIR_IER_BR, io + IT87_CIR_IER); - outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR); - outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR); - /* Baudrate Register off, define IRQs: Input only */ - if (digimatrix) { - outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER); - /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */ - } else { - outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER); - /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */ - } - it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1; - if (it87_enable_demodulator) - it87_rcr |= IT87_CIR_RCR_RXEND; - outb(it87_rcr, io + IT87_CIR_RCR); - if (digimatrix) { - /* Set FIFO depth to 1 byte, and disable TX */ - outb(inb(io + IT87_CIR_TCR1) | 0x00, - io + IT87_CIR_TCR1); - - /* - * TX: it87_freq (36kHz), 'reserved' sensitivity - * setting (0x00) - */ - outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00, - io + IT87_CIR_TCR2); - } else { - /* TX: 38kHz, 13,3us (pulse-width) */ - outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06, - io + IT87_CIR_TCR2); - } - spin_unlock_irqrestore(&hardware_lock, flags); - return 0; -} - - -static void drop_hardware(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - disable_irq(irq); - /* receiver disable */ - it87_RXEN_mask = 0; - outb(0x1, io + IT87_CIR_RCR); - /* turn off irqs */ - outb(0, io + IT87_CIR_IER); - /* fifo clear */ - outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1); - /* reset */ - outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); - enable_irq(irq); - spin_unlock_irqrestore(&hardware_lock, flags); -} - - -static unsigned char it87_read(unsigned char port) -{ - outb(port, IT87_ADRPORT); - return inb(IT87_DATAPORT); -} - - -static void it87_write(unsigned char port, unsigned char data) -{ - outb(port, IT87_ADRPORT); - outb(data, IT87_DATAPORT); -} - - -/* SECTION: Initialisation */ - -static int init_port(void) -{ - unsigned long hw_flags; - int retval = 0; - - unsigned char init_bytes[4] = IT87_INIT; - unsigned char it87_chipid = 0; - unsigned char ldn = 0; - unsigned int it87_io = 0; - unsigned int it87_irq = 0; - - /* Enter MB PnP Mode */ - outb(init_bytes[0], IT87_ADRPORT); - outb(init_bytes[1], IT87_ADRPORT); - outb(init_bytes[2], IT87_ADRPORT); - outb(init_bytes[3], IT87_ADRPORT); - - /* 8712 or 8705 ? */ - it87_chipid = it87_read(IT87_CHIP_ID1); - if (it87_chipid != 0x87) { - retval = -ENXIO; - return retval; - } - it87_chipid = it87_read(IT87_CHIP_ID2); - if ((it87_chipid != 0x05) && - (it87_chipid != 0x12) && - (it87_chipid != 0x18) && - (it87_chipid != 0x20)) { - printk(KERN_INFO LIRC_DRIVER_NAME - ": no IT8704/05/12/18/20 found (claimed IT87%02x), " - "exiting..\n", it87_chipid); - retval = -ENXIO; - return retval; - } - printk(KERN_INFO LIRC_DRIVER_NAME - ": found IT87%02x.\n", - it87_chipid); - - /* get I/O-Port and IRQ */ - if (it87_chipid == 0x12 || it87_chipid == 0x18) - ldn = IT8712_CIR_LDN; - else - ldn = IT8705_CIR_LDN; - it87_write(IT87_LDN, ldn); - - it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 + - it87_read(IT87_CIR_BASE_LSB); - if (it87_io == 0) { - if (io == 0) - io = IT87_CIR_DEFAULT_IOBASE; - printk(KERN_INFO LIRC_DRIVER_NAME - ": set default io 0x%x\n", - io); - it87_write(IT87_CIR_BASE_MSB, io / 0x100); - it87_write(IT87_CIR_BASE_LSB, io % 0x100); - } else - io = it87_io; - - it87_irq = it87_read(IT87_CIR_IRQ); - if (digimatrix || it87_irq == 0) { - if (irq == 0) - irq = IT87_CIR_DEFAULT_IRQ; - printk(KERN_INFO LIRC_DRIVER_NAME - ": set default irq 0x%x\n", - irq); - it87_write(IT87_CIR_IRQ, irq); - } else - irq = it87_irq; - - spin_lock_irqsave(&hardware_lock, hw_flags); - /* reset */ - outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); - /* fifo clear */ - outb(IT87_CIR_TCR1_FIFOCLR | - /* IT87_CIR_TCR1_ILE | */ - IT87_CIR_TCR1_TXRLE | - IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - - /* get I/O port access and IRQ line */ - if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); - return -EBUSY; - } - - /* activate CIR-Device */ - it87_write(IT87_CIR_ACT, 0x1); - - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); - - retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/, - LIRC_DRIVER_NAME, NULL); - if (retval < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); - release_region(io, 8); - return retval; - } - - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", io, irq); - - init_timer(&timerlist); - timerlist.function = it87_timeout; - timerlist.data = 0xabadcafe; - - return 0; -} - - -static void drop_port(void) -{ -#if 0 - unsigned char init_bytes[4] = IT87_INIT; - - /* Enter MB PnP Mode */ - outb(init_bytes[0], IT87_ADRPORT); - outb(init_bytes[1], IT87_ADRPORT); - outb(init_bytes[2], IT87_ADRPORT); - outb(init_bytes[3], IT87_ADRPORT); - - /* deactivate CIR-Device */ - it87_write(IT87_CIR_ACT, 0x0); - - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); -#endif - - del_timer_sync(&timerlist); - free_irq(irq, NULL); - release_region(io, 8); -} - - -static int init_lirc_it87(void) -{ - int retval; - - init_waitqueue_head(&lirc_read_queue); - retval = init_port(); - if (retval < 0) - return retval; - init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n"); - return 0; -} - -static int it87_probe(struct pnp_dev *pnp_dev, - const struct pnp_device_id *dev_id) -{ - int retval; - - driver.dev = &pnp_dev->dev; - - retval = init_chrdev(); - if (retval < 0) - return retval; - - retval = init_lirc_it87(); - if (retval) - goto init_lirc_it87_failed; - - return 0; - -init_lirc_it87_failed: - drop_chrdev(); - - return retval; -} - -static int __init lirc_it87_init(void) -{ - return pnp_register_driver(&it87_pnp_driver); -} - - -static void __exit lirc_it87_exit(void) -{ - drop_hardware(); - drop_chrdev(); - drop_port(); - pnp_unregister_driver(&it87_pnp_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); -} - -/* SECTION: PNP for ITE8704/13/18 */ - -static const struct pnp_device_id pnp_dev_table[] = { - {"ITE8704", 0}, - {"ITE8713", 0}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static struct pnp_driver it87_pnp_driver = { - .name = LIRC_DRIVER_NAME, - .id_table = pnp_dev_table, - .probe = it87_probe, -}; - -module_init(lirc_it87_init); -module_exit(lirc_it87_exit); - -MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port"); -MODULE_AUTHOR("Hans-Gunter Lutke Uphues"); -MODULE_LICENSE("GPL"); - -module_param(io, int, S_IRUGO); -MODULE_PARM_DESC(io, "I/O base address (default: 0x310)"); - -module_param(irq, int, S_IRUGO); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)"); -#else -MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)"); -#endif - -module_param(it87_enable_demodulator, bool, S_IRUGO); -MODULE_PARM_DESC(it87_enable_demodulator, - "Receiver demodulator enable/disable (1/0), default: 0"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); - -module_param(digimatrix, bool, S_IRUGO | S_IWUSR); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(digimatrix, - "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1"); -#else -MODULE_PARM_DESC(digimatrix, - "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0"); -#endif - - -module_param(it87_freq, int, S_IRUGO); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(it87_freq, - "Carrier demodulator frequency (kHz), (default: 36)"); -#else -MODULE_PARM_DESC(it87_freq, - "Carrier demodulator frequency (kHz), (default: 38)"); -#endif diff --git a/drivers/staging/lirc/lirc_it87.h b/drivers/staging/lirc/lirc_it87.h deleted file mode 100644 index cf021c893a35..000000000000 --- a/drivers/staging/lirc/lirc_it87.h +++ /dev/null @@ -1,116 +0,0 @@ -/* lirc_it87.h */ -/* SECTION: Definitions */ - -/********************************* ITE IT87xx ************************/ - -/* based on the following documentation from ITE: - a) IT8712F Preliminary CIR Programming Guide V0.1 - b) IT8705F Simple LPC I/O Preliminary Specification V0.3 - c) IT8712F EC-LPC I/O Preliminary Specification V0.5 -*/ - -/* IT8712/05 Ports: */ -#define IT87_ADRPORT 0x2e -#define IT87_DATAPORT 0x2f -#define IT87_INIT {0x87, 0x01, 0x55, 0x55} - -/* alternate Ports: */ -/* -#define IT87_ADRPORT 0x4e -#define IT87_DATAPORT 0x4f -#define IT87_INIT {0x87, 0x01, 0x55, 0xaa} - */ - -/* IT8712/05 Registers */ -#define IT87_CFGCTRL 0x2 -#define IT87_LDN 0x7 -#define IT87_CHIP_ID1 0x20 -#define IT87_CHIP_ID2 0x21 -#define IT87_CFG_VERSION 0x22 -#define IT87_SWSUSPEND 0x23 - -#define IT8712_CIR_LDN 0xa -#define IT8705_CIR_LDN 0x7 - -/* CIR Configuration Registers: */ -#define IT87_CIR_ACT 0x30 -#define IT87_CIR_BASE_MSB 0x60 -#define IT87_CIR_BASE_LSB 0x61 -#define IT87_CIR_IRQ 0x70 -#define IT87_CIR_CONFIG 0xf0 - -/* List of IT87_CIR registers: offset to BaseAddr */ -#define IT87_CIR_DR 0 -#define IT87_CIR_IER 1 -#define IT87_CIR_RCR 2 -#define IT87_CIR_TCR1 3 -#define IT87_CIR_TCR2 4 -#define IT87_CIR_TSR 5 -#define IT87_CIR_RSR 6 -#define IT87_CIR_BDLR 5 -#define IT87_CIR_BDHR 6 -#define IT87_CIR_IIR 7 - -/* Bit Definition */ -/* IER: */ -#define IT87_CIR_IER_TM_EN 0x80 -#define IT87_CIR_IER_RESEVED 0x40 -#define IT87_CIR_IER_RESET 0x20 -#define IT87_CIR_IER_BR 0x10 -#define IT87_CIR_IER_IEC 0x8 -#define IT87_CIR_IER_RFOIE 0x4 -#define IT87_CIR_IER_RDAIE 0x2 -#define IT87_CIR_IER_TLDLIE 0x1 - -/* RCR: */ -#define IT87_CIR_RCR_RDWOS 0x80 -#define IT87_CIR_RCR_HCFS 0x40 -#define IT87_CIR_RCR_RXEN 0x20 -#define IT87_CIR_RCR_RXEND 0x10 -#define IT87_CIR_RCR_RXACT 0x8 -#define IT87_CIR_RCR_RXDCR 0x7 - -/* TCR1: */ -#define IT87_CIR_TCR1_FIFOCLR 0x80 -#define IT87_CIR_TCR1_ILE 0x40 -#define IT87_CIR_TCR1_FIFOTL 0x30 -#define IT87_CIR_TCR1_TXRLE 0x8 -#define IT87_CIR_TCR1_TXENDF 0x4 -#define IT87_CIR_TCR1_TXMPM 0x3 - -/* TCR2: */ -#define IT87_CIR_TCR2_CFQ 0xf8 -#define IT87_CIR_TCR2_TXMPW 0x7 - -/* TSR: */ -#define IT87_CIR_TSR_RESERVED 0xc0 -#define IT87_CIR_TSR_TXFBC 0x3f - -/* RSR: */ -#define IT87_CIR_RSR_RXFTO 0x80 -#define IT87_CIR_RSR_RESERVED 0x40 -#define IT87_CIR_RSR_RXFBC 0x3f - -/* IIR: */ -#define IT87_CIR_IIR_RESERVED 0xf8 -#define IT87_CIR_IIR_IID 0x6 -#define IT87_CIR_IIR_IIP 0x1 - -/* TM: */ -#define IT87_CIR_TM_IL_SEL 0x80 -#define IT87_CIR_TM_RESERVED 0x40 -#define IT87_CIR_TM_TM_REG 0x3f - -#define IT87_CIR_FIFO_SIZE 32 - -/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */ -#define IT87_CIR_BAUDRATE_DIVISOR 0x1 -#define IT87_CIR_DEFAULT_IOBASE 0x310 -#define IT87_CIR_DEFAULT_IRQ 0x7 -#define IT87_CIR_SPACE 0x00 -#define IT87_CIR_PULSE 0xff -#define IT87_CIR_FREQ_MIN 27 -#define IT87_CIR_FREQ_MAX 58 -#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul) - -/********************************* ITE IT87xx ************************/ diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c deleted file mode 100644 index cb20cfdcfadd..000000000000 --- a/drivers/staging/lirc/lirc_ite8709.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * LIRC driver for ITE8709 CIR port - * - * Copyright (C) 2008 Grégory Lardière - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#define LIRC_DRIVER_NAME "lirc_ite8709" - -#define BUF_CHUNK_SIZE sizeof(int) -#define BUF_SIZE (128*BUF_CHUNK_SIZE) - -/* - * The ITE8709 device seems to be the combination of IT8512 superIO chip and - * a specific firmware running on the IT8512's embedded micro-controller. - * In addition of the embedded micro-controller, the IT8512 chip contains a - * CIR module and several other modules. A few modules are directly accessible - * by the host CPU, but most of them are only accessible by the - * micro-controller. The CIR module is only accessible by the micro-controller. - * The battery-backed SRAM module is accessible by the host CPU and the - * micro-controller. So one of the MC's firmware role is to act as a bridge - * between the host CPU and the CIR module. The firmware implements a kind of - * communication protocol using the SRAM module as a shared memory. The IT8512 - * specification is publicly available on ITE's web site, but the communication - * protocol is not, so it was reverse-engineered. - */ - -/* ITE8709 Registers addresses and values (reverse-engineered) */ -#define ITE8709_MODE 0x1a -#define ITE8709_REG_ADR 0x1b -#define ITE8709_REG_VAL 0x1c -#define ITE8709_IIR 0x1e /* Interrupt identification register */ -#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */ -#define ITE8709_FIFO_START 0x20 - -#define ITE8709_MODE_READY 0X00 -#define ITE8709_MODE_WRITE 0X01 -#define ITE8709_MODE_READ 0X02 -#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */ -#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */ -#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */ - -/* - * IT8512 CIR-module registers addresses and values - * (from IT8512 E/F specification v0.4.1) - */ -#define IT8512_REG_MSTCR 0x01 /* Master control register */ -#define IT8512_REG_IER 0x02 /* Interrupt enable register */ -#define IT8512_REG_CFR 0x04 /* Carrier frequency register */ -#define IT8512_REG_RCR 0x05 /* Receive control register */ -#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */ -#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */ - -#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */ -#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */ -#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */ -#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */ -#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */ -#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */ -#define IT8512_IER_IEC 0x80 /* Enable interrupt request */ -#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */ -#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */ -#define IT8512_RCR_RXACT 0x08 /* Receiver active */ -#define IT8512_RCR_RXEN 0x80 /* Receiver enable */ -#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */ - -/* Actual values used by this driver */ -#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25 -#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ -#define CFG_DCR IT8512_RCR_RXDCR_1 -#define CFG_BDR IT8512_BDR_6 -#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */ - -static int debug; - -struct ite8709_device { - int use_count; - int io; - int irq; - spinlock_t hardware_lock; - __u64 acc_pulse; - __u64 acc_space; - char lastbit; - struct timeval last_tv; - struct lirc_driver driver; - struct tasklet_struct tasklet; - char force_rearm; - char rearmed; - char device_busy; -}; - -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - - -static unsigned char ite8709_read(struct ite8709_device *dev, - unsigned char port) -{ - outb(port, dev->io); - return inb(dev->io+1); -} - -static void ite8709_write(struct ite8709_device *dev, unsigned char port, - unsigned char data) -{ - outb(port, dev->io); - outb(data, dev->io+1); -} - -static void ite8709_wait_device(struct ite8709_device *dev) -{ - int i = 0; - /* - * loop until device tells it's ready to continue - * iterations count is usually ~750 but can sometimes achieve 13000 - */ - for (i = 0; i < 15000; i++) { - udelay(2); - if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY) - break; - } -} - -static void ite8709_write_register(struct ite8709_device *dev, - unsigned char reg_adr, unsigned char reg_value) -{ - ite8709_wait_device(dev); - - ite8709_write(dev, ITE8709_REG_VAL, reg_value); - ite8709_write(dev, ITE8709_REG_ADR, reg_adr); - ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE); -} - -static void ite8709_init_hardware(struct ite8709_device *dev) -{ - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 1; - spin_unlock_irq(&dev->hardware_lock); - - ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff); - ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff); - ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ); - ite8709_write_register(dev, IT8512_REG_IER, - IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE); - ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR); - ite8709_write_register(dev, IT8512_REG_MSTCR, - CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 0; - spin_unlock_irq(&dev->hardware_lock); - - tasklet_enable(&dev->tasklet); -} - -static void ite8709_drop_hardware(struct ite8709_device *dev) -{ - tasklet_disable(&dev->tasklet); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 1; - spin_unlock_irq(&dev->hardware_lock); - - ite8709_write_register(dev, IT8512_REG_RCR, 0); - ite8709_write_register(dev, IT8512_REG_MSTCR, - IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 0; - spin_unlock_irq(&dev->hardware_lock); -} - -static int ite8709_set_use_inc(void *data) -{ - struct ite8709_device *dev; - dev = data; - if (dev->use_count == 0) - ite8709_init_hardware(dev); - dev->use_count++; - return 0; -} - -static void ite8709_set_use_dec(void *data) -{ - struct ite8709_device *dev; - dev = data; - dev->use_count--; - if (dev->use_count == 0) - ite8709_drop_hardware(dev); -} - -static void ite8709_add_read_queue(struct ite8709_device *dev, int flag, - __u64 val) -{ - int value; - - dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space"); - - value = (val > PULSE_MASK) ? PULSE_MASK : val; - if (flag) - value |= PULSE_BIT; - - if (!lirc_buffer_full(dev->driver.rbuf)) { - lirc_buffer_write(dev->driver.rbuf, (void *) &value); - wake_up(&dev->driver.rbuf->wait_poll); - } -} - -static irqreturn_t ite8709_interrupt(int irq, void *dev_id) -{ - unsigned char data; - int iir, rfsr, i; - int fifo = 0; - char bit; - struct timeval curr_tv; - - /* Bit duration in microseconds */ - const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR); - - struct ite8709_device *dev; - dev = dev_id; - - /* - * If device is busy, we simply discard data because we are in one of - * these two cases : shutting down or rearming the device, so this - * doesn't really matter and this avoids waiting too long in IRQ ctx - */ - spin_lock(&dev->hardware_lock); - if (dev->device_busy) { - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - } - - iir = ite8709_read(dev, ITE8709_IIR); - - switch (iir) { - case ITE8709_IIR_RFOI: - dprintk("fifo overrun, scheduling forced rearm just in case\n"); - dev->force_rearm = 1; - tasklet_schedule(&dev->tasklet); - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - - case ITE8709_IIR_RDAI: - rfsr = ite8709_read(dev, ITE8709_RFSR); - fifo = rfsr & ITE8709_RFSR_MASK; - if (fifo > 32) - fifo = 32; - dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo); - - if (dev->rearmed) { - do_gettimeofday(&curr_tv); - dev->acc_space += 1000000ull - * (curr_tv.tv_sec - dev->last_tv.tv_sec) - + (curr_tv.tv_usec - dev->last_tv.tv_usec); - dev->rearmed = 0; - } - for (i = 0; i < fifo; i++) { - data = ite8709_read(dev, i+ITE8709_FIFO_START); - data = ~data; - /* Loop through */ - for (bit = 0; bit < 8; ++bit) { - if ((data >> bit) & 1) { - dev->acc_pulse += bit_duration; - if (dev->lastbit == 0) { - ite8709_add_read_queue(dev, 0, - dev->acc_space); - dev->acc_space = 0; - } - } else { - dev->acc_space += bit_duration; - if (dev->lastbit == 1) { - ite8709_add_read_queue(dev, 1, - dev->acc_pulse); - dev->acc_pulse = 0; - } - } - dev->lastbit = (data >> bit) & 1; - } - } - ite8709_write(dev, ITE8709_RFSR, 0); - - if (dev->acc_space > CFG_TIMEOUT) { - dprintk("scheduling rearm IRQ\n"); - do_gettimeofday(&dev->last_tv); - dev->force_rearm = 0; - tasklet_schedule(&dev->tasklet); - } - - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - - default: - /* not our irq */ - dprintk("unknown IRQ (shouldn't happen) !!\n"); - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_NONE); - } -} - -static void ite8709_rearm_irq(unsigned long data) -{ - struct ite8709_device *dev; - unsigned long flags; - dev = (struct ite8709_device *) data; - - spin_lock_irqsave(&dev->hardware_lock, flags); - dev->device_busy = 1; - spin_unlock_irqrestore(&dev->hardware_lock, flags); - - if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) { - dprintk("rearming IRQ\n"); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXACT | CFG_DCR); - ite8709_write_register(dev, IT8512_REG_MSTCR, - CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); - if (!dev->force_rearm) - dev->rearmed = 1; - dev->force_rearm = 0; - } - - spin_lock_irqsave(&dev->hardware_lock, flags); - dev->device_busy = 0; - spin_unlock_irqrestore(&dev->hardware_lock, flags); -} - -static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno, - char *msg) -{ - if (msg != NULL) - printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg); - - switch (stage) { - case 6: - if (dev->use_count > 0) - ite8709_drop_hardware(dev); - case 5: - free_irq(dev->irq, dev); - case 4: - release_region(dev->io, 2); - case 3: - lirc_unregister_driver(dev->driver.minor); - case 2: - lirc_buffer_free(dev->driver.rbuf); - kfree(dev->driver.rbuf); - case 1: - kfree(dev); - case 0: - ; - } - - return errno; -} - -static int __devinit ite8709_pnp_probe(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) -{ - struct lirc_driver *driver; - struct ite8709_device *ite8709_dev; - int ret; - - /* Check resources validity */ - if (!pnp_irq_valid(dev, 0)) - return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ"); - if (!pnp_port_valid(dev, 2)) - return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port"); - - /* Allocate memory for device struct */ - ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL); - if (ite8709_dev == NULL) - return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed"); - pnp_set_drvdata(dev, ite8709_dev); - - /* Initialize device struct */ - ite8709_dev->use_count = 0; - ite8709_dev->irq = pnp_irq(dev, 0); - ite8709_dev->io = pnp_port_start(dev, 2); - ite8709_dev->hardware_lock = - __SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock); - ite8709_dev->acc_pulse = 0; - ite8709_dev->acc_space = 0; - ite8709_dev->lastbit = 0; - do_gettimeofday(&ite8709_dev->last_tv); - tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq, - (long) ite8709_dev); - ite8709_dev->force_rearm = 0; - ite8709_dev->rearmed = 0; - ite8709_dev->device_busy = 0; - - /* Initialize driver struct */ - driver = &ite8709_dev->driver; - strcpy(driver->name, LIRC_DRIVER_NAME); - driver->minor = -1; - driver->code_length = sizeof(int) * 8; - driver->sample_rate = 0; - driver->features = LIRC_CAN_REC_MODE2; - driver->data = ite8709_dev; - driver->add_to_buf = NULL; - driver->set_use_inc = ite8709_set_use_inc; - driver->set_use_dec = ite8709_set_use_dec; - driver->dev = &dev->dev; - driver->owner = THIS_MODULE; - - /* Initialize LIRC buffer */ - driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!driver->rbuf) - return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, - "can't allocate lirc_buffer"); - if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) - return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, - "lirc_buffer_init() failed"); - - /* Register LIRC driver */ - ret = lirc_register_driver(driver); - if (ret < 0) - return ite8709_cleanup(ite8709_dev, 2, ret, - "lirc_register_driver() failed"); - - /* Reserve I/O port access */ - if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME)) - return ite8709_cleanup(ite8709_dev, 3, -EBUSY, - "i/o port already in use"); - - /* Reserve IRQ line */ - ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0, - LIRC_DRIVER_NAME, ite8709_dev); - if (ret < 0) - return ite8709_cleanup(ite8709_dev, 4, ret, - "IRQ already in use"); - - /* Initialize hardware */ - ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */ - - printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n", - ite8709_dev->irq, ite8709_dev->io); - - return 0; -} - -static void __devexit ite8709_pnp_remove(struct pnp_dev *dev) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - ite8709_cleanup(ite8709_dev, 6, 0, NULL); - - printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n"); -} - -#ifdef CONFIG_PM -static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - if (ite8709_dev->use_count > 0) - ite8709_drop_hardware(ite8709_dev); - - return 0; -} - -static int ite8709_pnp_resume(struct pnp_dev *dev) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - if (ite8709_dev->use_count > 0) - ite8709_init_hardware(ite8709_dev); - - return 0; -} -#else -#define ite8709_pnp_suspend NULL -#define ite8709_pnp_resume NULL -#endif - -static const struct pnp_device_id pnp_dev_table[] = { - {"ITE8709", 0}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static struct pnp_driver ite8709_pnp_driver = { - .name = LIRC_DRIVER_NAME, - .probe = ite8709_pnp_probe, - .remove = __devexit_p(ite8709_pnp_remove), - .suspend = ite8709_pnp_suspend, - .resume = ite8709_pnp_resume, - .id_table = pnp_dev_table, -}; - -static int __init ite8709_init_module(void) -{ - return pnp_register_driver(&ite8709_pnp_driver); -} -module_init(ite8709_init_module); - -static void __exit ite8709_cleanup_module(void) -{ - pnp_unregister_driver(&ite8709_pnp_driver); -} -module_exit(ite8709_cleanup_module); - -MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port"); -MODULE_AUTHOR("Grégory Lardière"); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); -- cgit v1.2.3 From 2aefbc1acb5370b0753d16c9d6e65e01fe586c75 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 17 Mar 2011 21:08:55 -0300 Subject: [media] tm6000: fix s-video input Add compatibility for composite and s-video inputs. Some TV cards hasn't it. Fix S-Video input, the s-video cable has only video signals no audio. Call the function of audio configure kill chroma in signal. only b/w video. Known bugs: - after s-video the audio for radio didn't work, TV crashed hardly - after composite TV crashed hardly too. P.S. After this patch I'll want to rework the procedure of configure video. Now it has a lot of junk and dubles. With my best regards, Dmitry. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-cards.c | 46 +++++++++++++++++++++-------------- drivers/staging/tm6000/tm6000-stds.c | 2 -- drivers/staging/tm6000/tm6000-video.c | 17 ++++++++++--- drivers/staging/tm6000/tm6000.h | 2 ++ 4 files changed, 43 insertions(+), 24 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 88144a12745d..146c7e86deca 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -235,11 +235,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_input_comp = 1, + .has_input_svid = 1, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -255,11 +257,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 1, + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_input_comp = 1, + .has_input_svid = 1, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -327,10 +331,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_input_comp = 0, + .has_input_svid = 0, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -346,10 +353,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_input_comp = 0, + .has_input_svid = 0, }, .gpio = { .tuner_reset = TM6010_GPIO_0, diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c index a4c07e5a6f1d..da3e51bde109 100644 --- a/drivers/staging/tm6000/tm6000-stds.c +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -1161,8 +1161,6 @@ int tm6000_set_standard(struct tm6000_core *dev, v4l2_std_id * norm) rc = tm6000_load_std(dev, svideo_stds[i].common, sizeof(svideo_stds[i]. common)); - tm6000_set_audio_std(dev, svideo_stds[i].audio_default_std); - goto ret; } } diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index b5503409ca55..c80a316d9d8f 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -1080,18 +1080,27 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + switch (inp->index) { case TM6000_INPUT_TV: inp->type = V4L2_INPUT_TYPE_TUNER; strcpy(inp->name, "Television"); break; case TM6000_INPUT_COMPOSITE: - inp->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(inp->name, "Composite"); + if (dev->caps.has_input_comp) { + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name, "Composite"); + } else + return -EINVAL; break; case TM6000_INPUT_SVIDEO: - inp->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(inp->name, "S-Video"); + if (dev->caps.has_input_svid) { + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name, "S-Video"); + } else + return -EINVAL; break; default: return -EINVAL; diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h index ccd120fe1340..99ae50e82b28 100644 --- a/drivers/staging/tm6000/tm6000.h +++ b/drivers/staging/tm6000/tm6000.h @@ -129,6 +129,8 @@ struct tm6000_capabilities { unsigned int has_zl10353:1; unsigned int has_eeprom:1; unsigned int has_remote:1; + unsigned int has_input_comp:1; + unsigned int has_input_svid:1; }; struct tm6000_dvb { -- cgit v1.2.3 From 12d896e1c17978599a98510fdfaff27a4e82c0bf Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:06:43 -0300 Subject: [media] lirc_zilog: Restore checks for existence of the IR_tx object This reverts commit 8090232a237ab62e22307fc060097da1a283dd66 and adds an additional check for ir->tx == NULL. The user may need us to handle an RX only unit. Apparently there are TV capture units in existence with Rx only wiring and/or RX only firmware for the on-board Zilog Z8 IR unit. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 0aad0d7a74a3..7389b77ece8c 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -209,7 +209,8 @@ static int add_to_buf(struct IR *ir) return -ENODATA; } schedule_timeout((100 * HZ + 999) / 1000); - ir->tx->need_boot = 1; + if (ir->tx != NULL) + ir->tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); @@ -1032,9 +1033,10 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) int result; unsigned long mode, features = 0; - features |= LIRC_CAN_SEND_PULSE; if (ir->rx != NULL) features |= LIRC_CAN_REC_LIRCCODE; + if (ir->tx != NULL) + features |= LIRC_CAN_SEND_PULSE; switch (cmd) { case LIRC_GET_LENGTH: @@ -1061,9 +1063,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) result = -EINVAL; break; case LIRC_GET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); break; case LIRC_SET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + result = get_user(mode, (unsigned long *) arg); if (!result && mode != LIRC_MODE_PULSE) return -EINVAL; @@ -1242,8 +1250,10 @@ static int ir_remove(struct i2c_client *client) } /* Good-bye Tx */ - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); + if (ir->tx != NULL) { + i2c_set_clientdata(ir->tx->c, NULL); + kfree(ir->tx); + } /* Good-bye IR */ del_ir_device(ir); @@ -1393,9 +1403,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * after registering with lirc as otherwise hotplug seems to take * 10s to create the lirc device. */ - ret = tx_init(ir->tx); - if (ret != 0) - goto out_unregister; + if (ir->tx != NULL) { + /* Special TX init */ + ret = tx_init(ir->tx); + if (ret != 0) + goto out_unregister; + } zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); -- cgit v1.2.3 From 9b28500a59d7ee5da5adb43bcb384391cac401c4 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:25:47 -0300 Subject: [media] lirc_zilog: Remove broken, ineffective reference counting The set_use_inc() and set_use_dec() functions tried to lock the underlying bridge driver device instance in memory by changing the use count on the device's i2c_clients. This worked for PCI devices (ivtv, cx18, bttv). It doesn't work for hot-pluggable usb devices (pvrusb2 and hdpvr). With usb device instances, the driver may get locked into memory, but the unplugged hardware is gone. The set_use_inc() set_use_dec() functions also tried to have lirc_zilog change its own module refernce count, which is racy and not guaranteed to work. The lirc_dev module does actually perform proper module ref count manipulation on the lirc_zilog module, so there is need for lirc_zilog to attempt a buggy module get on itself anyway. lirc_zilog also errantly called these functions on itself in open() and close(), but lirc_dev did that already too. So let's just gut the bodies of the set_use_*() functions, and remove the extra calls to them from within lirc_zilog. Proper reference counting of the struct IR, IR_rx, and IR_tx objects -- to handle the case when the underlying bttv, ivtv, cx18, hdpvr, or pvrusb2 bridge driver module or device instance goes away -- will be added in subsequent patches. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 7389b77ece8c..3a912575e12d 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -305,34 +305,12 @@ static int lirc_thread(void *arg) static int set_use_inc(void *data) { - struct IR *ir = data; - - if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0) - return -ENODEV; - - /* lock bttv in memory while /dev/lirc is in use */ - /* - * this is completely broken code. lirc_unregister_driver() - * must be possible even when the device is open - */ - if (ir->rx != NULL) - i2c_use_client(ir->rx->c); - if (ir->tx != NULL) - i2c_use_client(ir->tx->c); - return 0; } static void set_use_dec(void *data) { - struct IR *ir = data; - - if (ir->rx) - i2c_release_client(ir->rx->c); - if (ir->tx) - i2c_release_client(ir->tx->c); - if (ir->l.owner != NULL) - module_put(ir->l.owner); + return; } /* safe read of a uint32 (always network byte order) */ @@ -1098,7 +1076,6 @@ static struct IR *find_ir_device_by_minor(unsigned int minor) static int open(struct inode *node, struct file *filep) { struct IR *ir; - int ret; unsigned int minor = MINOR(node->i_rdev); /* find our IR struct */ @@ -1112,12 +1089,6 @@ static int open(struct inode *node, struct file *filep) /* increment in use count */ mutex_lock(&ir->ir_lock); ++ir->open; - ret = set_use_inc(ir); - if (ret != 0) { - --ir->open; - mutex_unlock(&ir->ir_lock); - return ret; - } mutex_unlock(&ir->ir_lock); /* stash our IR struct */ @@ -1139,7 +1110,6 @@ static int close(struct inode *node, struct file *filep) /* decrement in use count */ mutex_lock(&ir->ir_lock); --ir->open; - set_use_dec(ir); mutex_unlock(&ir->ir_lock); return 0; -- cgit v1.2.3 From 5c07134fff9e6b1f9a3c75e62bbb827d1faa72eb Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:48:32 -0300 Subject: [media] lirc_zilog: Convert ir_device instance array to a linked list Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 59 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 28 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 3a912575e12d..39f7b53f0785 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -89,6 +89,8 @@ struct IR_tx { }; struct IR { + struct list_head list; + struct lirc_driver l; struct mutex ir_lock; @@ -99,9 +101,9 @@ struct IR { struct IR_tx *tx; }; -/* Minor -> data mapping */ -static struct mutex ir_devices_lock; -static struct IR *ir_devices[MAX_IRCTL_DEVICES]; +/* IR transceiver instance object list */ +static DEFINE_MUTEX(ir_devices_lock); +static LIST_HEAD(ir_devices_list); /* Block size for IR transmitter */ #define TX_BLOCK_SIZE 99 @@ -1063,10 +1065,16 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) /* ir_devices_lock must be held */ static struct IR *find_ir_device_by_minor(unsigned int minor) { - if (minor >= MAX_IRCTL_DEVICES) + struct IR *ir; + + if (list_empty(&ir_devices_list)) return NULL; - return ir_devices[minor]; + list_for_each_entry(ir, &ir_devices_list, list) + if (ir->l.minor == minor) + return ir; + + return NULL; } /* @@ -1172,25 +1180,21 @@ static void destroy_rx_kthread(struct IR_rx *rx) /* ir_devices_lock must be held */ static int add_ir_device(struct IR *ir) { - int i; - - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] == NULL) { - ir_devices[i] = ir; - break; - } - - return i == MAX_IRCTL_DEVICES ? -ENOMEM : i; + list_add_tail(&ir->list, &ir_devices_list); + return 0; } /* ir_devices_lock must be held */ static void del_ir_device(struct IR *ir) { - int i; + struct IR *p; + + if (list_empty(&ir_devices_list)) + return; - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] == ir) { - ir_devices[i] = NULL; + list_for_each_entry(p, &ir_devices_list, list) + if (p == ir) { + list_del(&p->list); break; } } @@ -1237,17 +1241,16 @@ static int ir_remove(struct i2c_client *client) /* ir_devices_lock must be held */ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) { - int i; - struct IR *ir = NULL; + struct IR *ir; - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] != NULL && - ir_devices[i]->adapter == adapter) { - ir = ir_devices[i]; - break; - } + if (list_empty(&ir_devices_list)) + return NULL; + + list_for_each_entry(ir, &ir_devices_list, list) + if (ir->adapter == adapter) + return ir; - return ir; + return NULL; } static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1284,6 +1287,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_no_ir; } /* store for use in ir_probe() again, and open() later on */ + INIT_LIST_HEAD(&ir->list); ret = add_ir_device(ir); if (ret) goto out_free_ir; @@ -1421,7 +1425,6 @@ static int __init zilog_init(void) zilog_notify("Zilog/Hauppauge IR driver initializing\n"); mutex_init(&tx_data_lock); - mutex_init(&ir_devices_lock); request_module("firmware_class"); -- cgit v1.2.3 From a30648550f2f758c9c00c493c8919f89f65438c4 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 22:04:24 -0300 Subject: [media] lirc_zilog: Convert the instance open count to an atomic_t The open count is simply used for deciding if the Rx polling thread needs to poll the IR chip for userspace. Simplify the manipulation of the open count by using an atomic_t and not requiring a lock The polling thread errantly didn't try to take the lock anyway. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 39f7b53f0785..c857b99e5ace 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -94,7 +94,7 @@ struct IR { struct lirc_driver l; struct mutex ir_lock; - int open; + atomic_t open_count; struct i2c_adapter *adapter; struct IR_rx *rx; @@ -279,7 +279,7 @@ static int lirc_thread(void *arg) set_current_state(TASK_INTERRUPTIBLE); /* if device not opened, we can sleep half a second */ - if (!ir->open) { + if (atomic_read(&ir->open_count) == 0) { schedule_timeout(HZ/2); continue; } @@ -1094,10 +1094,7 @@ static int open(struct inode *node, struct file *filep) if (ir == NULL) return -ENODEV; - /* increment in use count */ - mutex_lock(&ir->ir_lock); - ++ir->open; - mutex_unlock(&ir->ir_lock); + atomic_inc(&ir->open_count); /* stash our IR struct */ filep->private_data = ir; @@ -1115,10 +1112,7 @@ static int close(struct inode *node, struct file *filep) return -ENODEV; } - /* decrement in use count */ - mutex_lock(&ir->ir_lock); - --ir->open; - mutex_unlock(&ir->ir_lock); + atomic_dec(&ir->open_count); return 0; } @@ -1294,6 +1288,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->adapter = adap; mutex_init(&ir->ir_lock); + atomic_set(&ir->open_count, 0); /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); -- cgit v1.2.3 From 9a55a2b37eb7e4941ef55aadb858b615d229afee Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 00:04:15 -0300 Subject: [media] lirc_zilog: Use kernel standard methods for marking device non-seekable lirc_zilog had its own llseek stub that returned -ESPIPE. Get rid of it and use the kernel's no_llseek() and nonseekable_open() functions instead. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index c857b99e5ace..720ef6739d0e 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -712,12 +712,6 @@ static int tx_init(struct IR_tx *tx) return 0; } -/* do nothing stub to make LIRC happy */ -static loff_t lseek(struct file *filep, loff_t offset, int orig) -{ - return -ESPIPE; -} - /* copied from lirc_dev */ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { @@ -1099,6 +1093,7 @@ static int open(struct inode *node, struct file *filep) /* stash our IR struct */ filep->private_data = ir; + nonseekable_open(node, filep); return 0; } @@ -1150,7 +1145,7 @@ static struct i2c_driver driver = { static const struct file_operations lirc_fops = { .owner = THIS_MODULE, - .llseek = lseek, + .llseek = no_llseek, .read = read, .write = write, .poll = poll, -- cgit v1.2.3 From 915e54733de4a36742ab78b4768750ce72662f85 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 02:10:42 -0300 Subject: [media] lirc_zilog: Don't acquire the rx->buf_lock in the poll() function There is no need to take the rx->buf_lock in the the poll() function as all the underling calls made on objects in the rx->buf lirc_buffer object are protected by spinlocks. Corrected a bad error return value in poll(): return POLLERR instead of -ENODEV. Added some comments to poll() for when, in the future, I forget what poll() and poll_wait() are supposed to do. [Jarod: minor debug spew fix] Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 720ef6739d0e..695c3df93ca8 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -985,19 +985,26 @@ static unsigned int poll(struct file *filep, poll_table *wait) unsigned int ret; dprintk("poll called\n"); - if (rx == NULL) - return -ENODEV; - mutex_lock(&rx->buf_lock); + if (rx == NULL) { + /* + * Revisit this, if our poll function ever reports writeable + * status for Tx + */ + dprintk("poll result = POLLERR\n"); + return POLLERR; + } + /* + * Add our lirc_buffer's wait_queue to the poll_table. A wake up on + * that buffer's wait queue indicates we may have a new poll status. + */ poll_wait(filep, &rx->buf.wait_poll, wait); - dprintk("poll result = %s\n", - lirc_buffer_empty(&rx->buf) ? "0" : "POLLIN|POLLRDNORM"); - + /* Indicate what ops could happen immediately without blocking */ ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM); - mutex_unlock(&rx->buf_lock); + dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); return ret; } -- cgit v1.2.3 From 8152b760effbff11cb4eef6292ae3b47719ee792 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 02:34:13 -0300 Subject: [media] lirc_zilog: Remove unneeded rx->buf_lock Remove the rx->buf_lock that protected the rx->buf lirc_buffer. The underlying operations on the objects within the lirc_buffer are already protected by spinlocks, or the objects are constant (e.g. chunk_size). Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 695c3df93ca8..a62b94bf6a1a 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -67,9 +67,8 @@ struct IR_rx { /* RX device */ struct i2c_client *c; - /* RX device buffer & lock */ + /* RX device buffer */ struct lirc_buffer buf; - struct mutex buf_lock; /* RX polling thread data */ struct task_struct *task; @@ -718,18 +717,15 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; int ret = 0, written = 0; + unsigned int m; DECLARE_WAITQUEUE(wait, current); dprintk("read called\n"); if (rx == NULL) return -ENODEV; - if (mutex_lock_interruptible(&rx->buf_lock)) - return -ERESTARTSYS; - if (n % rx->buf.chunk_size) { dprintk("read result = -EINVAL\n"); - mutex_unlock(&rx->buf_lock); return -EINVAL; } @@ -767,19 +763,19 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) set_current_state(TASK_INTERRUPTIBLE); } else { unsigned char buf[rx->buf.chunk_size]; - lirc_buffer_read(&rx->buf, buf); - ret = copy_to_user((void *)outbuf+written, buf, - rx->buf.chunk_size); - written += rx->buf.chunk_size; + m = lirc_buffer_read(&rx->buf, buf); + if (m == rx->buf.chunk_size) { + ret = copy_to_user((void *)outbuf+written, buf, + rx->buf.chunk_size); + written += rx->buf.chunk_size; + } } } remove_wait_queue(&rx->buf.wait_poll, &wait); set_current_state(TASK_RUNNING); - mutex_unlock(&rx->buf_lock); - dprintk("read result = %s (%d)\n", - ret ? "-EFAULT" : "OK", ret); + dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); return ret ? ret : written; } @@ -1327,7 +1323,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) goto out_free_xx; - mutex_init(&ir->rx->buf_lock); ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v1.2.3 From bcbd165509da05a2317ac7dce3c6b952e98a01ef Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 22:32:44 -0300 Subject: [media] lirc_zilog: Always allocate a Rx lirc_buffer object Always allocate a lirc_buffer object, instead of just upon setup of the Rx i2c_client. If we do not allocate a lirc_buffer object, because we are not handling the Rx i2c_client, lirc_dev will allocate its own lirc_buffer anyway and not tell us about its location. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 62 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index a62b94bf6a1a..053fddb8e448 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -67,9 +67,6 @@ struct IR_rx { /* RX device */ struct i2c_client *c; - /* RX device buffer */ - struct lirc_buffer buf; - /* RX polling thread data */ struct task_struct *task; @@ -91,6 +88,7 @@ struct IR { struct list_head list; struct lirc_driver l; + struct lirc_buffer rbuf; struct mutex ir_lock; atomic_t open_count; @@ -157,12 +155,13 @@ static int add_to_buf(struct IR *ir) int ret; int failures = 0; unsigned char sendbuf[1] = { 0 }; + struct lirc_buffer *rbuf = ir->l.rbuf; struct IR_rx *rx = ir->rx; if (rx == NULL) return -ENXIO; - if (lirc_buffer_full(&rx->buf)) { + if (lirc_buffer_full(rbuf)) { dprintk("buffer overflow\n"); return -EOVERFLOW; } @@ -250,9 +249,9 @@ static int add_to_buf(struct IR *ir) codes[1] = code & 0xff; /* return it */ - lirc_buffer_write(&rx->buf, codes); + lirc_buffer_write(rbuf, codes); ++got_data; - } while (!lirc_buffer_full(&rx->buf)); + } while (!lirc_buffer_full(rbuf)); return 0; } @@ -270,7 +269,7 @@ static int add_to_buf(struct IR *ir) static int lirc_thread(void *arg) { struct IR *ir = arg; - struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; dprintk("poll thread started\n"); @@ -297,7 +296,7 @@ static int lirc_thread(void *arg) if (kthread_should_stop()) break; if (!add_to_buf(ir)) - wake_up_interruptible(&rx->buf.wait_poll); + wake_up_interruptible(&rbuf->wait_poll); } dprintk("poll thread ended\n"); @@ -716,6 +715,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; int ret = 0, written = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); @@ -724,7 +724,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) if (rx == NULL) return -ENODEV; - if (n % rx->buf.chunk_size) { + if (n % rbuf->chunk_size) { dprintk("read result = -EINVAL\n"); return -EINVAL; } @@ -734,7 +734,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * to avoid losing scan code (in case when queue is awaken somewhere * between while condition checking and scheduling) */ - add_wait_queue(&rx->buf.wait_poll, &wait); + add_wait_queue(&rbuf->wait_poll, &wait); set_current_state(TASK_INTERRUPTIBLE); /* @@ -742,7 +742,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * mode and 'copy_to_user' is happy, wait for data. */ while (written < n && ret == 0) { - if (lirc_buffer_empty(&rx->buf)) { + if (lirc_buffer_empty(rbuf)) { /* * According to the read(2) man page, 'written' can be * returned as less than 'n', instead of blocking @@ -762,17 +762,17 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) schedule(); set_current_state(TASK_INTERRUPTIBLE); } else { - unsigned char buf[rx->buf.chunk_size]; - m = lirc_buffer_read(&rx->buf, buf); - if (m == rx->buf.chunk_size) { + unsigned char buf[rbuf->chunk_size]; + m = lirc_buffer_read(rbuf, buf); + if (m == rbuf->chunk_size) { ret = copy_to_user((void *)outbuf+written, buf, - rx->buf.chunk_size); - written += rx->buf.chunk_size; + rbuf->chunk_size); + written += rbuf->chunk_size; } } } - remove_wait_queue(&rx->buf.wait_poll, &wait); + remove_wait_queue(&rbuf->wait_poll, &wait); set_current_state(TASK_RUNNING); dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); @@ -978,6 +978,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; dprintk("poll called\n"); @@ -995,10 +996,10 @@ static unsigned int poll(struct file *filep, poll_table *wait) * Add our lirc_buffer's wait_queue to the poll_table. A wake up on * that buffer's wait queue indicates we may have a new poll status. */ - poll_wait(filep, &rx->buf.wait_poll, wait); + poll_wait(filep, &rbuf->wait_poll, wait); /* Indicate what ops could happen immediately without blocking */ - ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM); + ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); return ret; @@ -1209,8 +1210,6 @@ static int ir_remove(struct i2c_client *client) /* Good-bye Rx */ destroy_rx_kthread(ir->rx); if (ir->rx != NULL) { - if (ir->rx->buf.fifo_initialized) - lirc_buffer_free(&ir->rx->buf); i2c_set_clientdata(ir->rx->c, NULL); kfree(ir->rx); } @@ -1222,6 +1221,8 @@ static int ir_remove(struct i2c_client *client) } /* Good-bye IR */ + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); del_ir_device(ir); kfree(ir); @@ -1292,11 +1293,17 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = minor; /* module option */ ir->l.code_length = 13; - ir->l.rbuf = NULL; + ir->l.chunk_size = 2; + ir->l.buffer_size = BUFLEN / 2; + ir->l.rbuf = &ir->rbuf; ir->l.fops = &lirc_fops; ir->l.data = ir; ir->l.dev = &adap->dev; ir->l.sample_rate = 0; + ret = lirc_buffer_init(ir->l.rbuf, + ir->l.chunk_size, ir->l.buffer_size); + if (ret) + goto out_free_ir; } if (tx_probe) { @@ -1319,16 +1326,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } - ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2); - if (ret) - goto out_free_xx; - ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; - - /* set lirc_dev stuff */ - ir->l.rbuf = &ir->rx->buf; } i2c_set_clientdata(client, ir); @@ -1388,8 +1388,6 @@ out_free_thread: destroy_rx_kthread(ir->rx); out_free_xx: if (ir->rx != NULL) { - if (ir->rx->buf.fifo_initialized) - lirc_buffer_free(&ir->rx->buf); if (ir->rx->c != NULL) i2c_set_clientdata(ir->rx->c, NULL); kfree(ir->rx); @@ -1399,6 +1397,8 @@ out_free_xx: i2c_set_clientdata(ir->tx->c, NULL); kfree(ir->tx); } + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); out_free_ir: del_ir_device(ir); kfree(ir); -- cgit v1.2.3 From 534c1eab1e2770dca3cf4616cc4059683020114b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 23:02:20 -0300 Subject: [media] lirc_zilog: Move constants from ir_probe() into the lirc_driver template ir_probe() makes a number of constant assignments into the lirc_driver object after copying in a template. Make better use of the template. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 053fddb8e448..d06788561189 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1116,13 +1116,6 @@ static int close(struct inode *node, struct file *filep) return 0; } -static struct lirc_driver lirc_template = { - .name = "lirc_zilog", - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .owner = THIS_MODULE -}; - static int ir_remove(struct i2c_client *client); static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); @@ -1161,6 +1154,19 @@ static const struct file_operations lirc_fops = { .release = close }; +static struct lirc_driver lirc_template = { + .name = "lirc_zilog", + .minor = -1, + .code_length = 13, + .buffer_size = BUFLEN / 2, + .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ + .chunk_size = 2, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .owner = THIS_MODULE, +}; + static void destroy_rx_kthread(struct IR_rx *rx) { /* end up polling thread */ @@ -1292,14 +1298,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = minor; /* module option */ - ir->l.code_length = 13; - ir->l.chunk_size = 2; - ir->l.buffer_size = BUFLEN / 2; ir->l.rbuf = &ir->rbuf; - ir->l.fops = &lirc_fops; ir->l.data = ir; ir->l.dev = &adap->dev; - ir->l.sample_rate = 0; ret = lirc_buffer_init(ir->l.rbuf, ir->l.chunk_size, ir->l.buffer_size); if (ret) @@ -1314,6 +1315,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } + ir->l.features |= LIRC_CAN_SEND_PULSE; ir->tx->c = client; ir->tx->need_boot = 1; ir->tx->post_tx_ready_poll = @@ -1326,6 +1328,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } + ir->l.features |= LIRC_CAN_REC_LIRCCODE; ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v1.2.3 From 5bd6b0464b68d429bc8a3fe6595d19c39dfc4d95 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 7 Feb 2011 22:30:55 -0300 Subject: [media] lirc_zilog: Add ref counting of struct IR, IR_tx, and IR_rx This is a major change to add pointer reference counting for struct IR, struct IR_tx, and struct IR_rx object instances. This ref counting gets lirc_zilog closer to gracefully handling bridge drivers and hot-unplugged USB devices disappearing out from under lirc_zilog when the /dev/lircN node is still open. (mutexes to protect the i2c_client pointers in struct IR_tx and struct IR_rx still need to be added.) This reference counting also helps lirc_zilog clean up properly when the i2c_clients disappear. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 584 +++++++++++++++++++++++++------------- 1 file changed, 380 insertions(+), 204 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index d06788561189..5df90dbba1c6 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -63,8 +63,14 @@ #include #include +struct IR; + struct IR_rx { + struct kref ref; + struct IR *ir; + /* RX device */ + /* FIXME mutex lock access to this pointer */ struct i2c_client *c; /* RX polling thread data */ @@ -76,7 +82,11 @@ struct IR_rx { }; struct IR_tx { + struct kref ref; + struct IR *ir; + /* TX device */ + /* FIXME mutex lock access to this pointer */ struct i2c_client *c; /* TX additional actions needed */ @@ -85,8 +95,10 @@ struct IR_tx { }; struct IR { + struct kref ref; struct list_head list; + /* FIXME spinlock access to l.features */ struct lirc_driver l; struct lirc_buffer rbuf; @@ -94,11 +106,21 @@ struct IR { atomic_t open_count; struct i2c_adapter *adapter; + + spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */ struct IR_rx *rx; + + spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */ struct IR_tx *tx; }; /* IR transceiver instance object list */ +/* + * This lock is used for the following: + * a. ir_devices_list access, insertions, deletions + * b. struct IR kref get()s and put()s + * c. serialization of ir_probe() for the two i2c_clients for a Z8 + */ static DEFINE_MUTEX(ir_devices_lock); static LIST_HEAD(ir_devices_list); @@ -146,6 +168,157 @@ static int minor = -1; /* minor number */ ## args); \ } while (0) + +/* struct IR reference counting */ +static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + if (ir_devices_lock_held) { + kref_get(&ir->ref); + } else { + mutex_lock(&ir_devices_lock); + kref_get(&ir->ref); + mutex_unlock(&ir_devices_lock); + } + return ir; +} + +static void release_ir_device(struct kref *ref) +{ + struct IR *ir = container_of(ref, struct IR, ref); + + /* + * Things should be in this state by now: + * ir->rx set to NULL and deallocated - happens before ir->rx->ir put() + * ir->rx->task kthread stopped - happens before ir->rx->ir put() + * ir->tx set to NULL and deallocated - happens before ir->tx->ir put() + * ir->open_count == 0 - happens on final close() + * ir_lock, tx_ref_lock, rx_ref_lock, all released + */ + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + lirc_unregister_driver(ir->l.minor); + ir->l.minor = MAX_IRCTL_DEVICES; + } + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); + list_del(&ir->list); + kfree(ir); +} + +static int put_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + int released; + + if (ir_devices_lock_held) + return kref_put(&ir->ref, release_ir_device); + + mutex_lock(&ir_devices_lock); + released = kref_put(&ir->ref, release_ir_device); + mutex_unlock(&ir_devices_lock); + + return released; +} + +/* struct IR_rx reference counting */ +static struct IR_rx *get_ir_rx(struct IR *ir) +{ + struct IR_rx *rx; + + spin_lock(&ir->rx_ref_lock); + rx = ir->rx; + if (rx != NULL) + kref_get(&rx->ref); + spin_unlock(&ir->rx_ref_lock); + return rx; +} + +static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held) +{ + /* end up polling thread */ + if (!IS_ERR_OR_NULL(rx->task)) { + kthread_stop(rx->task); + rx->task = NULL; + /* Put the ir ptr that ir_probe() gave to the rx poll thread */ + put_ir_device(rx->ir, ir_devices_lock_held); + } +} + +static void release_ir_rx(struct kref *ref) +{ + struct IR_rx *rx = container_of(ref, struct IR_rx, ref); + struct IR *ir = rx->ir; + + /* + * This release function can't do all the work, as we want + * to keep the rx_ref_lock a spinlock, and killing the poll thread + * and releasing the ir reference can cause a sleep. That work is + * performed by put_ir_rx() + */ + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */ + ir->rx = NULL; + /* Don't do the kfree(rx) here; we still need to kill the poll thread */ + return; +} + +static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = rx->ir; + + spin_lock(&ir->rx_ref_lock); + released = kref_put(&rx->ref, release_ir_rx); + spin_unlock(&ir->rx_ref_lock); + /* Destroy the rx kthread while not holding the spinlock */ + if (released) { + destroy_rx_kthread(rx, ir_devices_lock_held); + kfree(rx); + /* Make sure we're not still in a poll_table somewhere */ + wake_up_interruptible(&ir->rbuf.wait_poll); + } + /* Do a reference put() for the rx->ir reference, if we released rx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + +/* struct IR_tx reference counting */ +static struct IR_tx *get_ir_tx(struct IR *ir) +{ + struct IR_tx *tx; + + spin_lock(&ir->tx_ref_lock); + tx = ir->tx; + if (tx != NULL) + kref_get(&tx->ref); + spin_unlock(&ir->tx_ref_lock); + return tx; +} + +static void release_ir_tx(struct kref *ref) +{ + struct IR_tx *tx = container_of(ref, struct IR_tx, ref); + struct IR *ir = tx->ir; + + ir->l.features &= ~LIRC_CAN_SEND_PULSE; + /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ + ir->tx = NULL; + kfree(tx); +} + +static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = tx->ir; + + spin_lock(&ir->tx_ref_lock); + released = kref_put(&tx->ref, release_ir_tx); + spin_unlock(&ir->tx_ref_lock); + /* Do a reference put() for the tx->ir reference, if we released tx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + static int add_to_buf(struct IR *ir) { __u16 code; @@ -156,23 +329,29 @@ static int add_to_buf(struct IR *ir) int failures = 0; unsigned char sendbuf[1] = { 0 }; struct lirc_buffer *rbuf = ir->l.rbuf; - struct IR_rx *rx = ir->rx; - - if (rx == NULL) - return -ENXIO; + struct IR_rx *rx; + struct IR_tx *tx; if (lirc_buffer_full(rbuf)) { dprintk("buffer overflow\n"); return -EOVERFLOW; } + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + + tx = get_ir_tx(ir); + /* * service the device as long as it is returning * data and we have space */ do { - if (kthread_should_stop()) - return -ENODATA; + if (kthread_should_stop()) { + ret = -ENODATA; + break; + } /* * Lock i2c bus for the duration. RX/TX chips interfere so @@ -182,7 +361,8 @@ static int add_to_buf(struct IR *ir) if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } /* @@ -196,7 +376,7 @@ static int add_to_buf(struct IR *ir) mutex_unlock(&ir->ir_lock); zilog_error("unable to read from the IR chip " "after 3 resets, giving up\n"); - return ret; + break; } /* Looks like the chip crashed, reset it */ @@ -206,20 +386,23 @@ static int add_to_buf(struct IR *ir) set_current_state(TASK_UNINTERRUPTIBLE); if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } schedule_timeout((100 * HZ + 999) / 1000); - if (ir->tx != NULL) - ir->tx->need_boot = 1; + if (tx != NULL) + tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); + ret = 0; continue; } if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); @@ -235,12 +418,17 @@ static int add_to_buf(struct IR *ir) /* key pressed ? */ if (rx->hdpvr_data_fmt) { - if (got_data && (keybuf[0] == 0x80)) - return 0; - else if (got_data && (keybuf[0] == 0x00)) - return -ENODATA; - } else if ((rx->b[0] & 0x80) == 0) - return got_data ? 0 : -ENODATA; + if (got_data && (keybuf[0] == 0x80)) { + ret = 0; + break; + } else if (got_data && (keybuf[0] == 0x00)) { + ret = -ENODATA; + break; + } + } else if ((rx->b[0] & 0x80) == 0) { + ret = got_data ? 0 : -ENODATA; + break; + } /* look what we have */ code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); @@ -251,9 +439,13 @@ static int add_to_buf(struct IR *ir) /* return it */ lirc_buffer_write(rbuf, codes); ++got_data; + ret = 0; } while (!lirc_buffer_full(rbuf)); - return 0; + if (tx != NULL) + put_ir_tx(tx, false); + put_ir_rx(rx, false); + return ret; } /* @@ -274,14 +466,14 @@ static int lirc_thread(void *arg) dprintk("poll thread started\n"); while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - /* if device not opened, we can sleep half a second */ if (atomic_read(&ir->open_count) == 0) { schedule_timeout(HZ/2); continue; } + set_current_state(TASK_INTERRUPTIBLE); + /* * This is ~113*2 + 24 + jitter (2*repeat gap + code length). * We use this interval as the chip resets every time you poll @@ -564,7 +756,7 @@ static int fw_load(struct IR_tx *tx) } /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c->dev); + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); if (ret != 0) { zilog_error("firmware haup-ir-blaster.bin not available " "(%d)\n", ret); @@ -690,45 +882,26 @@ out: return ret; } -/* initialise the IR TX device */ -static int tx_init(struct IR_tx *tx) -{ - int ret; - - /* Load 'firmware' */ - ret = fw_load(tx); - if (ret != 0) - return ret; - - /* Send boot block */ - ret = send_boot_data(tx); - if (ret != 0) - return ret; - tx->need_boot = 0; - - /* Looks good */ - return 0; -} - /* copied from lirc_dev */ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; - struct IR_rx *rx = ir->rx; + struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; int ret = 0, written = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); dprintk("read called\n"); - if (rx == NULL) - return -ENODEV; - if (n % rbuf->chunk_size) { dprintk("read result = -EINVAL\n"); return -EINVAL; } + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + /* * we add ourselves to the task queue before buffer check * to avoid losing scan code (in case when queue is awaken somewhere @@ -773,6 +946,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) } remove_wait_queue(&rbuf->wait_poll, &wait); + put_ir_rx(rx, false); set_current_state(TASK_RUNNING); dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); @@ -902,17 +1076,19 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; - struct IR_tx *tx = ir->tx; + struct IR_tx *tx; size_t i; int failures = 0; - if (tx == NULL) - return -ENODEV; - /* Validate user parameters */ if (n % sizeof(int)) return -EINVAL; + /* Get a struct IR_tx reference */ + tx = get_ir_tx(ir); + if (tx == NULL) + return -ENXIO; + /* Lock i2c bus for the duration */ mutex_lock(&ir->ir_lock); @@ -923,11 +1099,22 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (copy_from_user(&command, buf + i, sizeof(command))) { mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return -EFAULT; } /* Send boot data first if required */ if (tx->need_boot == 1) { + /* Make sure we have the 'firmware' loaded, first */ + ret = fw_load(tx); + if (ret != 0) { + mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); + if (ret != -ENOMEM) + ret = -EIO; + return ret; + } + /* Prep the chip for transmitting codes */ ret = send_boot_data(tx); if (ret == 0) tx->need_boot = 0; @@ -939,6 +1126,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return ret; } } @@ -956,6 +1144,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, zilog_error("unable to send to the IR chip " "after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return ret; } set_current_state(TASK_UNINTERRUPTIBLE); @@ -969,6 +1158,9 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, /* Release i2c bus */ mutex_unlock(&ir->ir_lock); + /* Give back our struct IR_tx reference */ + put_ir_tx(tx, false); + /* All looks good */ return n; } @@ -977,12 +1169,13 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = filep->private_data; - struct IR_rx *rx = ir->rx; + struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; dprintk("poll called\n"); + rx = get_ir_rx(ir); if (rx == NULL) { /* * Revisit this, if our poll function ever reports writeable @@ -1009,12 +1202,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct IR *ir = filep->private_data; int result; - unsigned long mode, features = 0; + unsigned long mode, features; - if (ir->rx != NULL) - features |= LIRC_CAN_REC_LIRCCODE; - if (ir->tx != NULL) - features |= LIRC_CAN_SEND_PULSE; + features = ir->l.features; switch (cmd) { case LIRC_GET_LENGTH: @@ -1060,19 +1250,24 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return result; } -/* ir_devices_lock must be held */ -static struct IR *find_ir_device_by_minor(unsigned int minor) +static struct IR *get_ir_device_by_minor(unsigned int minor) { struct IR *ir; + struct IR *ret = NULL; - if (list_empty(&ir_devices_list)) - return NULL; + mutex_lock(&ir_devices_lock); - list_for_each_entry(ir, &ir_devices_list, list) - if (ir->l.minor == minor) - return ir; + if (!list_empty(&ir_devices_list)) { + list_for_each_entry(ir, &ir_devices_list, list) { + if (ir->l.minor == minor) { + ret = get_ir_device(ir, true); + break; + } + } + } - return NULL; + mutex_unlock(&ir_devices_lock); + return ret; } /* @@ -1085,9 +1280,7 @@ static int open(struct inode *node, struct file *filep) unsigned int minor = MINOR(node->i_rdev); /* find our IR struct */ - mutex_lock(&ir_devices_lock); - ir = find_ir_device_by_minor(minor); - mutex_unlock(&ir_devices_lock); + ir = get_ir_device_by_minor(minor); if (ir == NULL) return -ENODEV; @@ -1113,6 +1306,7 @@ static int close(struct inode *node, struct file *filep) atomic_dec(&ir->open_count); + put_ir_device(ir, false); return 0; } @@ -1167,78 +1361,23 @@ static struct lirc_driver lirc_template = { .owner = THIS_MODULE, }; -static void destroy_rx_kthread(struct IR_rx *rx) -{ - /* end up polling thread */ - if (rx != NULL && !IS_ERR_OR_NULL(rx->task)) { - kthread_stop(rx->task); - rx->task = NULL; - } -} - -/* ir_devices_lock must be held */ -static int add_ir_device(struct IR *ir) -{ - list_add_tail(&ir->list, &ir_devices_list); - return 0; -} - -/* ir_devices_lock must be held */ -static void del_ir_device(struct IR *ir) -{ - struct IR *p; - - if (list_empty(&ir_devices_list)) - return; - - list_for_each_entry(p, &ir_devices_list, list) - if (p == ir) { - list_del(&p->list); - break; - } -} - static int ir_remove(struct i2c_client *client) { - struct IR *ir = i2c_get_clientdata(client); - - mutex_lock(&ir_devices_lock); - - if (ir == NULL) { - /* We destroyed everything when the first client came through */ - mutex_unlock(&ir_devices_lock); - return 0; + if (strncmp("ir_tx_z8", client->name, 8) == 0) { + struct IR_tx *tx = i2c_get_clientdata(client); + if (tx != NULL) + put_ir_tx(tx, false); + } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { + struct IR_rx *rx = i2c_get_clientdata(client); + if (rx != NULL) + put_ir_rx(rx, false); } - - /* Good-bye LIRC */ - lirc_unregister_driver(ir->l.minor); - - /* Good-bye Rx */ - destroy_rx_kthread(ir->rx); - if (ir->rx != NULL) { - i2c_set_clientdata(ir->rx->c, NULL); - kfree(ir->rx); - } - - /* Good-bye Tx */ - if (ir->tx != NULL) { - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); - } - - /* Good-bye IR */ - if (ir->rbuf.fifo_initialized) - lirc_buffer_free(&ir->rbuf); - del_ir_device(ir); - kfree(ir); - - mutex_unlock(&ir_devices_lock); return 0; } /* ir_devices_lock must be held */ -static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) +static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) { struct IR *ir; @@ -1246,8 +1385,10 @@ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) return NULL; list_for_each_entry(ir, &ir_devices_list, list) - if (ir->adapter == adapter) + if (ir->adapter == adapter) { + get_ir_device(ir, true); return ir; + } return NULL; } @@ -1255,6 +1396,8 @@ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct IR *ir; + struct IR_tx *tx; + struct IR_rx *rx; struct i2c_adapter *adap = client->adapter; int ret; bool tx_probe = false; @@ -1278,133 +1421,166 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) mutex_lock(&ir_devices_lock); /* Use a single struct IR instance for both the Rx and Tx functions */ - ir = find_ir_device_by_adapter(adap); + ir = get_ir_device_by_adapter(adap); if (ir == NULL) { ir = kzalloc(sizeof(struct IR), GFP_KERNEL); if (ir == NULL) { ret = -ENOMEM; goto out_no_ir; } + kref_init(&ir->ref); + /* store for use in ir_probe() again, and open() later on */ INIT_LIST_HEAD(&ir->list); - ret = add_ir_device(ir); - if (ret) - goto out_free_ir; + list_add_tail(&ir->list, &ir_devices_list); ir->adapter = adap; mutex_init(&ir->ir_lock); atomic_set(&ir->open_count, 0); + spin_lock_init(&ir->tx_ref_lock); + spin_lock_init(&ir->rx_ref_lock); /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); - ir->l.minor = minor; /* module option */ - ir->l.rbuf = &ir->rbuf; - ir->l.data = ir; - ir->l.dev = &adap->dev; + /* + * FIXME this is a pointer reference to us, but no refcount. + * + * This OK for now, since lirc_dev currently won't touch this + * buffer as we provide our own lirc_fops. + * + * Currently our own lirc_fops rely on this ir->l.rbuf pointer + */ + ir->l.rbuf = &ir->rbuf; + ir->l.dev = &adap->dev; ret = lirc_buffer_init(ir->l.rbuf, ir->l.chunk_size, ir->l.buffer_size); if (ret) - goto out_free_ir; + goto out_put_ir; } if (tx_probe) { + /* Get the IR_rx instance for later, if already allocated */ + rx = get_ir_rx(ir); + /* Set up a struct IR_tx instance */ - ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); - if (ir->tx == NULL) { + tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + if (tx == NULL) { ret = -ENOMEM; - goto out_free_xx; + goto out_put_xx; } + kref_init(&tx->ref); + ir->tx = tx; ir->l.features |= LIRC_CAN_SEND_PULSE; - ir->tx->c = client; - ir->tx->need_boot = 1; - ir->tx->post_tx_ready_poll = + tx->c = client; + tx->need_boot = 1; + tx->post_tx_ready_poll = (id->driver_data & ID_FLAG_HDPVR) ? false : true; + + /* An ir ref goes to the struct IR_tx instance */ + tx->ir = get_ir_device(ir, true); + + /* A tx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_tx(ir)); + + /* + * Load the 'firmware'. We do this before registering with + * lirc_dev, so the first firmware load attempt does not happen + * after a open() or write() call on the device. + * + * Failure here is not deemed catastrophic, so the receiver will + * still be usable. Firmware load will be retried in write(), + * if it is needed. + */ + fw_load(tx); + + /* Proceed only if the Rx client is also ready or not needed */ + if (rx == NULL && !tx_only) { + zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting" + " on IR Rx.\n", adap->name, adap->nr); + goto out_ok; + } } else { + /* Get the IR_tx instance for later, if already allocated */ + tx = get_ir_tx(ir); + /* Set up a struct IR_rx instance */ - ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); - if (ir->rx == NULL) { + rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + if (rx == NULL) { ret = -ENOMEM; - goto out_free_xx; + goto out_put_xx; } + kref_init(&rx->ref); + ir->rx = rx; ir->l.features |= LIRC_CAN_REC_LIRCCODE; - ir->rx->c = client; - ir->rx->hdpvr_data_fmt = + rx->c = client; + rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; - } - i2c_set_clientdata(client, ir); + /* An ir ref goes to the struct IR_rx instance */ + rx->ir = get_ir_device(ir, true); - /* Proceed only if we have the required Tx and Rx clients ready to go */ - if (ir->tx == NULL || - (ir->rx == NULL && !tx_only)) { - zilog_info("probe of IR %s on %s (i2c-%d) done. Waiting on " - "IR %s.\n", tx_probe ? "Tx" : "Rx", adap->name, - adap->nr, tx_probe ? "Rx" : "Tx"); - goto out_ok; - } + /* An rx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_rx(ir)); - /* initialise RX device */ - if (ir->rx != NULL) { - /* try to fire up polling thread */ - ir->rx->task = kthread_run(lirc_thread, ir, - "zilog-rx-i2c-%d", adap->nr); - if (IS_ERR(ir->rx->task)) { - ret = PTR_ERR(ir->rx->task); + /* + * Start the polling thread. + * It will only perform an empty loop around schedule_timeout() + * until we register with lirc_dev and the first user open() + */ + /* An ir ref goes to the new rx polling kthread */ + rx->task = kthread_run(lirc_thread, get_ir_device(ir, true), + "zilog-rx-i2c-%d", adap->nr); + if (IS_ERR(rx->task)) { + ret = PTR_ERR(rx->task); zilog_error("%s: could not start IR Rx polling thread" "\n", __func__); - goto out_free_xx; + /* Failed kthread, so put back the ir ref */ + put_ir_device(ir, true); + /* Failure exit, so put back rx ref from i2c_client */ + i2c_set_clientdata(client, NULL); + put_ir_rx(rx, true); + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + goto out_put_xx; + } + + /* Proceed only if the Tx client is also ready */ + if (tx == NULL) { + zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting" + " on IR Tx.\n", adap->name, adap->nr); + goto out_ok; } } /* register with lirc */ + ir->l.minor = minor; /* module option: user requested minor number */ ir->l.minor = lirc_register_driver(&ir->l); if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n", __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); ret = -EBADRQC; - goto out_free_thread; - } - - /* - * if we have the tx device, load the 'firmware'. We do this - * after registering with lirc as otherwise hotplug seems to take - * 10s to create the lirc device. - */ - if (ir->tx != NULL) { - /* Special TX init */ - ret = tx_init(ir->tx); - if (ret != 0) - goto out_unregister; + goto out_put_xx; } +out_ok: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); + put_ir_device(ir, true); zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); -out_ok: mutex_unlock(&ir_devices_lock); return 0; -out_unregister: - lirc_unregister_driver(ir->l.minor); -out_free_thread: - destroy_rx_kthread(ir->rx); -out_free_xx: - if (ir->rx != NULL) { - if (ir->rx->c != NULL) - i2c_set_clientdata(ir->rx->c, NULL); - kfree(ir->rx); - } - if (ir->tx != NULL) { - if (ir->tx->c != NULL) - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); - } - if (ir->rbuf.fifo_initialized) - lirc_buffer_free(&ir->rbuf); -out_free_ir: - del_ir_device(ir); - kfree(ir); +out_put_xx: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); +out_put_ir: + put_ir_device(ir, true); out_no_ir: zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n", __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, -- cgit v1.2.3 From d6dbd939b97d271766a58a66d69b27023bc90993 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 20:50:38 -0300 Subject: [media] lirc_zilog: Add locking of the i2c_clients when in use Lock the i2c_client pointers and prevent i2c_client removal when lirc_zilog is perfoming a series of operations that require valid i2c_client pointers. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 5df90dbba1c6..5946a9541c43 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -70,7 +70,7 @@ struct IR_rx { struct IR *ir; /* RX device */ - /* FIXME mutex lock access to this pointer */ + struct mutex client_lock; struct i2c_client *c; /* RX polling thread data */ @@ -86,7 +86,7 @@ struct IR_tx { struct IR *ir; /* TX device */ - /* FIXME mutex lock access to this pointer */ + struct mutex client_lock; struct i2c_client *c; /* TX additional actions needed */ @@ -341,6 +341,14 @@ static int add_to_buf(struct IR *ir) if (rx == NULL) return -ENXIO; + /* Ensure our rx->c i2c_client remains valid for the duration */ + mutex_lock(&rx->client_lock); + if (rx->c == NULL) { + mutex_unlock(&rx->client_lock); + put_ir_rx(rx, false); + return -ENXIO; + } + tx = get_ir_tx(ir); /* @@ -442,6 +450,7 @@ static int add_to_buf(struct IR *ir) ret = 0; } while (!lirc_buffer_full(rbuf)); + mutex_unlock(&rx->client_lock); if (tx != NULL) put_ir_tx(tx, false); put_ir_rx(rx, false); @@ -1089,6 +1098,14 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (tx == NULL) return -ENXIO; + /* Ensure our tx->c i2c_client remains valid for the duration */ + mutex_lock(&tx->client_lock); + if (tx->c == NULL) { + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return -ENXIO; + } + /* Lock i2c bus for the duration */ mutex_lock(&ir->ir_lock); @@ -1099,6 +1116,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (copy_from_user(&command, buf + i, sizeof(command))) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return -EFAULT; } @@ -1109,6 +1127,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, ret = fw_load(tx); if (ret != 0) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); if (ret != -ENOMEM) ret = -EIO; @@ -1126,6 +1145,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return ret; } @@ -1144,6 +1164,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, zilog_error("unable to send to the IR chip " "after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return ret; } @@ -1158,6 +1179,8 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, /* Release i2c bus */ mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + /* Give back our struct IR_tx reference */ put_ir_tx(tx, false); @@ -1365,12 +1388,20 @@ static int ir_remove(struct i2c_client *client) { if (strncmp("ir_tx_z8", client->name, 8) == 0) { struct IR_tx *tx = i2c_get_clientdata(client); - if (tx != NULL) + if (tx != NULL) { + mutex_lock(&tx->client_lock); + tx->c = NULL; + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); + } } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { struct IR_rx *rx = i2c_get_clientdata(client); - if (rx != NULL) + if (rx != NULL) { + mutex_lock(&rx->client_lock); + rx->c = NULL; + mutex_unlock(&rx->client_lock); put_ir_rx(rx, false); + } } return 0; } @@ -1472,6 +1503,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->tx = tx; ir->l.features |= LIRC_CAN_SEND_PULSE; + mutex_init(&tx->client_lock); tx->c = client; tx->need_boot = 1; tx->post_tx_ready_poll = @@ -1514,6 +1546,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->rx = rx; ir->l.features |= LIRC_CAN_REC_LIRCCODE; + mutex_init(&rx->client_lock); rx->c = client; rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v1.2.3 From 4933fc9d72104d66c54620537eb6f43457a6896f Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 21:14:13 -0300 Subject: [media] lirc_zilog: Fix somewhat confusing information messages in ir_probe() The total sequence of messages emitted by the ir_porbe() calls for a transceiver's two i2c_clients was confusing. Clean it up a bit. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 5946a9541c43..407d4b409a8b 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1595,6 +1595,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ret = -EBADRQC; goto out_put_xx; } + zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n", + adap->name, adap->nr, ir->l.minor); out_ok: if (rx != NULL) @@ -1602,7 +1604,7 @@ out_ok: if (tx != NULL) put_ir_tx(tx, true); put_ir_device(ir, true); - zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", + zilog_info("probe of IR %s on %s (i2c-%d) done\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_unlock(&ir_devices_lock); return 0; -- cgit v1.2.3 From aa3f4e446e784372498f009ea37177857f23331e Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 21:32:06 -0300 Subject: [media] lirc_zilog: Update TODO list based on work completed and revised plans Update the TODO.lirc_zilog based on what has been completed. Also revised the development plan for lirc_zilog to not try and split Tx/Rx for one IR transceiver unit between lirc_zilog and ir-kbd-i2c, since that would be a ref-counting nightmare. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/TODO.lirc_zilog | 51 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 26 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/lirc/TODO.lirc_zilog index 2d0263f07937..a97800a8e127 100644 --- a/drivers/staging/lirc/TODO.lirc_zilog +++ b/drivers/staging/lirc/TODO.lirc_zilog @@ -1,34 +1,33 @@ -1. Both ir-kbd-i2c and lirc_zilog provide support for RX events. -The 'tx_only' lirc_zilog module parameter will allow ir-kbd-i2c -and lirc_zilog to coexist in the kernel, if the user requires such a set-up. -However the IR unit will not work well without coordination between the -two modules. A shared mutex, for transceiver access locking, needs to be -supplied by bridge drivers, in struct IR_i2_init_data, to both ir-kbd-i2c -and lirc_zilog, before they will coexist usefully. This should be fixed -before moving out of staging. - -2. References and locking need careful examination. For cx18 and ivtv PCI -cards, which are not easily "hot unplugged", the imperfect state of reference -counting and locking is acceptable if not correct. For USB connected units -like HD PVR, PVR USB2, HVR-1900, and HVR1950, the likelyhood of an Ooops on -unplug is probably great. Proper reference counting and locking needs to be -implemented before this module is moved out of staging. - -3. The binding between hdpvr and lirc_zilog is currently disabled, -due to an OOPS reported a few years ago when both the hdpvr and cx18 -drivers were loaded in his system. More details can be seen at: - http://www.mail-archive.com/linux-media@vger.kernel.org/msg09163.html -More tests need to be done, in order to fix the reported issue. - -4. In addition to providing a shared mutex for transceiver access -locking, bridge drivers, if able, should provide a chip reset() callback +1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for +the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: + +a. ir-kbd-i2c needs a module parameter added to allow the user to tell + ir-kbd-i2c to ignore Z8 IR units. + +b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c + does. + + +2. lirc_zilog module ref-counting need examination. It has not been +verified that cdev and lirc_dev will take the proper module references on +lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node +is open. + +(The good news is ref-counting of lirc_zilog internal structures appears to be +complete. Testing has shown the cx18 module can be unloaded out from under +irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse +effects. The cx18 module could then be reloaded and irw properly began +receiving button presses again and ir_send worked without error.) + + +3. Bridge drivers, if able, should provide a chip reset() callback to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines -to perform Z8 chip resets via GPIO manipulations. This will allow lirc_zilog +to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog to bring the chip back to normal when it hangs, in the same places the original lirc_pvr150 driver code does. This is not strictly needed, so it is not required to move lirc_zilog out of staging. -5. Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed +Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed and installed on Hauppauge products. When working on either module, developers must consider at least the following bridge drivers which mention an IR Rx unit at address 0x71 (indicative of a Z8): -- cgit v1.2.3 From 55734785cdaff596be9a5238af54fc0f4ace2e63 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 17:57:24 -0300 Subject: [media] lirc: silence some compile warnings Both lirc_imon and lirc_sasem were causing gcc to complain about the possible use of uninitialized variables. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_imon.c | 2 +- drivers/staging/lirc/lirc_sasem.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c index 235cab0eb087..4039eda2a15b 100644 --- a/drivers/staging/lirc/lirc_imon.c +++ b/drivers/staging/lirc/lirc_imon.c @@ -379,7 +379,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, struct imon_context *context; const unsigned char vfd_packet6[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; - int *data_buf; + int *data_buf = NULL; context = file->private_data; if (!context) { diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c index 925eabe14854..63a438d1c849 100644 --- a/drivers/staging/lirc/lirc_sasem.c +++ b/drivers/staging/lirc/lirc_sasem.c @@ -364,7 +364,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, int i; int retval = 0; struct sasem_context *context; - int *data_buf; + int *data_buf = NULL; context = (struct sasem_context *) file->private_data; if (!context) { -- cgit v1.2.3 From cc664ae026f1b0f3254564a7d45cc29e735c7fed Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 19:53:05 -0300 Subject: [media] lirc_zilog: error out if buffer read bytes != chunk size Give it a few tries, then exit. Prevents a possible endless loop situation. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 407d4b409a8b..dd6a57c3c3a3 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -897,7 +897,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) struct IR *ir = filep->private_data; struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; - int ret = 0, written = 0; + int ret = 0, written = 0, retries = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); @@ -950,6 +950,12 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) ret = copy_to_user((void *)outbuf+written, buf, rbuf->chunk_size); written += rbuf->chunk_size; + } else { + retries++; + } + if (retries >= 5) { + zilog_error("Buffer read failed!\n"); + ret = -EIO; } } } -- cgit v1.2.3