Commit 2add87a9 authored by Johannes Stezenbach's avatar Johannes Stezenbach Committed by Linus Torvalds

[PATCH] dvb: b2c2/flexcop driver refactoring part 2: add modular Flexcop driver

b2c2/flexcop driver refactoring to support PCI and USB based cards part 2: add
modular Flexcop driver
Signed-off-by: default avatarPatrick Boettcher <pb@linuxtv.org>
Signed-off-by: default avatarJohannes Stezenbach <js@linuxtv.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1ec35972
This README escorted the skystar2-driver rewriting procedure. It describes the
state of the new flexcop-driver set and some internals are written down here
too.
How to do something in here?
============================
make -f Makefile.t
make -C ../build-2.6
./in.sh # load the drivers
./rm.sh # unload the drivers
Please read this file, if you want to contribute.
This document hopefully describes things about the flexcop and its
device-offsprings. Goal is to write a easy-to-write and easy-to-read set of
drivers based on the skystar2.c and other information.
This directory is temporary. It is used for rewriting the skystar2.c and to
create shared code, which then can be used by the usb box as well.
Remark: flexcop-pci.c was a copy of skystar2.c, but every line has been
touched and rewritten.
General coding processing
=========================
We should proceed as follows (as long as no one complains):
0) Think before start writing code!
1) rewriting the skystar2.c with the help of the flexcop register descriptions
and splitting up the files to a pci-bus-part and a flexcop-part.
The new driver will be called b2c2-flexcop-pci.ko/b2c2-flexcop-usb.ko for the
device-specific part and b2c2-flexcop.ko for the common flexcop-functions.
2) Search for errors in the leftover of flexcop-pci.c (compare with pluto2.c
and other pci drivers)
3) make some beautification (see 'Improvements when rewriting (refactoring) is
done')
4) Testing the new driver and maybe substitute the skystar2.c with it, to reach
a wider tester audience.
5) creating an usb-bus-part using the already written flexcop code for the pci
card.
Idea: create a kernel-object for the flexcop and export all important
functions. This option saves kernel-memory, but maybe a lot of functions have
to be exported to kernel namespace.
Current situation
=================
0) Done :)
1) Done (some minor issues left)
2) Done
3) Not ready yet, more information is necessary
4) next to be done (see the table below)
5) USB driver is working (yes, there are some minor issues)
What seems to be ready?
-----------------------
1) Rewriting
1a) i2c is cut off from the flexcop-pci.c and seems to work
1b) moved tuner and demod stuff from flexcop-pci.c to flexcop-tuner-fe.c
1c) moved lnb and diseqc stuff from flexcop-pci.c to flexcop-tuner-fe.c
1e) eeprom (reading MAC address)
1d) sram (no dynamic sll size detection (commented out) (using default as JJ told me))
1f) misc. register accesses for reading parameters (e.g. resetting, revision)
1g) pid/mac filter (flexcop-hw-filter.c)
1i) dvb-stuff initialization in flexcop.c (done)
1h) dma stuff (now just using the size-irq, instead of all-together, to be done)
1j) remove flexcop initialization from flexcop-pci.c completely (done)
1l) use a well working dma IRQ method (done, see 'Known bugs and problems and TODO')
1k) cleanup flexcop-files (remove unused EXPORT_SYMBOLs, make static from
non-static where possible, moved code to proper places)
2) Search for errors in the leftover of flexcop-pci.c (partially done)
5a) add MAC address reading
What to do in the near future?
--------------------------------------
(no special order here)
5) USB driver
5b) optimize isoc-transfer (submitting/killing isoc URBs when transfer is starting)
5c) feeding of ISOC data to the software demux (format of the isochronous data
and speed optimization, no real error)
Testing changes
---------------
O = item is working
P = item is partially working
X = item is not working
N = item does not apply here
<empty field> = item need to be examined
| PCI | USB
item | mt352 | nxt2002 | stv0299 | mt312 | mt352 | nxt2002 | stv0299 | mt312
-------+-------+---------+---------+-------+-------+---------+---------+-------
1a) | O | | | | N | N | N | N
1b) | O | | | | | | O |
1c) | N | N | | | N | N | O |
1d) | O | O
1e) | O | O
1f) | P
1g) | O
1h) | P |
1i) | O | N
1j) | O | N
1l) | O | N
2) | O | N
5a) | N | O
5b)* | N |
5c)* | N |
* - not done yet
Known bugs and problems and TODO
--------------------------------
1g/h/l) when pid filtering is enabled on the pci card
DMA usage currently:
The DMA is splitted in 2 equal-sized subbuffers. The Flexcop writes to first
address and triggers an IRQ when it's full and starts writing to the second
address. When the second address is full, the IRQ is triggered again, and
the flexcop writes to first address again, and so on.
The buffersize of each address is currently 640*188 bytes.
Problem is, when using hw-pid-filtering and doing some low-bandwidth
operation (like scanning) the buffers won't be filled enough to trigger
the IRQ. That's why:
When PID filtering is activated, the timer IRQ is used. Every 1.97 ms the IRQ
is triggered. Is the current write address of DMA1 different to the one
during the last IRQ, then the data is passed to the demuxer.
There is an additional DMA-IRQ-method: packet count IRQ. This isn't
implemented correctly yet.
The solution is to disable HW PID filtering, but I don't know how the DVB
API software demux behaves on slow systems with 45MBit/s TS.
Solved bugs :)
--------------
1g) pid-filtering (somehow pid index 4 and 5 (EMM_PID and ECM_PID) aren't
working)
SOLUTION: also index 0 was affected, because net_translation is done for
these indexes by default
5b) isochronous transfer does only work in the first attempt (for the Sky2PC USB,
Air2PC is working)
SOLUTION: the flexcop was going asleep and never really woke up again (don't
know if this need fixes, see flexcop-fe-tuner.c:flexcop_sleep)
Improvements when rewriting (refactoring) is done
=================================================
- split sleeping of the flexcop (misc_204.ACPI3_sig = 1;) from lnb_control
(enable sleeping for other demods than dvb-s)
- add support for CableStar (stv0297 Microtune 203x/ALPS)
Debugging
---------
- add verbose debugging to skystar2.c (dump the reg_dw_data) and compare it
with this flexcop, this is important, because i2c is now using the
flexcop_ibi_value union from flexcop-reg.h (do you have a better idea for
that, please tell us so).
Everything which is identical in the following table, can be put into a common
flexcop-module.
PCI USB
-------------------------------------------------------------------------------
Different:
Register access: accessing IO memory USB control message
I2C bus: I2C bus of the FC USB control message
Data transfer: DMA isochronous transfer
EEPROM transfer: through i2c bus not clear yet
Identical:
Streaming: accessing registers
PID Filtering: accessing registers
Sram destinations: accessing registers
Tuner/Demod: I2C bus
DVB-stuff: can be written for common use
Restrictions:
============
We need to create a bus-specific-struct and a flexcop-struct.
bus-specific-struct:
struct flexcop_pci
...
struct flexcop_usb
...
struct flexcop_device {
void *bus_specific; /* container for bus-specific struct */
...
}
PCI i2c can read/write max 4 bytes at a time, USB can more
Functions
=========
Syntax
------
- Flexcop functions will be called "flexcop(_[a-z0-9]+)+" and exported as such
if needed.
- Flexcop-device functions will be called "flexcop_device(_[a-z0-9]+)+" and
exported as such if needed.
- Both will be compiled to b2c2-flexcop.ko and their source can be found in the
flexcop*.[hc]
Callbacks and exports
---------------------
Bus-specific functions will be given as callbacks (function pointers) to the
flexcop-module. (within the flexcop_device-struct)
Initialization process
======================
b2c2-flexcop.ko is loaded
b2c2-flexcop-<bus>.ko is loaded
suppose a device is found:
malloc flexcop and the bus-specific variables (via flexcop_device_malloc)
fill the bus-specific variable
fill the flexcop variable (especially the bus-specific callbacks)
bus-specific initialization
- ...
do the common initialization (via flexcop_device_initialize)
- reset the card
- determine flexcop type (II, IIB, III)
- hw_filters (bus dependent)
- 0x204
- set sram size
- create the dvb-stuff
- create i2c stuff
- frontend-initialization
done
bus specific:
- media_destination (this and the following 3 are bus specific)
- cai_dest
- cao_dest
- net_destination
Bugs fixed while rewriting the driver
=====================================
- EEPROM access (to read the MAC address) was fixed to death some time last
year. (fixed here and in skystar2.c) (Bjarne, this was the piece of code
(fix-chipaddr) we were wondering about)
Acknowledgements (just for the rewriting part)
================
Bjarne Steinsbo thought a lot in the first place of the pci part for this code
sharing idea.
Andreas Oberritter for providing a recent PCI initialization template (pluto2.c).
comments, critics and ideas to linux-dvb@linuxtv.org or patrick.boettcher@desy.de
......@@ -33,7 +33,7 @@ source "drivers/media/dvb/dibusb/Kconfig"
source "drivers/media/dvb/cinergyT2/Kconfig"
comment "Supported FlexCopII (B2C2) Adapters"
depends on DVB_CORE && PCI
depends on DVB_CORE && (PCI || USB)
source "drivers/media/dvb/b2c2/Kconfig"
comment "Supported BT878 Adapters"
......
config DVB_B2C2_FLEXCOP
tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters"
depends on DVB_CORE
select DVB_STV0299
select DVB_MT352
select DVB_MT312
select DVB_NXT2002
select DVB_STV0297
help
Support for the digital TV receiver chip made by B2C2 Inc. included in
Technisats PCI cards and USB boxes.
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_PCI
tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI"
depends on DVB_B2C2_FLEXCOP && PCI
help
Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2.
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_USB
tristate "Technisat/B2C2 Air/Sky/Cable2PC USB"
depends on DVB_B2C2_FLEXCOP && USB
help
Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2,
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_DEBUG
bool "Enable debug for the B2C2 FlexCop drivers"
depends on DVB_B2C2_FLEXCOP
help
Say Y if you want to enable the module option to control debug messages
of all B2C2 FlexCop drivers.
config DVB_B2C2_SKYSTAR
tristate "B2C2/Technisat Air/Sky/CableStar 2 PCI"
depends on DVB_CORE && PCI
......
b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \
flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o \
flexcop-dma.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
b2c2-flexcop-pci-objs = flexcop-pci.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
b2c2-flexcop-usb-objs = flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_SKYSTAR) += skystar2.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-common.h - common header file for device-specific source files also.
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_COMMON_H__
#define __FLEXCOP_COMMON_H__
#include <linux/config.h>
#include <linux/pci.h>
#include "flexcop-reg.h"
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_filter.h"
#include "dvb_net.h"
#include "dvb_frontend.h"
#define FC_MAX_FEED 256
#ifndef FC_LOG_PREFIX
#warning please define a log prefix for your file, using a default one
#define FC_LOG_PREFIX "b2c2-undef"
#endif
/* Steal from usb.h */
#undef err
#define err(format, arg...) printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg)
#undef info
#define info(format, arg...) printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg)
#undef warn
#define warn(format, arg...) printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
struct flexcop_dma {
struct pci_dev *pdev;
u8 *cpu_addr0;
dma_addr_t dma_addr0;
u8 *cpu_addr1;
dma_addr_t dma_addr1;
u32 size; /* size of each address in bytes */
};
/* Control structure for data definitions that are common to
* the B2C2-based PCI and USB devices.
*/
struct flexcop_device {
/* general */
struct device *dev; /* for firmware_class */
#define FC_STATE_DVB_INIT 0x01
#define FC_STATE_I2C_INIT 0x02
#define FC_STATE_FE_INIT 0x04
int init_state;
/* device information */
u8 mac_address[6];
int has_32_hw_pid_filter;
flexcop_revision_t rev;
flexcop_device_type_t dev_type;
flexcop_bus_t bus_type;
/* dvb stuff */
struct dvb_adapter dvb_adapter;
struct dvb_frontend *fe;
struct dvb_net dvbnet;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend hw_frontend;
struct dmx_frontend mem_frontend;
int (*fe_sleep) (struct dvb_frontend *);
struct i2c_adapter i2c_adap;
struct semaphore i2c_sem;
/* options and status */
int feedcount;
int pid_filtering;
/* bus specific callbacks */
flexcop_ibi_value (*read_ibi_reg) (struct flexcop_device *, flexcop_ibi_register);
int (*write_ibi_reg) (struct flexcop_device *, flexcop_ibi_register, flexcop_ibi_value);
int (*i2c_request) (struct flexcop_device*, flexcop_access_op_t, flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
int (*stream_control) (struct flexcop_device*, int);
int (*get_mac_addr) (struct flexcop_device *fc, int extended);
void *bus_specific;
};
/* exported prototypes */
/* from flexcop.c */
void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len);
void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no);
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len);
void flexcop_device_kfree(struct flexcop_device*);
int flexcop_device_initialize(struct flexcop_device*);
void flexcop_device_exit(struct flexcop_device *fc);
/* from flexcop-dma.c */
int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size);
void flexcop_dma_free(struct flexcop_dma *dma);
int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
int flexcop_dma_control_size_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
int flexcop_dma_control_packet_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx,flexcop_dma_addr_index_t index);
int flexcop_dma_config_timer(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 cycles);
int flexcop_dma_config_packet_count(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 packets);
/* from flexcop-eeprom.c */
/* the PCI part uses this call to get the MAC address, the USB part has its own */
int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended);
/* from flexcop-i2c.c */
/* the PCI part uses this a i2c_request callback, whereas the usb part has its own
* one. We have it in flexcop-i2c.c, because it is going via the actual
* I2C-channel of the flexcop.
*/
int flexcop_i2c_request(struct flexcop_device*, flexcop_access_op_t,
flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
/* from flexcop-sram.c */
int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target);
void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s);
void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill);
/* global prototypes for the flexcop-chip */
/* from flexcop-fe-tuner.c */
int flexcop_frontend_init(struct flexcop_device *card);
void flexcop_frontend_exit(struct flexcop_device *fc);
/* from flexcop-i2c.c */
int flexcop_i2c_init(struct flexcop_device *fc);
void flexcop_i2c_exit(struct flexcop_device *fc);
/* from flexcop-sram.c */
int flexcop_sram_init(struct flexcop_device *fc);
/* from flexcop-misc.c */
void flexcop_determine_revision(struct flexcop_device *fc);
void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const char *suffix);
/* from flexcop-hw-filter.c */
int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff);
void flexcop_hw_filter_init(struct flexcop_device *fc);
void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff);
void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]);
void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff);
#endif
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-dma.c - methods for configuring and controlling the DMA of the FlexCop.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size)
{
u8 *tcpu;
dma_addr_t tdma;
if (size % 2) {
err("dma buffersize has to be even.");
return -EINVAL;
}
if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) {
dma->pdev = pdev;
dma->cpu_addr0 = tcpu;
dma->dma_addr0 = tdma;
dma->cpu_addr1 = tcpu + size/2;
dma->dma_addr1 = tdma + size/2;
dma->size = size/2;
return 0;
}
return -ENOMEM;
}
EXPORT_SYMBOL(flexcop_dma_allocate);
void flexcop_dma_free(struct flexcop_dma *dma)
{
pci_free_consistent(dma->pdev, dma->size*2,dma->cpu_addr0, dma->dma_addr0);
memset(dma,0,sizeof(struct flexcop_dma));
}
EXPORT_SYMBOL(flexcop_dma_free);
int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
if (no & FC_DMA_1)
v.ctrl_208.DMA1_Timer_Enable_sig = onoff;
if (no & FC_DMA_2)
v.ctrl_208.DMA2_Timer_Enable_sig = onoff;
fc->write_ibi_reg(fc,ctrl_208,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_control_timer_irq);
int flexcop_dma_control_size_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
if (no & FC_DMA_1)
v.ctrl_208.DMA1_IRQ_Enable_sig = onoff;
if (no & FC_DMA_2)
v.ctrl_208.DMA2_IRQ_Enable_sig = onoff;
fc->write_ibi_reg(fc,ctrl_208,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_control_size_irq);
int flexcop_dma_control_packet_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
if (no & FC_DMA_1)
v.ctrl_208.DMA1_Size_IRQ_Enable_sig = onoff;
if (no & FC_DMA_2)
v.ctrl_208.DMA2_Size_IRQ_Enable_sig = onoff;
fc->write_ibi_reg(fc,ctrl_208,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_control_packet_irq);
int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx,flexcop_dma_addr_index_t index)
{
flexcop_ibi_value v0x0,v0x4,v0xc;
v0x0.raw = v0x4.raw = v0xc.raw = 0;
v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
v0x4.dma_0x4_write.dma_addr_size = dma->size / 4;
if (index & FC_DMA_SUBADDR_0)
v0x0.dma_0x0.dma_0start = 1;
if (index & FC_DMA_SUBADDR_1)
v0xc.dma_0xc.dma_1start = 1;
if (dma_idx & FC_DMA_1) {
fc->write_ibi_reg(fc,dma1_000,v0x0);
fc->write_ibi_reg(fc,dma1_004,v0x4);
fc->write_ibi_reg(fc,dma1_00c,v0xc);
} else { /* (dma_idx & FC_DMA_2) */
fc->write_ibi_reg(fc,dma2_010,v0x0);
fc->write_ibi_reg(fc,dma2_014,v0x4);
fc->write_ibi_reg(fc,dma2_01c,v0xc);
}
return 0;
}
EXPORT_SYMBOL(flexcop_dma_config);
static int flexcop_dma_remap(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, int onoff)
{
flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c;
flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
v.dma_0xc.remap_enable = onoff;
fc->write_ibi_reg(fc,r,v);
return 0;
}
/* 1 cycles = 1.97 msec */
int flexcop_dma_config_timer(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 cycles)
{
flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
flexcop_dma_remap(fc,dma_idx,0);
v.dma_0x4_write.dmatimer = cycles >> 1;
fc->write_ibi_reg(fc,r,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_config_timer);
int flexcop_dma_config_packet_count(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 packets)
{
flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
flexcop_dma_remap(fc,dma_idx,1);
v.dma_0x4_remap.DMA_maxpackets = packets;
fc->write_ibi_reg(fc,r,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_config_packet_count);
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-eeprom.c - eeprom access methods (currently only MAC address reading is used)
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#if 0
/*EEPROM (Skystar2 has one "24LC08B" chip on board) */
static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
{
return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
}
static int eeprom_lrc_write(struct adapter *adapter, u32 addr, u32 len, u8 *wbuf, u8 *rbuf, int retries)
{
int i;
for (i = 0; i < retries; i++) {
if (eeprom_write(adapter, addr, wbuf, len) == len) {
if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
return 1;
}
}
return 0;
}
/* These functions could be used to unlock SkyStar2 cards. */
static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
{
u8 rbuf[20];
u8 wbuf[20];
if (len != 16)
return 0;
memcpy(wbuf, key, len);
wbuf[16] = 0;
wbuf[17] = 0;
wbuf[18] = 0;
wbuf[19] = calc_lrc(wbuf, 19);
return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
}
static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
{
u8 buf[20];
if (len != 16)
return 0;
if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0)
return 0;
memcpy(key, buf, len);
return 1;
}
static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
{
u8 tmp[8];
if (type != 0) {
tmp[0] = mac[0];
tmp[1] = mac[1];
tmp[2] = mac[2];
tmp[3] = mac[5];
tmp[4] = mac[6];
tmp[5] = mac[7];
} else {
tmp[0] = mac[0];
tmp[1] = mac[1];
tmp[2] = mac[2];
tmp[3] = mac[3];
tmp[4] = mac[4];
tmp[5] = mac[5];
}
tmp[6] = 0;
tmp[7] = calc_lrc(tmp, 7);
if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
return 1;
return 0;
}
static int flexcop_eeprom_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len)
{
return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len);
}
#endif
static u8 calc_lrc(u8 *buf, int len)
{
int i;
u8 sum = 0;
for (i = 0; i < len; i++)
sum = sum ^ buf[i];
return sum;
}
static int flexcop_eeprom_request(struct flexcop_device *fc, flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries)
{
int i,ret = 0;
u8 chipaddr = 0x50 | ((addr >> 8) & 3);
for (i = 0; i < retries; i++)
if ((ret = fc->i2c_request(fc,op,FC_I2C_PORT_EEPROM,chipaddr,addr & 0xff,buf,len)) == 0)
break;
return ret;
}
static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len, int retries)
{
int ret = flexcop_eeprom_request(fc,FC_READ,addr,buf,len,retries);
if (ret == 0)
if (calc_lrc(buf, len - 1) != buf[len - 1])
ret = -EINVAL;
return ret;
}
/* TODO how is it handled in USB */
/* JJ's comment about extended == 1: it is not presently used anywhere but was
* added to the low-level functions for possible support of EUI64
*/
int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
{
u8 buf[8];
int ret = 0;
memset(fc->mac_address,0,6);
if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) {
if (extended != 0) {
err("TODO: extended (EUI64) MAC addresses aren't completely supported yet");
ret = -EINVAL;
/* memcpy(fc->mac_address,buf,3);
mac[3] = 0xfe;
mac[4] = 0xff;
memcpy(&fc->mac_address[3],&buf[5],3); */
} else
memcpy(fc->mac_address,buf,6);
}
return ret;
}
EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr);
This diff is collapsed.
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-hw-filter.c - pid and mac address filtering and corresponding control functions.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Rcv_Data_sig,onoff);
}
void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,SMC_Enable_sig,onoff);
}
void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Null_filter_sig,onoff);
}
void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
{
flexcop_ibi_value v418,v41c;
v41c = fc->read_ibi_reg(fc,mac_address_41c);
v418.mac_address_418.MAC1 = mac[0];
v418.mac_address_418.MAC2 = mac[1];
v418.mac_address_418.MAC3 = mac[2];
v418.mac_address_418.MAC6 = mac[3];
v41c.mac_address_41c.MAC7 = mac[4];
v41c.mac_address_41c.MAC8 = mac[5];
fc->write_ibi_reg(fc,mac_address_418,v418);
fc->write_ibi_reg(fc,mac_address_41c,v41c);
}
void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,MAC_filter_Mode_sig,onoff);
}
static void flexcop_pid_group_filter(struct flexcop_device *fc, u16 pid, u16 mask)
{
/* index_reg_310.extra_index_reg need to 0 or 7 to work */
flexcop_ibi_value v30c;
v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid;
v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask;
fc->write_ibi_reg(fc,pid_filter_30c,v30c);
}
static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Mask_filter_sig,onoff);
}
/* this fancy define reduces the code size of the quite similar PID controlling of
* the first 6 PIDs
*/
#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \
flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \
v208 = fc->read_ibi_reg(fc, ctrl_208); \
\
vpid.vregname.field = onoff ? pid : 0x1fff; \
vpid.vregname.trans_field = transval; \
v208.ctrl_208.enablefield = onoff; \
\
fc->write_ibi_reg(fc,vregname,vpid); \
fc->write_ibi_reg(fc,ctrl_208,v208);
static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_300,Stream1_PID,Stream1_filter_sig,Stream1_trans,0);
}
static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_300,Stream2_PID,Stream2_filter_sig,Stream2_trans,0);
}
static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_304,PCR_PID,PCR_filter_sig,PCR_trans,0);
}
static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_304,PMT_PID,PMT_filter_sig,PMT_trans,0);
}
static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_308,EMM_PID,EMM_filter_sig,EMM_trans,0);
}
static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_308,ECM_PID,ECM_filter_sig,ECM_trans,0);
}
static void flexcop_pid_control(struct flexcop_device *fc, int index, u16 pid,int onoff)
{
deb_ts("setting pid: %5d %04x at index %d '%s'\n",pid,pid,index,onoff ? "on" : "off");
/* We could use bit magic here to reduce source code size.
* I decided against it, but to use the real register names */
switch (index) {
case 0: flexcop_pid_Stream1_PID_ctrl(fc,pid,onoff); break;
case 1: flexcop_pid_Stream2_PID_ctrl(fc,pid,onoff); break;
case 2: flexcop_pid_PCR_PID_ctrl(fc,pid,onoff); break;
case 3: flexcop_pid_PMT_PID_ctrl(fc,pid,onoff); break;
case 4: flexcop_pid_EMM_PID_ctrl(fc,pid,onoff); break;
case 5: flexcop_pid_ECM_PID_ctrl(fc,pid,onoff); break;
default:
if (fc->has_32_hw_pid_filter && index < 38) {
flexcop_ibi_value vpid,vid;
/* set the index */
vid = fc->read_ibi_reg(fc,index_reg_310);
vid.index_reg_310.index_reg = index - 6;
fc->write_ibi_reg(fc,index_reg_310, vid);
vpid = fc->read_ibi_reg(fc,pid_n_reg_314);
vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
vpid.pid_n_reg_314.PID_enable_bit = onoff;
fc->write_ibi_reg(fc,pid_n_reg_314, vpid);
}
break;
}
}
int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;
fc->feedcount += (onoff ? 1 : -1);
/* when doing hw pid filtering, set the pid */
if (fc->pid_filtering)
flexcop_pid_control(fc,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
/* if it was the first feed request */
if (fc->feedcount == onoff && onoff) {
if (!fc->pid_filtering) {
deb_ts("enabling full TS transfer\n");
flexcop_pid_group_filter(fc, 0,0);
flexcop_pid_group_filter_ctrl(fc,1);
}
if (fc->stream_control)
fc->stream_control(fc,1);
flexcop_rcv_data_ctrl(fc,1);
/* if there is no more feed left to feed */
} else if (fc->feedcount == onoff && !onoff) {
if (!fc->pid_filtering) {
deb_ts("disabling full TS transfer\n");
flexcop_pid_group_filter(fc, 0x1fe0,0);
flexcop_pid_group_filter_ctrl(fc,0);
}
flexcop_rcv_data_ctrl(fc,0);
if (fc->stream_control)
fc->stream_control(fc,0);
}
/* if pid_filtering is on and more pids than the hw-filter can provide are
* requested enable the whole bandwidth.
*/
if (fc->pid_filtering && fc->feedcount > max_pid_filter) {
flexcop_pid_group_filter(fc, 0,0);
flexcop_pid_group_filter_ctrl(fc,1);
} else if (fc->pid_filtering && fc->feedcount <= max_pid_filter) {
flexcop_pid_group_filter(fc, 0x1fe0,0);
flexcop_pid_group_filter_ctrl(fc,0);
}
return 0;
}
void flexcop_hw_filter_init(struct flexcop_device *fc)
{
int i;
flexcop_ibi_value v;
for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
flexcop_pid_control(fc,i,0x1fff,0);
flexcop_pid_group_filter(fc, 0x1fe0,0);
v = fc->read_ibi_reg(fc,pid_filter_308);
v.pid_filter_308.EMM_filter_4 = 1;
v.pid_filter_308.EMM_filter_6 = 0;
fc->write_ibi_reg(fc,pid_filter_308,v);
}
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#define FC_MAX_I2C_RETRIES 100000
static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r100, int max_ack_errors)
{
int i,ack_errors = 0;
flexcop_ibi_value r;
r100->tw_sm_c_100.working_start = 1;
deb_i2c("r100 before: %08x\n",r100->raw);
fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */
for (i = 0; i < FC_MAX_I2C_RETRIES; i++) {
r = fc->read_ibi_reg(fc, tw_sm_c_100);
if (!r.tw_sm_c_100.no_base_addr_ack_error) {
if (r.tw_sm_c_100.st_done) { /* && !r.tw_sm_c_100.working_start */
*r100 = r;
deb_i2c("i2c success\n");
return 0;
}
} else {
deb_i2c("suffering from an i2c ack_error\n");
if (++ack_errors >= max_ack_errors)
break;
fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
fc->write_ibi_reg(fc, tw_sm_c_100, *r100);
}
}
deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",i);
return -EREMOTEIO;
}
static int flexcop_i2c_read4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
{
flexcop_ibi_value r104;
int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */
ret;
if ((ret = flexcop_i2c_operation(fc,&r100,30)) != 0)
return ret;
r104 = fc->read_ibi_reg(fc,tw_sm_c_104);
deb_i2c("read: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
/* there is at least one byte, otherwise we wouldn't be here */
buf[0] = r100.tw_sm_c_100.data1_reg;
if (len > 0) buf[1] = r104.tw_sm_c_104.data2_reg;
if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
return 0;
}
static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
{
flexcop_ibi_value r104;
int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
r104.raw = 0;
/* there is at least one byte, otherwise we wouldn't be here */
r100.tw_sm_c_100.data1_reg = buf[0];
r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
deb_i2c("write: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
/* write the additional i2c data before doing the actual i2c operation */
fc->write_ibi_reg(fc,tw_sm_c_104,r104);
return flexcop_i2c_operation(fc,&r100,30);
}
/* master xfer callback for demodulator */
static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
{
struct flexcop_device *fc = i2c_get_adapdata(i2c_adap);
int i, ret = 0;
if (down_interruptible(&fc->i2c_sem))
return -ERESTARTSYS;
/* reading */
if (num == 2 &&
msgs[0].flags == 0 &&
msgs[1].flags == I2C_M_RD &&
msgs[0].buf != NULL &&
msgs[1].buf != NULL) {
ret = fc->i2c_request(fc, FC_READ, FC_I2C_PORT_DEMOD, msgs[0].addr, msgs[0].buf[0], msgs[1].buf, msgs[1].len);
} else for (i = 0; i < num; i++) { /* writing command */
if (msgs[i].flags != 0 || msgs[i].buf == NULL || msgs[i].len < 2) {
ret = -EINVAL;
break;
}
ret = fc->i2c_request(fc, FC_WRITE, FC_I2C_PORT_DEMOD, msgs[i].addr, msgs[i].buf[0], &msgs[i].buf[1], msgs[i].len - 1);
}
if (ret < 0)
err("i2c master_xfer failed");
else
ret = num;
up(&fc->i2c_sem);
return ret;
}
int flexcop_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
{
int ret;
u16 bytes_to_transfer;
flexcop_ibi_value r100;
deb_i2c("op = %d\n",op);
r100.raw = 0;
r100.tw_sm_c_100.chipaddr = chipaddr;
r100.tw_sm_c_100.twoWS_rw = op;
r100.tw_sm_c_100.twoWS_port_reg = port;
while (len != 0) {
bytes_to_transfer = len > 4 ? 4 : len;
r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1;
r100.tw_sm_c_100.baseaddr = addr;
if (op == FC_READ)
ret = flexcop_i2c_read4(fc, r100, buf);
else
ret = flexcop_i2c_write4(fc,r100, buf);
if (ret < 0)
return ret;
buf += bytes_to_transfer;
addr += bytes_to_transfer;
len -= bytes_to_transfer;
};
return 0;
}
/* exported for PCI i2c */
EXPORT_SYMBOL(flexcop_i2c_request);
static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm flexcop_algo = {
.name = "FlexCop I2C algorithm",
.id = I2C_ALGO_BIT,
.master_xfer = flexcop_master_xfer,
.functionality = flexcop_i2c_func,
};
int flexcop_i2c_init(struct flexcop_device *fc)
{
int ret;
sema_init(&fc->i2c_sem,1);
memset(&fc->i2c_adap, 0, sizeof(struct i2c_adapter));
strncpy(fc->i2c_adap.name, "B2C2 FlexCop device",I2C_NAME_SIZE);
i2c_set_adapdata(&fc->i2c_adap,fc);
fc->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
fc->i2c_adap.algo = &flexcop_algo;
fc->i2c_adap.algo_data = NULL;
fc->i2c_adap.id = I2C_ALGO_BIT;
if ((ret = i2c_add_adapter(&fc->i2c_adap)) < 0)
return ret;
fc->init_state |= FC_STATE_I2C_INIT;
return 0;
}
void flexcop_i2c_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_I2C_INIT)
i2c_del_adapter(&fc->i2c_adap);
fc->init_state &= ~FC_STATE_I2C_INIT;
}
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-misc.c - miscellaneous functions.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
void flexcop_determine_revision(struct flexcop_device *fc)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204);
switch (v.misc_204.Rev_N_sig_revision_hi) {
case 0x2:
deb_info("found a FlexCopII.\n");
fc->rev = FLEXCOP_II;
break;
case 0x3:
deb_info("found a FlexCopIIb.\n");
fc->rev = FLEXCOP_IIB;
break;
case 0x0:
deb_info("found a FlexCopIII.\n");
fc->rev = FLEXCOP_III;
break;
default:
err("unkown FlexCop Revision: %x. Please report the linux-dvb@linuxtv.org.",v.misc_204.Rev_N_sig_revision_hi);
break;
}
if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps))
deb_info("this FlexCop has the additional 32 hardware pid filter.\n");
else
deb_info("this FlexCop has only the 6 basic main hardware pid filter.\n");
/* bus parts have to decide if hw pid filtering is used or not. */
}
const char *flexcop_revision_names[] = {
"Unkown chip",
"FlexCopII",
"FlexCopIIb",
"FlexCopIII",
};
const char *flexcop_device_names[] = {
"Unkown device",
"AirStar 2 DVB-T",
"AirStar 2 ATSC",
"SkyStar 2 DVB-S",
"SkyStar 2 DVB-S (old version)",
"CableStar 2 DVB-C",
};
const char *flexcop_bus_names[] = {
"USB",
"PCI",
};
void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const
char *suffix)
{
info("%s '%s' at the '%s' bus controlled by a '%s' %s",prefix,
flexcop_device_names[fc->dev_type],flexcop_bus_names[fc->bus_type],
flexcop_revision_names[fc->rev],suffix);
}
This diff is collapsed.
This diff is collapsed.
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-sram.c - functions for controlling the SRAM.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
static void flexcop_sram_set_chip (struct flexcop_device *fc, flexcop_sram_type_t type)
{
flexcop_set_ibi_value(wan_ctrl_reg_71c,sram_chip,type);
}
int flexcop_sram_init(struct flexcop_device *fc)
{
switch (fc->rev) {
case FLEXCOP_II:
case FLEXCOP_IIB:
flexcop_sram_set_chip(fc,FC_SRAM_1_32KB);
break;
case FLEXCOP_III:
flexcop_sram_set_chip(fc,FC_SRAM_1_48KB);
break;
default:
return -EINVAL;
}
return 0;
}
int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target)
{
flexcop_ibi_value v;
v = fc->read_ibi_reg(fc,sram_dest_reg_714);
if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) {
err("SRAM destination target to available on FlexCopII(b)\n");
return -EINVAL;
}
deb_sram("sram dest: %x target: %x\n",dest, target);
if (dest & FC_SRAM_DEST_NET)
v.sram_dest_reg_714.NET_Dest = target;
if (dest & FC_SRAM_DEST_CAI)
v.sram_dest_reg_714.CAI_Dest = target;
if (dest & FC_SRAM_DEST_CAO)
v.sram_dest_reg_714.CAO_Dest = target;
if (dest & FC_SRAM_DEST_MEDIA)
v.sram_dest_reg_714.MEDIA_Dest = target;
fc->write_ibi_reg(fc,sram_dest_reg_714,v);
udelay(1000); /* TODO delay really necessary */
return 0;
}
EXPORT_SYMBOL(flexcop_sram_set_dest);
void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s)
{
flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s);
}
EXPORT_SYMBOL(flexcop_wan_set_speed);
void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714);
v.sram_dest_reg_714.ctrl_usb_wan = usb_wan;
v.sram_dest_reg_714.ctrl_sramdma = sramdma;
v.sram_dest_reg_714.ctrl_maximumfill = maximumfill;
fc->write_ibi_reg(fc,sram_dest_reg_714,v);
}
EXPORT_SYMBOL(flexcop_sram_ctrl);
#if 0
static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
{
int i, retries;
u32 command;
for (i = 0; i < len; i++) {
command = bank | addr | 0x04000000 | (*buf << 0x10);
retries = 2;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
write_reg_dw(adapter, 0x700, command);
buf++;
addr++;
}
}
static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
{
int i, retries;
u32 command, value;
for (i = 0; i < len; i++) {
command = bank | addr | 0x04008000;
retries = 10000;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
write_reg_dw(adapter, 0x700, command);
retries = 10000;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
value = read_reg_dw(adapter, 0x700) >> 0x10;
*buf = (value & 0xff);
addr++;
buf++;
}
}
static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
{
u32 bank;
bank = 0;
if (adapter->dw_sram_type == 0x20000) {
bank = (addr & 0x18000) << 0x0d;
}
if (adapter->dw_sram_type == 0x00000) {
if ((addr >> 0x0f) == 0)
bank = 0x20000000;
else
bank = 0x10000000;
}
flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
}
static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
{
u32 bank;
bank = 0;
if (adapter->dw_sram_type == 0x20000) {
bank = (addr & 0x18000) << 0x0d;
}
if (adapter->dw_sram_type == 0x00000) {
if ((addr >> 0x0f) == 0)
bank = 0x20000000;
else
bank = 0x10000000;
}
flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
}
static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
{
u32 length;
while (len != 0) {
length = len;
// check if the address range belongs to the same
// 32K memory chip. If not, the data is read from
// one chip at a time.
if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
}
sram_read_chunk(adapter, addr, buf, length);
addr = addr + length;
buf = buf + length;
len = len - length;
}
}
static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
{
u32 length;
while (len != 0) {
length = len;
// check if the address range belongs to the same
// 32K memory chip. If not, the data is written to
// one chip at a time.
if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
}
sram_write_chunk(adapter, addr, buf, length);
addr = addr + length;
buf = buf + length;
len = len - length;
}
}
static void sram_set_size(struct adapter *adapter, u32 mask)
{
write_reg_dw(adapter, 0x71c, (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
}
static void sram_init(struct adapter *adapter)
{
u32 tmp;
tmp = read_reg_dw(adapter, 0x71c);
write_reg_dw(adapter, 0x71c, 1);
if (read_reg_dw(adapter, 0x71c) != 0) {
write_reg_dw(adapter, 0x71c, tmp);
adapter->dw_sram_type = tmp & 0x30000;
ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
} else {
adapter->dw_sram_type = 0x10000;
ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
}
/* return value is never used? */
/* return adapter->dw_sram_type; */
}
static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
{
u8 tmp1, tmp2;
dprintk("%s: mask = %x, addr = %x\n", __FUNCTION__, mask, addr);
sram_set_size(adapter, mask);
sram_init(adapter);
tmp2 = 0xa5;
tmp1 = 0x4f;
sram_write(adapter, addr, &tmp2, 1);
sram_write(adapter, addr + 4, &tmp1, 1);
tmp2 = 0;
mdelay(20);
sram_read(adapter, addr, &tmp2, 1);
sram_read(adapter, addr, &tmp2, 1);
dprintk("%s: wrote 0xa5, read 0x%2x\n", __FUNCTION__, tmp2);
if (tmp2 != 0xa5)
return 0;
tmp2 = 0x5a;
tmp1 = 0xf4;
sram_write(adapter, addr, &tmp2, 1);
sram_write(adapter, addr + 4, &tmp1, 1);
tmp2 = 0;
mdelay(20);
sram_read(adapter, addr, &tmp2, 1);
sram_read(adapter, addr, &tmp2, 1);
dprintk("%s: wrote 0x5a, read 0x%2x\n", __FUNCTION__, tmp2);
if (tmp2 != 0x5a)
return 0;
return 1;
}
static u32 sram_length(struct adapter *adapter)
{
if (adapter->dw_sram_type == 0x10000)
return 32768; // 32K
if (adapter->dw_sram_type == 0x00000)
return 65536; // 64K
if (adapter->dw_sram_type == 0x20000)
return 131072; // 128K
return 32768; // 32K
}
/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
- for 128K there are 4x32K chips at bank 0,1,2,3.
- for 64K there are 2x32K chips at bank 1,2.
- for 32K there is one 32K chip at bank 0.
FlexCop works only with one bank at a time. The bank is selected
by bits 28-29 of the 0x700 register.
bank 0 covers addresses 0x00000-0x07fff
bank 1 covers addresses 0x08000-0x0ffff
bank 2 covers addresses 0x10000-0x17fff
bank 3 covers addresses 0x18000-0x1ffff
*/
static int flexcop_sram_detect(struct flexcop_device *fc)
{
flexcop_ibi_value r208,r71c_0,vr71c_1;
r208 = fc->read_ibi_reg(fc, ctrl_208);
fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c);
write_reg_dw(adapter, 0x71c, 1);
tmp3 = read_reg_dw(adapter, 0x71c);
dprintk("%s: tmp3 = %x\n", __FUNCTION__, tmp3);
write_reg_dw(adapter, 0x71c, tmp2);
// check for internal SRAM ???
tmp3--;
if (tmp3 != 0) {
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 32K\n", __FUNCTION__);
return 32;
}
if (sram_test_location(adapter, 0x20000, 0x18000) != 0) {
sram_set_size(adapter, 0x20000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 128K\n", __FUNCTION__);
return 128;
}
if (sram_test_location(adapter, 0x00000, 0x10000) != 0) {
sram_set_size(adapter, 0x00000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 64K\n", __FUNCTION__);
return 64;
}
if (sram_test_location(adapter, 0x10000, 0x00000) != 0) {
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 32K\n", __FUNCTION__);
return 32;
}
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: SRAM detection failed. Set to 32K \n", __FUNCTION__);
return 0;
}
static void sll_detect_sram_size(struct adapter *adapter)
{
sram_detect_for_flex2(adapter);
}
#endif
This diff is collapsed.
#ifndef __FLEXCOP_USB_H_INCLUDED__
#define __FLEXCOP_USB_H_INCLUDED__
#include <linux/usb.h>
/* transfer parameters */
#define B2C2_USB_FRAMES_PER_ISO 4
#define B2C2_USB_NUM_ISO_URB 4
#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev,0)
#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev,0)
#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev,0x81)
struct flexcop_usb {
struct usb_device *udev;
struct usb_interface *uintf;
u8 *iso_buffer;
int buffer_size;
dma_addr_t dma_addr;
struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
struct flexcop_device *fc_dev;
};
#if 0
/* request types TODO What is its use?*/
typedef enum {
/* something is wrong with this part
RTYPE_READ_DW = (1 << 6),
RTYPE_WRITE_DW_1 = (3 << 6),
RTYPE_READ_V8_MEMORY = (6 << 6),
RTYPE_WRITE_V8_MEMORY = (7 << 6),
RTYPE_WRITE_V8_FLASH = (8 << 6),
RTYPE_GENERIC = (9 << 6),
*/
} flexcop_usb_request_type_t;
#endif
/* request */
typedef enum {
B2C2_USB_WRITE_V8_MEM = 0x04,
B2C2_USB_READ_V8_MEM = 0x05,
B2C2_USB_READ_REG = 0x08,
B2C2_USB_WRITE_REG = 0x0A,
/* B2C2_USB_WRITEREGLO = 0x0A, */
B2C2_USB_WRITEREGHI = 0x0B,
B2C2_USB_FLASH_BLOCK = 0x10,
B2C2_USB_I2C_REQUEST = 0x11,
B2C2_USB_UTILITY = 0x12,
} flexcop_usb_request_t;
/* function definition for I2C_REQUEST */
typedef enum {
USB_FUNC_I2C_WRITE = 0x01,
USB_FUNC_I2C_MULTIWRITE = 0x02,
USB_FUNC_I2C_READ = 0x03,
USB_FUNC_I2C_REPEATWRITE = 0x04,
USB_FUNC_GET_DESCRIPTOR = 0x05,
USB_FUNC_I2C_REPEATREAD = 0x06,
/* DKT 020208 - add this to support special case of DiSEqC */
USB_FUNC_I2C_CHECKWRITE = 0x07,
USB_FUNC_I2C_CHECKRESULT = 0x08,
} flexcop_usb_i2c_function_t;
/*
* function definition for UTILITY request 0x12
* DKT 020304 - new utility function
*/
typedef enum {
UTILITY_SET_FILTER = 0x01,
UTILITY_DATA_ENABLE = 0x02,
UTILITY_FLEX_MULTIWRITE = 0x03,
UTILITY_SET_BUFFER_SIZE = 0x04,
UTILITY_FLEX_OPERATOR = 0x05,
UTILITY_FLEX_RESET300_START = 0x06,
UTILITY_FLEX_RESET300_STOP = 0x07,
UTILITY_FLEX_RESET300 = 0x08,
UTILITY_SET_ISO_SIZE = 0x09,
UTILITY_DATA_RESET = 0x0A,
UTILITY_GET_DATA_STATUS = 0x10,
UTILITY_GET_V8_REG = 0x11,
/* DKT 020326 - add function for v1.14 */
UTILITY_SRAM_WRITE = 0x12,
UTILITY_SRAM_READ = 0x13,
UTILITY_SRAM_TESTFILL = 0x14,
UTILITY_SRAM_TESTSET = 0x15,
UTILITY_SRAM_TESTVERIFY = 0x16,
} flexcop_usb_utility_function_t;
#define B2C2_WAIT_FOR_OPERATION_RW 1*HZ /* 1 s */
#define B2C2_WAIT_FOR_OPERATION_RDW 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_WDW 1*HZ /* 1 s */
#define B2C2_WAIT_FOR_OPERATION_V8READ 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_V8WRITE 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_V8FLASH 3*HZ /* 3 s */
typedef enum {
V8_MEMORY_PAGE_DVB_CI = 0x20,
V8_MEMORY_PAGE_DVB_DS = 0x40,
V8_MEMORY_PAGE_MULTI2 = 0x60,
V8_MEMORY_PAGE_FLASH = 0x80
} flexcop_usb_mem_page_t;
#define V8_MEMORY_EXTENDED (1 << 15)
#define USB_MEM_READ_MAX 32
#define USB_MEM_WRITE_MAX 1
#define USB_FLASH_MAX 8
#define V8_MEMORY_PAGE_SIZE 0x8000 // 32K
#define V8_MEMORY_PAGE_MASK 0x7FFF
#endif
/*
* flexcop.c - driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* Copyright (C) 2004-5 Patrick Boettcher <patrick.boettcher@desy.de>
*
* based on the skystar2-driver
* Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
*
* Acknowledgements:
* John Jurrius from BBTI, Inc. for extensive support with
* code examples and data books
*
* Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
*
* Contributions to the skystar2-driver have been done by
* Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
* Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
* Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac filtering)
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* 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 Lesser 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 "flexcop.h"
#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define DEBSTATUS ""
#else
#define DEBSTATUS " (debugging is not enabled)"
#endif
int b2c2_flexcop_debug;
module_param_named(debug, b2c2_flexcop_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram (|-able))." DEBSTATUS);
#undef DEBSTATUS
/* global zero for ibi values */
flexcop_ibi_value ibi_zero;
static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct flexcop_device *fc = dvbdmxfeed->demux->priv;
return flexcop_pid_feed_control(fc,dvbdmxfeed,1);
}
static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct flexcop_device *fc = dvbdmxfeed->demux->priv;
return flexcop_pid_feed_control(fc,dvbdmxfeed,0);
}
static int flexcop_dvb_init(struct flexcop_device *fc)
{
int ret;
if ((ret = dvb_register_adapter(&fc->dvb_adapter,"FlexCop Digital TV device",THIS_MODULE)) < 0) {
err("error registering DVB adapter");
return ret;
}
fc->dvb_adapter.priv = fc;
fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
fc->demux.priv = fc;
fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
fc->demux.start_feed = flexcop_dvb_start_feed;
fc->demux.stop_feed = flexcop_dvb_stop_feed;
fc->demux.write_to_decoder = NULL;
if ((ret = dvb_dmx_init(&fc->demux)) < 0) {
err("dvb_dmx failed: error %d",ret);
goto err_dmx;
}
fc->hw_frontend.source = DMX_FRONTEND_0;
fc->dmxdev.filternum = fc->demux.feednum;
fc->dmxdev.demux = &fc->demux.dmx;
fc->dmxdev.capabilities = 0;
if ((ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter)) < 0) {
err("dvb_dmxdev_init failed: error %d",ret);
goto err_dmx_dev;
}
if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
err("adding hw_frontend to dmx failed: error %d",ret);
goto err_dmx_add_hw_frontend;
}
fc->mem_frontend.source = DMX_MEMORY_FE;
if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend)) < 0) {
err("adding mem_frontend to dmx failed: error %d",ret);
goto err_dmx_add_mem_frontend;
}
if ((ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
err("connect frontend failed: error %d",ret);
goto err_connect_frontend;
}
dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
fc->init_state |= FC_STATE_DVB_INIT;
goto success;
err_connect_frontend:
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
err_dmx_add_mem_frontend:
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
err_dmx_add_hw_frontend:
dvb_dmxdev_release(&fc->dmxdev);
err_dmx_dev:
dvb_dmx_release(&fc->demux);
err_dmx:
dvb_unregister_adapter(&fc->dvb_adapter);
return ret;
success:
return 0;
}
static void flexcop_dvb_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_DVB_INIT) {
dvb_net_release(&fc->dvbnet);
fc->demux.dmx.close(&fc->demux.dmx);
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
dvb_dmxdev_release(&fc->dmxdev);
dvb_dmx_release(&fc->demux);
dvb_unregister_adapter(&fc->dvb_adapter);
deb_info("deinitialized dvb stuff\n");
}
fc->init_state &= ~FC_STATE_DVB_INIT;
}
/* these methods are necessary to achieve the long-term-goal of hiding the
* struct flexcop_device from the bus-parts */
void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
{
dvb_dmx_swfilter(&fc->demux, buf, len);
}
EXPORT_SYMBOL(flexcop_pass_dmx_data);
void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
{
dvb_dmx_swfilter_packets(&fc->demux, buf, no);
}
EXPORT_SYMBOL(flexcop_pass_dmx_packets);
static void flexcop_reset(struct flexcop_device *fc)
{
flexcop_ibi_value v210,v204;
/* reset the flexcop itself */
fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
v210.raw = 0;
v210.sw_reset_210.reset_blocks = 0xff;
v210.sw_reset_210.Block_reset_enable = 0xb2;
fc->write_ibi_reg(fc,sw_reset_210,v210);
/* reset the periphical devices */
v204 = fc->read_ibi_reg(fc,misc_204);
v204.misc_204.Per_reset_sig = 0;
fc->write_ibi_reg(fc,misc_204,v204);
v204.misc_204.Per_reset_sig = 1;
fc->write_ibi_reg(fc,misc_204,v204);
/* v208.raw = 0;
v208.ctrl_208.Null_filter_sig = 1;
fc->write_ibi_reg(fc,ctrl_208,v208);*/
}
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
{
void *bus;
struct flexcop_device *fc = kmalloc(sizeof(struct flexcop_device), GFP_KERNEL);
if (!fc) {
err("no memory");
return NULL;
}
memset(fc, 0, sizeof(struct flexcop_device));
bus = kmalloc(bus_specific_len, GFP_KERNEL);
if (!bus) {
err("no memory");
kfree(fc);
return NULL;
}
memset(bus, 0, bus_specific_len);
fc->bus_specific = bus;
return fc;
}
EXPORT_SYMBOL(flexcop_device_kmalloc);
void flexcop_device_kfree(struct flexcop_device *fc)
{
kfree(fc->bus_specific);
kfree(fc);
}
EXPORT_SYMBOL(flexcop_device_kfree);
int flexcop_device_initialize(struct flexcop_device *fc)
{
int ret;
ibi_zero.raw = 0;
flexcop_reset(fc);
flexcop_determine_revision(fc);
flexcop_sram_init(fc);
flexcop_hw_filter_init(fc);
flexcop_smc_ctrl(fc, 0);
if (fc->get_mac_addr(fc, 0) == 0) {
u8 *b = fc->mac_address;
info("MAC address = %02x:%02x:%02x:%02x:%02x:%02x", b[0],b[1],b[2],b[3],b[4],b[5]);
flexcop_set_mac_filter(fc,fc->mac_address);
flexcop_mac_filter_ctrl(fc,1);
} else
warn("reading of MAC address failed.\n");
if ((ret = flexcop_dvb_init(fc)))
goto error;
if ((ret = flexcop_i2c_init(fc)))
goto error;
if ((ret = flexcop_frontend_init(fc)))
goto error;
flexcop_device_name(fc,"initialization of","complete");
ret = 0;
goto success;
error:
flexcop_device_exit(fc);
success:
return ret;
}
EXPORT_SYMBOL(flexcop_device_initialize);
void flexcop_device_exit(struct flexcop_device *fc)
{
flexcop_frontend_exit(fc);
flexcop_i2c_exit(fc);
flexcop_dvb_exit(fc);
}
EXPORT_SYMBOL(flexcop_device_exit);
static int flexcop_module_init(void)
{
info(DRIVER_NAME " loaded successfully");
return 0;
}
static void flexcop_module_cleanup(void)
{
info(DRIVER_NAME " unloaded successfully");
}
module_init(flexcop_module_init);
module_exit(flexcop_module_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop.h - private header file for all flexcop-chip-source files.
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_H__
#define __FLEXCOP_H___
#define FC_LOG_PREFIX "b2c2-flexcop"
#include "flexcop-common.h"
extern int b2c2_flexcop_debug;
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0)
#else
#define dprintk(level,args...)
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_tuner(args...) dprintk(0x02,args)
#define deb_i2c(args...) dprintk(0x04,args)
#define deb_ts(args...) dprintk(0x08,args)
#define deb_sram(args...) dprintk(0x10,args)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment