1
0
Fork 0

Merge with master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

hifive-unleashed-5.1
David Woodhouse 2005-05-19 11:54:00 +01:00
commit 7063e6c717
206 changed files with 8580 additions and 3758 deletions

View File

@ -882,13 +882,12 @@ S: Blacksburg, Virginia 24061
S: USA
N: Randy Dunlap
E: rddunlap@osdl.org
E: rdunlap@xenotime.net
W: http://www.xenotime.net/linux/linux.html
W: http://www.linux-usb.org
D: Linux-USB subsystem, USB core/UHCI/printer/storage drivers
D: x86 SMP, ACPI, bootflag hacking
S: 12725 SW Millikan Way, Suite 400
S: Beaverton, Oregon 97005
S: (ask for current address)
S: USA
N: Bob Dunlop

View File

@ -0,0 +1,205 @@
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.
This document hopefully describes things about the flexcop and its
device-offsprings. Goal was to write an easy-to-write and easy-to-read set of
drivers based on the skystar2.c and other information.
Remark: flexcop-pci.c was a copy of skystar2.c, but every line has been
touched and rewritten.
History & News
==============
2005-04-01 - correct USB ISOC transfers (thanks to Vadim Catana)
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
5c) feeding of ISOC data to the software demux (format of the isochronous data
and speed optimization, no real error) (thanks to Vadim Catana)
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)
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 | O
* - 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)
NEWS: when the driver is loaded and unloaded and loaded again (w/o doing
anything in the while the driver is loaded the first time), no transfers take
place anymore.
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) (almost done, incompatibilities with the Nexus-CA)
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
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).
Boleslaw Ciesielski for pointing out a problem with firmware loader.
Vadim Catana for correcting the USB transfer.
comments, critics and ideas to linux-dvb@linuxtv.org.

View File

@ -17,74 +17,53 @@ Because of this, you need to enable
"Device drivers" => "Multimedia devices"
=> "Video For Linux" => "BT848 Video For Linux"
Furthermore you need to enable
"Device drivers" => "Multimedia devices" => "Digital Video Broadcasting Devices"
=> "DVB for Linux" "DVB Core Support" "Nebula/Pinnacle PCTV/TwinHan PCI Cards"
2) Loading Modules
==================
In general you need to load the bttv driver, which will handle the gpio and
i2c communication for us. Next you need the common dvb-bt8xx device driver
and one frontend driver.
The bttv driver will HANG YOUR SYSTEM IF YOU DO NOT SPECIFY THE CORRECT
CARD ID!
(If you don't get your card running and you suspect that the card id you're
using is wrong, have a look at "bttv-cards.c" for a list of possible card
ids.)
Pay attention to failures when you load the frontend drivers
(e.g. dmesg, /var/log/messages).
i2c communication for us, plus the common dvb-bt8xx device driver.
The frontends for Nebula (nxt6000), Pinnacle PCTV (cx24110) and
TwinHan (dst) are loaded automatically by the dvb-bt8xx device driver.
3a) Nebula / Pinnacle PCTV
--------------------------
$ modprobe bttv i2c_hw=1 card=0x68
$ modprobe dvb-bt8xx
For Nebula cards use the "nxt6000" frontend driver:
$ modprobe nxt6000
$ modprobe bttv (normally bttv is being loaded automatically by kmod)
$ modprobe dvb-bt8xx (or just place dvb-bt8xx in /etc/modules for automatic loading)
For Pinnacle PCTV cards use the "cx24110" frontend driver:
$ modprobe cx24110
3b) TwinHan
-----------
3b) TwinHan and Clones
--------------------------
$ modprobe bttv i2c_hw=1 card=0x71
$ modprobe dvb-bt8xx
$ modprobe dst
The value 0x71 will override the PCI type detection for dvb-bt8xx, which
is necessary for TwinHan cards.#
The value 0x71 will override the PCI type detection for dvb-bt8xx,
which is necessary for TwinHan cards.
If you're having an older card (blue color circuit) and card=0x71 locks your
machine, try using 0x68, too. If that does not work, ask on the DVB mailing list.
If you're having an older card (blue color circuit) and card=0x71 locks
your machine, try using 0x68, too. If that does not work, ask on the
mailing list.
The DST module takes a couple of useful parameters, in case the
dst drivers fails to detect your type of card correctly.
The DST module takes a couple of useful parameters.
dst_type takes values 0 (satellite), 1 (terrestial TV), 2 (cable).
verbose takes values 0 to 5. These values control the verbosity level.
dst_type_flags takes bit combined values:
1 = new tuner type packets. You can use this if your card is detected
and you have debug and you continually see the tuner packets not
working (make sure not a basic problem like dish alignment etc.)
debug takes values 0 and 1. You can either disable or enable debugging.
2 = TS 204. If your card tunes OK, but the picture is terrible, seemingly
breaking up in one half continually, and crc fails a lot, then
this is worth a try (or trying to turn off)
dst_addons takes values 0 and 0x20. A value of 0 means it is a FTA card.
0x20 means it has a Conditional Access slot.
4 = has symdiv. Some cards, mostly without new tuner packets, require
a symbol division algorithm. Doesn't apply to terrestial TV.
You can also specify a value to have the autodetected values turned off
(e.g. 0). The autodected values are determined bythe cards 'response
The autodected values are determined bythe cards 'response
string' which you can see in your logs e.g.
dst_check_ci: recognize DST-MOT
dst_get_device_id: Recognise [DSTMCI]
or
dst_check_ci: unable to recognize DSTXCI or STXCI
--
Authors: Richard Walker, Jamie Honan, Michael Hunold
Authors: Richard Walker, Jamie Honan, Michael Hunold, Manu Abraham

View File

@ -0,0 +1,219 @@
* For the user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTE: This document describes the usage of the high level CI API as
in accordance to the Linux DVB API. This is a not a documentation for the,
existing low level CI API.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To utilize the High Level CI capabilities,
(1*) This point is valid only for the Twinhan/clones
For the Twinhan/Twinhan clones, the dst_ca module handles the CI
hardware handling.This module is loaded automatically if a CI
(Common Interface, that holds the CAM (Conditional Access Module)
is detected.
(2) one requires a userspace application, ca_zap. This small userland
application is in charge of sending the descrambling related information
to the CAM.
This application requires the following to function properly as of now.
(a) Tune to a valid channel, with szap.
eg: $ szap -c channels.conf -r "TMC" -x
(b) a channels.conf containing a valid PMT PID
eg: TMC:11996:h:0:27500:278:512:650:321
here 278 is a valid PMT PID. the rest of the values are the
same ones that szap uses.
(c) after running a szap, you have to run ca_zap, for the
descrambler to function,
eg: $ ca_zap patched_channels.conf "TMC"
The patched means a patch to apply to scan, such that scan can
generate a channels.conf_with pmt, which has this PMT PID info
(NOTE: szap cannot use this channels.conf with the PMT_PID)
(d) Hopeflly Enjoy your favourite subscribed channel as you do with
a FTA card.
(3) Currently ca_zap, and dst_test, both are meant for demonstration
purposes only, they can become full fledged applications if necessary.
* Cards that fall in this category
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At present the cards that fall in this category are the Twinhan and it's
clones, these cards are available as VVMER, Tomato, Hercules, Orange and
so on.
* CI modules that are supported
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The CI module support is largely dependant upon the firmware on the cards
Some cards do support almost all of the available CI modules. There is
nothing much that can be done in order to make additional CI modules
working with these cards.
Modules that have been tested by this driver at present are
(1) Irdeto 1 and 2 from SCM
(2) Viaccess from SCM
(3) Dragoncam
* The High level CI API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* For the programmer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With the High Level CI approach any new card with almost any random
architecture can be implemented with this style, the definitions
insidethe switch statement can be easily adapted for any card, thereby
eliminating the need for any additional ioctls.
The disadvantage is that the driver/hardware has to manage the rest. For
the application programmer it would be as simple as sending/receiving an
array to/from the CI ioctls as defined in the Linux DVB API. No changes
have been made in the API to accomodate this feature.
* Why the need for another CI interface ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is one of the most commonly asked question. Well a nice question.
Strictly speaking this is not a new interface.
The CI interface is defined in the DVB API in ca.h as
typedef struct ca_slot_info {
int num; /* slot number */
int type; /* CA interface this slot supports */
#define CA_CI 1 /* CI high level interface */
#define CA_CI_LINK 2 /* CI link layer level interface */
#define CA_CI_PHYS 4 /* CI physical layer level interface */
#define CA_DESCR 8 /* built-in descrambler */
#define CA_SC 128 /* simple smart card interface */
unsigned int flags;
#define CA_CI_MODULE_PRESENT 1 /* module (or card) inserted */
#define CA_CI_MODULE_READY 2
} ca_slot_info_t;
This CI interface follows the CI high level interface, which is not
implemented by most applications. Hence this area is revisited.
This CI interface is quite different in the case that it tries to
accomodate all other CI based devices, that fall into the other categories
This means that this CI interface handles the EN50221 style tags in the
Application layer only and no session management is taken care of by the
application. The driver/hardware will take care of all that.
This interface is purely an EN50221 interface exchanging APDU's. This
means that no session management, link layer or a transport layer do
exist in this case in the application to driver communication. It is
as simple as that. The driver/hardware has to take care of that.
With this High Level CI interface, the interface can be defined with the
regular ioctls.
All these ioctls are also valid for the High level CI interface
#define CA_RESET _IO('o', 128)
#define CA_GET_CAP _IOR('o', 129, ca_caps_t)
#define CA_GET_SLOT_INFO _IOR('o', 130, ca_slot_info_t)
#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t)
#define CA_GET_MSG _IOR('o', 132, ca_msg_t)
#define CA_SEND_MSG _IOW('o', 133, ca_msg_t)
#define CA_SET_DESCR _IOW('o', 134, ca_descr_t)
#define CA_SET_PID _IOW('o', 135, ca_pid_t)
On querying the device, the device yields information thus
CA_GET_SLOT_INFO
----------------------------
Command = [info]
APP: Number=[1]
APP: Type=[1]
APP: flags=[1]
APP: CI High level interface
APP: CA/CI Module Present
CA_GET_CAP
----------------------------
Command = [caps]
APP: Slots=[1]
APP: Type=[1]
APP: Descrambler keys=[16]
APP: Type=[1]
CA_SEND_MSG
----------------------------
Descriptors(Program Level)=[ 09 06 06 04 05 50 ff f1]
Found CA descriptor @ program level
(20) ES type=[2] ES pid=[201] ES length =[0 (0x0)]
(25) ES type=[4] ES pid=[301] ES length =[0 (0x0)]
ca_message length is 25 (0x19) bytes
EN50221 CA MSG=[ 9f 80 32 19 03 01 2d d1 f0 08 01 09 06 06 04 05 50 ff f1 02 e0 c9 00 00 04 e1 2d 00 00]
Not all ioctl's are implemented in the driver from the API, the other
features of the hardware that cannot be implemented by the API are achieved
using the CA_GET_MSG and CA_SEND_MSG ioctls. An EN50221 style wrapper is
used to exchange the data to maintain compatibility with other hardware.
/* a message to/from a CI-CAM */
typedef struct ca_msg {
unsigned int index;
unsigned int type;
unsigned int length;
unsigned char msg[256];
} ca_msg_t;
The flow of data can be described thus,
App (User)
-----
parse
|
|
v
en50221 APDU (package)
--------------------------------------
| | | High Level CI driver
| | |
| v |
| en50221 APDU (unpackage) |
| | |
| | |
| v |
| sanity checks |
| | |
| | |
| v |
| do (H/W dep) |
--------------------------------------
| Hardware
|
v
The High Level CI interface uses the EN50221 DVB standard, following a
standard ensures futureproofness.

View File

@ -107,7 +107,7 @@ sub tda10045 {
sub tda10046 {
my $sourcefile = "tt_budget_217g.zip";
my $url = "http://www.technotrend.de/new/217g/$sourcefile";
my $hash = "a25b579e37109af60f4a36c37893957c";
my $hash = "6a7e1e2f2644b162ff0502367553c72d";
my $outfile = "dvb-fe-tda10046.fw";
my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
@ -115,7 +115,7 @@ sub tda10046 {
wgetfile($sourcefile, $url);
unzip($sourcefile, $tmpdir);
extract("$tmpdir/software/OEM/PCI/App/ttlcdacc.dll", 0x3f731, 24479, "$tmpdir/fwtmp");
extract("$tmpdir/software/OEM/PCI/App/ttlcdacc.dll", 0x3f731, 24478, "$tmpdir/fwtmp");
verify("$tmpdir/fwtmp", $hash);
copy("$tmpdir/fwtmp", $outfile);

View File

@ -63,3 +63,23 @@ Why: Outside of Linux, the only implementations of anything even
people, who might be using implementations that I am not aware
of, to adjust to this upcoming change.
Who: Paul E. McKenney <paulmck@us.ibm.com>
---------------------------
What: IEEE1394 Audio and Music Data Transmission Protocol driver,
Connection Management Procedures driver
When: November 2005
Files: drivers/ieee1394/{amdtp,cmp}*
Why: These are incomplete, have never worked, and are better implemented
in userland via raw1394 (see http://freebob.sourceforge.net/ for
example.)
Who: Jody McIntyre <scjody@steamballoon.com>
---------------------------
What: raw1394: requests of type RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN
When: November 2005
Why: Deprecated in favour of the new ioctl-based rawiso interface, which is
more efficient. You should really be using libraw1394 for raw1394
access anyway.
Who: Jody McIntyre <scjody@steamballoon.com>

View File

@ -7,7 +7,6 @@ that support it. For example, a given bus might look like this:
|-- 0000:17:00.0
| |-- class
| |-- config
| |-- detach_state
| |-- device
| |-- irq
| |-- local_cpus
@ -19,7 +18,7 @@ that support it. For example, a given bus might look like this:
| |-- subsystem_device
| |-- subsystem_vendor
| `-- vendor
`-- detach_state
`-- ...
The topmost element describes the PCI domain and bus number. In this case,
the domain number is 0000 and the bus number is 17 (both values are in hex).
@ -31,7 +30,6 @@ files, each with their own function.
---- --------
class PCI class (ascii, ro)
config PCI config space (binary, rw)
detach_state connection status (bool, rw)
device PCI device (ascii, ro)
irq IRQ number (ascii, ro)
local_cpus nearby CPU mask (cpumask, ro)
@ -85,4 +83,4 @@ useful return codes should be provided.
Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms
wishing to support legacy functionality should define it and provide
pci_legacy_read, pci_legacy_write and pci_mmap_legacy_page_range functions.
pci_legacy_read, pci_legacy_write and pci_mmap_legacy_page_range functions.

View File

@ -207,27 +207,6 @@ SYSTEM_SHUTDOWN, I do not understand this one too much. probably event
#READY_AFTER_RESUME
#
Driver Detach Power Management
The kernel now supports the ability to place a device in a low-power
state when it is detached from its driver, which happens when its
module is removed.
Each device contains a 'detach_state' file in its sysfs directory
which can be used to control this state. Reading from this file
displays what the current detach state is set to. This is 0 (On) by
default. A user may write a positive integer value to this file in the
range of 1-4 inclusive.
A value of 1-3 will indicate the device should be placed in that
low-power state, which will cause ->suspend() to be called for that
device. A value of 4 indicates that the device should be shutdown, so
->shutdown() will be called for that device.
The driver is responsible for reinitializing the device when the
module is re-inserted during it's ->probe() (or equivalent) method.
The driver core will not call any extra functions when binding the
device to the driver.
pm_message_t meaning

View File

@ -347,8 +347,8 @@ address that is created by firmware. An example vty-server sysfs entry
looks like the following:
Pow5:/sys/bus/vio/drivers/hvcs/30000004 # ls
. current_vty devspec name partner_vtys
.. detach_state index partner_clcs vterm_state
. current_vty devspec name partner_vtys
.. index partner_clcs vterm_state
Each entry is provided, by default with a "name" attribute. Reading the
"name" attribute will reveal the device type as shown in the following

View File

@ -530,7 +530,7 @@ endif
include $(srctree)/arch/$(ARCH)/Makefile
# arch Makefile may override CC so keep this after arch Makefile is included
NOSTDINC_FLAGS := -nostdinc -isystem $(shell $(CC) -print-file-name=include)
NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
CHECKFLAGS += $(NOSTDINC_FLAGS)
# warn about C99 declaration after statement

View File

@ -1150,16 +1150,13 @@ osf_usleep_thread(struct timeval32 __user *sleep, struct timeval32 __user *remai
if (get_tv32(&tmp, sleep))
goto fault;
ticks = tmp.tv_usec;
ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ);
ticks += tmp.tv_sec * HZ;
ticks = timeval_to_jiffies(&tmp);
current->state = TASK_INTERRUPTIBLE;
ticks = schedule_timeout(ticks);
if (remain) {
tmp.tv_sec = ticks / HZ;
tmp.tv_usec = ticks % HZ;
jiffies_to_timeval(ticks, &tmp);
if (put_tv32(remain, &tmp))
goto fault;
}

View File

@ -24,9 +24,6 @@ __asm__(".align 4\nvide: ret");
static void __init init_amd(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_X86_SMP
int cpu = c == &boot_cpu_data ? 0 : c - cpu_data;
#endif
u32 l, h;
int mbytes = num_physpages >> (20-PAGE_SHIFT);
int r;
@ -205,7 +202,9 @@ static void __init init_amd(struct cpuinfo_x86 *c)
* of two.
*/
if (c->x86_num_cores > 1) {
cpu_core_id[cpu] = cpu >> hweight32(c->x86_num_cores - 1);
int cpu = smp_processor_id();
/* Fix up the APIC ID following AMD specifications. */
cpu_core_id[cpu] >>= hweight32(c->x86_num_cores - 1);
printk(KERN_INFO "CPU %d(%d) -> Core %d\n",
cpu, c->x86_num_cores, cpu_core_id[cpu]);
}

View File

@ -243,6 +243,13 @@ static void __init early_cpu_detect(void)
}
early_intel_workaround(c);
#ifdef CONFIG_SMP
#ifdef CONFIG_X86_HT
phys_proc_id[smp_processor_id()] =
#endif
cpu_core_id[smp_processor_id()] = (cpuid_ebx(1) >> 24) & 0xff;
#endif
}
void __init generic_identify(struct cpuinfo_x86 * c)

View File

@ -253,7 +253,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci
#define MAX_PCIEROOT 6
static int quirk_aspm_offset[MAX_PCIEROOT << 3];
#define GET_INDEX(a, b) (((a - PCI_DEVICE_ID_INTEL_MCH_PA) << 3) + b)
#define GET_INDEX(a, b) ((((a) - PCI_DEVICE_ID_INTEL_MCH_PA) << 3) + ((b) & 7))
static int quirk_pcie_aspm_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{

View File

@ -13,7 +13,6 @@
#define INCLUDES
#include "compat_ioctl.c"
#include <asm/ioctl32.h>
#define IOCTL_NR(a) ((a) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))

View File

@ -1,7 +1,7 @@
/*
* pmu.c, Power Management Unit routines for NEC VR4100 series.
*
* Copyright (C) 2003-2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
* Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
*
* 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
@ -17,7 +17,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/types.h>
@ -27,20 +29,31 @@
#include <asm/reboot.h>
#include <asm/system.h>
#define PMUCNT2REG KSEG1ADDR(0x0f0000c6)
#define PMU_TYPE1_BASE 0x0b0000a0UL
#define PMU_TYPE1_SIZE 0x0eUL
#define PMU_TYPE2_BASE 0x0f0000c0UL
#define PMU_TYPE2_SIZE 0x10UL
#define PMUCNT2REG 0x06
#define SOFTRST 0x0010
static void __iomem *pmu_base;
#define pmu_read(offset) readw(pmu_base + (offset))
#define pmu_write(offset, value) writew((value), pmu_base + (offset))
static inline void software_reset(void)
{
uint16_t val;
uint16_t pmucnt2;
switch (current_cpu_data.cputype) {
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
val = readw(PMUCNT2REG);
val |= SOFTRST;
writew(val, PMUCNT2REG);
pmucnt2 = pmu_read(PMUCNT2REG);
pmucnt2 |= SOFTRST;
pmu_write(PMUCNT2REG, pmucnt2);
break;
default:
break;
@ -71,6 +84,34 @@ static void vr41xx_power_off(void)
static int __init vr41xx_pmu_init(void)
{
unsigned long start, size;
switch (current_cpu_data.cputype) {
case CPU_VR4111:
case CPU_VR4121:
start = PMU_TYPE1_BASE;
size = PMU_TYPE1_SIZE;
break;
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
start = PMU_TYPE2_BASE;
size = PMU_TYPE2_SIZE;
break;
default:
printk("Unexpected CPU of NEC VR4100 series\n");
return -ENODEV;
}
if (request_mem_region(start, size, "PMU") == NULL)
return -EBUSY;
pmu_base = ioremap(start, size);
if (pmu_base == NULL) {
release_mem_region(start, size);
return -EBUSY;
}
_machine_restart = vr41xx_restart;
_machine_halt = vr41xx_halt;
_machine_power_off = vr41xx_power_off;
@ -78,4 +119,4 @@ static int __init vr41xx_pmu_init(void)
return 0;
}
early_initcall(vr41xx_pmu_init);
core_initcall(vr41xx_pmu_init);

View File

@ -753,6 +753,8 @@ void __init setup_arch(char **cmdline_p)
strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
parse_early_param();
/* set up the bootmem stuff with available memory */
do_init_bootmem();
if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab);

View File

@ -236,9 +236,15 @@ static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
(*prev)->fd, pollfds[i].fd);
goto out;
}
memcpy(&pollfds[i], &pollfds[i + 1],
(pollfds_num - i - 1) * sizeof(pollfds[0]));
pollfds_num--;
/* This moves the *whole* array after pollfds[i] (though
* it doesn't spot as such)! */
memmove(&pollfds[i], &pollfds[i + 1],
(pollfds_num - i) * sizeof(pollfds[0]));
if(last_irq_ptr == &old_fd->next)
last_irq_ptr = prev;
*prev = (*prev)->next;

View File

@ -303,6 +303,20 @@ config HPET_TIMER
as it is off-chip. You can find the HPET spec at
<http://www.intel.com/labs/platcomp/hpet/hpetspec.htm>.
config X86_PM_TIMER
bool "PM timer"
default y
help
Support the ACPI PM timer for time keeping. This is slow,
but is useful on some chipsets without HPET on systems with more
than one CPU. On a single processor or single socket multi core
system it is normally not required.
When the PM timer is active 64bit vsyscalls are disabled
and should not be enabled (/proc/sys/kernel/vsyscall64 should
not be changed).
The kernel selects the PM timer only as a last resort, so it is
useful to enable just in case.
config HPET_EMULATE_RTC
bool "Provide RTC interrupt"
depends on HPET_TIMER && RTC=y

View File

@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.11-bk7
# Sat Mar 12 23:43:44 2005
# Linux kernel version: 2.6.12-rc4
# Fri May 13 06:39:11 2005
#
CONFIG_X86_64=y
CONFIG_64BIT=y
@ -11,8 +11,6 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_X86_CMPXCHG=y
CONFIG_EARLY_PRINTK=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
@ -22,6 +20,7 @@ CONFIG_GENERIC_IOMAP=y
CONFIG_EXPERIMENTAL=y
CONFIG_CLEAN_COMPILE=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
#
# General setup
@ -33,7 +32,6 @@ CONFIG_POSIX_MQUEUE=y
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
# CONFIG_AUDIT is not set
CONFIG_LOG_BUF_SHIFT=18
# CONFIG_HOTPLUG is not set
CONFIG_KOBJECT_UEVENT=y
CONFIG_IKCONFIG=y
@ -43,10 +41,11 @@ CONFIG_IKCONFIG_PROC=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SHMEM=y
CONFIG_CC_ALIGN_FUNCTIONS=0
CONFIG_CC_ALIGN_LABELS=0
@ -93,6 +92,9 @@ CONFIG_DISCONTIGMEM=y
CONFIG_NUMA=y
CONFIG_HAVE_DEC_LOCK=y
CONFIG_NR_CPUS=8
CONFIG_HPET_TIMER=y
CONFIG_X86_PM_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_GART_IOMMU=y
CONFIG_SWIOTLB=y
CONFIG_X86_MCE=y
@ -100,6 +102,7 @@ CONFIG_X86_MCE_INTEL=y
CONFIG_SECCOMP=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_ISA_DMA_API=y
#
# Power management options
@ -129,7 +132,7 @@ CONFIG_ACPI_NUMA=y
# CONFIG_ACPI_IBM is not set
CONFIG_ACPI_TOSHIBA=y
CONFIG_ACPI_BLACKLIST_YEAR=2001
CONFIG_ACPI_DEBUG=y
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_BUS=y
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
@ -141,6 +144,7 @@ CONFIG_ACPI_SYSTEM=y
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=y
# CONFIG_CPU_FREQ_DEBUG is not set
CONFIG_CPU_FREQ_STAT=y
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
@ -150,7 +154,6 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_TABLE=y
#
# CPUFreq processor drivers
@ -164,6 +167,7 @@ CONFIG_X86_ACPI_CPUFREQ=y
# shared options
#
CONFIG_X86_ACPI_CPUFREQ_PROC_INTF=y
# CONFIG_X86_SPEEDSTEP_LIB is not set
#
# Bus options (PCI etc.)
@ -172,19 +176,17 @@ CONFIG_PCI=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
CONFIG_UNORDERED_IO=y
# CONFIG_PCIEPORTBUS is not set
CONFIG_PCI_MSI=y
# CONFIG_PCI_LEGACY_PROC is not set
# CONFIG_PCI_NAMES is not set
# CONFIG_PCI_DEBUG is not set
#
# PCCARD (PCMCIA/CardBus) support
#
# CONFIG_PCCARD is not set
#
# PC-card bridges
#
#
# PCI Hotplug Support
#
@ -254,7 +256,7 @@ CONFIG_LBD=y
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
# CONFIG_IOSCHED_AS is not set
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_ATA_OVER_ETH is not set
@ -308,7 +310,8 @@ CONFIG_BLK_DEV_AMD74XX=y
CONFIG_BLK_DEV_PIIX=y
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
# CONFIG_BLK_DEV_PDC202XX_NEW is not set
CONFIG_BLK_DEV_PDC202XX_NEW=y
# CONFIG_PDC202XX_FORCE is not set
# CONFIG_BLK_DEV_SVWKS is not set
# CONFIG_BLK_DEV_SIIMAGE is not set
# CONFIG_BLK_DEV_SIS5513 is not set
@ -353,7 +356,7 @@ CONFIG_BLK_DEV_SD=y
#
# SCSI low-level drivers
#
CONFIG_BLK_DEV_3W_XXXX_RAID=y
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
@ -384,7 +387,6 @@ CONFIG_SCSI_SATA_VIA=y
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_IPS is not set
@ -392,7 +394,6 @@ CONFIG_SCSI_SATA_VIA=y
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_QLOGIC_ISP is not set
# CONFIG_SCSI_QLOGIC_FC is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
CONFIG_SCSI_QLA2XXX=y
@ -401,6 +402,7 @@ CONFIG_SCSI_QLA2XXX=y
# CONFIG_SCSI_QLA2300 is not set
# CONFIG_SCSI_QLA2322 is not set
# CONFIG_SCSI_QLA6312 is not set
# CONFIG_SCSI_LPFC is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_DEBUG is not set
@ -437,7 +439,6 @@ CONFIG_NET=y
#
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
# CONFIG_NETLINK_DEV is not set
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
@ -502,7 +503,7 @@ CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
CONFIG_TUN=y
#
# ARCnet devices
@ -525,8 +526,7 @@ CONFIG_MII=y
# CONFIG_HP100 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
CONFIG_AMD8111_ETH=y
# CONFIG_AMD8111E_NAPI is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_B44 is not set
CONFIG_FORCEDETH=y
@ -536,7 +536,7 @@ CONFIG_FORCEDETH=y
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
CONFIG_8139CP=m
CONFIG_8139CP=y
CONFIG_8139TOO=y
# CONFIG_8139TOO_PIO is not set
# CONFIG_8139TOO_TUNE_TWISTER is not set
@ -671,6 +671,7 @@ CONFIG_SERIAL_8250_NR_UARTS=4
#
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
@ -696,6 +697,7 @@ CONFIG_RTC=y
#
CONFIG_AGP=y
CONFIG_AGP_AMD64=y
CONFIG_AGP_INTEL=y
# CONFIG_DRM is not set
# CONFIG_MWAVE is not set
CONFIG_RAW_DRIVER=y
@ -703,7 +705,7 @@ CONFIG_HPET=y
# CONFIG_HPET_RTC_IRQ is not set
CONFIG_HPET_MMAP=y
CONFIG_MAX_RAW_DEVS=256
CONFIG_HANGCHECK_TIMER=y
# CONFIG_HANGCHECK_TIMER is not set
#
# TPM devices
@ -786,6 +788,8 @@ CONFIG_SOUND_ICH=y
#
# USB support
#
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
@ -797,8 +801,6 @@ CONFIG_USB_DEVICEFS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_OTG is not set
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
#
# USB Host Controller Drivers
@ -826,7 +828,6 @@ CONFIG_USB_PRINTER=y
#
CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_DEBUG is not set
# CONFIG_USB_STORAGE_RW_DETECT is not set
# CONFIG_USB_STORAGE_DATAFAB is not set
# CONFIG_USB_STORAGE_FREECOM is not set
# CONFIG_USB_STORAGE_ISD200 is not set
@ -965,7 +966,7 @@ CONFIG_AUTOFS_FS=y
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
CONFIG_JOLIET=y
# CONFIG_ZISOFS is not set
# CONFIG_UDF_FS is not set
@ -1092,9 +1093,10 @@ CONFIG_OPROFILE=y
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEBUG_KERNEL=y
CONFIG_MAGIC_SYSRQ=y
# CONFIG_PRINTK_TIME is not set
CONFIG_LOG_BUF_SHIFT=18
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set

View File

@ -28,6 +28,7 @@ obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o
obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o
obj-$(CONFIG_SWIOTLB) += swiotlb.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_X86_PM_TIMER) += pmtimer.o
obj-$(CONFIG_MODULES) += module.o

View File

@ -33,6 +33,7 @@
#include <asm/mpspec.h>
#include <asm/pgalloc.h>
#include <asm/mach_apic.h>
#include <asm/nmi.h>
int apic_verbosity;
@ -925,7 +926,7 @@ __init int oem_force_hpet_timer(void)
unsigned id;
DECLARE_BITMAP(clustermap, NUM_APIC_CLUSTERS);
bitmap_empty(clustermap, NUM_APIC_CLUSTERS);
bitmap_zero(clustermap, NUM_APIC_CLUSTERS);
for (i = 0; i < NR_CPUS; i++) {
id = bios_cpu_apicid[i];
@ -1056,7 +1057,7 @@ int __init APIC_init_uniprocessor (void)
nr_ioapics = 0;
#endif
setup_boot_APIC_clock();
check_nmi_watchdog();
return 0;
}

View File

@ -220,13 +220,18 @@ sysret_careful:
jmp sysret_check
/* Handle a signal */
/* edx: work flags (arg3) */
sysret_signal:
sti
testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME|_TIF_SINGLESTEP),%edx
jz 1f
/* Really a signal */
/* edx: work flags (arg3) */
leaq do_notify_resume(%rip),%rax
leaq -ARGOFFSET(%rsp),%rdi # &pt_regs -> arg1
xorl %esi,%esi # oldset -> arg2
call ptregscall_common
1: movl $_TIF_NEED_RESCHED,%edi
jmp sysret_check
/* Do syscall tracing */
@ -484,6 +489,8 @@ retint_careful:
jmp retint_check
retint_signal:
testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME|_TIF_SINGLESTEP),%edx
jz retint_swapgs
sti
SAVE_REST
movq $-1,ORIG_RAX(%rsp)
@ -492,8 +499,8 @@ retint_signal:
call do_notify_resume
RESTORE_REST
cli
movl $_TIF_NEED_RESCHED,%edi
GET_THREAD_INFO(%rcx)
movl $_TIF_WORK_MASK,%edi
jmp retint_check
#ifdef CONFIG_PREEMPT

View File

@ -1804,76 +1804,6 @@ device_initcall(ioapic_init_sysfs);
#define IO_APIC_MAX_ID 0xFE
int __init io_apic_get_unique_id (int ioapic, int apic_id)
{
union IO_APIC_reg_00 reg_00;
static physid_mask_t apic_id_map;
unsigned long flags;
int i = 0;
/*
* The P4 platform supports up to 256 APIC IDs on two separate APIC
* buses (one for LAPICs, one for IOAPICs), where predecessors only
* supports up to 16 on one shared APIC bus.
*
* TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full
* advantage of new APIC bus architecture.
*/
if (physids_empty(apic_id_map))
apic_id_map = phys_cpu_present_map;
spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(ioapic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
if (apic_id >= IO_APIC_MAX_ID) {
apic_printk(APIC_QUIET, KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying "
"%d\n", ioapic, apic_id, reg_00.bits.ID);
apic_id = reg_00.bits.ID;
}
/*
* Every APIC in a system must have a unique ID or we get lots of nice
* 'stuck on smp_invalidate_needed IPI wait' messages.
*/
if (physid_isset(apic_id, apic_id_map)) {
for (i = 0; i < IO_APIC_MAX_ID; i++) {
if (!physid_isset(i, apic_id_map))
break;
}
if (i == IO_APIC_MAX_ID)
panic("Max apic_id exceeded!\n");
apic_printk(APIC_VERBOSE, KERN_WARNING "IOAPIC[%d]: apic_id %d already used, "
"trying %d\n", ioapic, apic_id, i);
apic_id = i;
}
physid_set(apic_id, apic_id_map);
if (reg_00.bits.ID != apic_id) {
reg_00.bits.ID = apic_id;
spin_lock_irqsave(&ioapic_lock, flags);
io_apic_write(ioapic, 0, reg_00.raw);
reg_00.raw = io_apic_read(ioapic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
/* Sanity check */
if (reg_00.bits.ID != apic_id)
panic("IOAPIC[%d]: Unable change apic_id!\n", ioapic);
}
apic_printk(APIC_VERBOSE,KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id);
return apic_id;
}
int __init io_apic_get_version (int ioapic)
{
union IO_APIC_reg_01 reg_01;

View File

@ -107,6 +107,7 @@ static int __init mpf_checksum(unsigned char *mp, int len)
static void __init MP_processor_info (struct mpc_config_processor *m)
{
int ver;
static int found_bsp=0;
if (!(m->mpc_cpuflag & CPU_ENABLED))
return;
@ -126,11 +127,6 @@ static void __init MP_processor_info (struct mpc_config_processor *m)
" Processor ignored.\n", NR_CPUS);
return;
}
if (num_processors >= maxcpus) {
printk(KERN_WARNING "WARNING: maxcpus limit of %i reached."
" Processor ignored.\n", maxcpus);
return;
}
num_processors++;
@ -150,7 +146,19 @@ static void __init MP_processor_info (struct mpc_config_processor *m)
ver = 0x10;
}
apic_version[m->mpc_apicid] = ver;
bios_cpu_apicid[num_processors - 1] = m->mpc_apicid;
if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) {
/*
* bios_cpu_apicid is required to have processors listed
* in same order as logical cpu numbers. Hence the first
* entry is BSP, and so on.
*/
bios_cpu_apicid[0] = m->mpc_apicid;
x86_cpu_to_apicid[0] = m->mpc_apicid;
found_bsp = 1;
} else {
bios_cpu_apicid[num_processors - found_bsp] = m->mpc_apicid;
x86_cpu_to_apicid[num_processors - found_bsp] = m->mpc_apicid;
}
}
static void __init MP_bus_info (struct mpc_config_bus *m)
@ -759,7 +767,7 @@ void __init mp_register_ioapic (
mp_ioapics[idx].mpc_apicaddr = address;
set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
mp_ioapics[idx].mpc_apicid = io_apic_get_unique_id(idx, id);
mp_ioapics[idx].mpc_apicid = id;
mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx);
/*

View File

@ -33,6 +33,7 @@
#include <asm/msr.h>
#include <asm/proto.h>
#include <asm/kdebug.h>
#include <asm/local.h>
/*
* lapic_nmi_owner tracks the ownership of the lapic NMI hardware:
@ -59,7 +60,8 @@ int panic_on_timeout;
unsigned int nmi_watchdog = NMI_DEFAULT;
static unsigned int nmi_hz = HZ;
unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */
static unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */
static unsigned int nmi_p4_cccr_val;
/* Note that these events don't tick when the CPU idles. This means
the frequency varies with CPU load. */
@ -71,61 +73,87 @@ unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */
#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76
#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING
#define P6_EVNTSEL0_ENABLE (1 << 22)
#define P6_EVNTSEL_INT (1 << 20)
#define P6_EVNTSEL_OS (1 << 17)
#define P6_EVNTSEL_USR (1 << 16)
#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79
#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED
#define MSR_P4_MISC_ENABLE 0x1A0
#define MSR_P4_MISC_ENABLE_PERF_AVAIL (1<<7)
#define MSR_P4_MISC_ENABLE_PEBS_UNAVAIL (1<<12)
#define MSR_P4_PERFCTR0 0x300
#define MSR_P4_CCCR0 0x360
#define P4_ESCR_EVENT_SELECT(N) ((N)<<25)
#define P4_ESCR_OS (1<<3)
#define P4_ESCR_USR (1<<2)
#define P4_CCCR_OVF_PMI0 (1<<26)
#define P4_CCCR_OVF_PMI1 (1<<27)
#define P4_CCCR_THRESHOLD(N) ((N)<<20)
#define P4_CCCR_COMPLEMENT (1<<19)
#define P4_CCCR_COMPARE (1<<18)
#define P4_CCCR_REQUIRED (3<<16)
#define P4_CCCR_ESCR_SELECT(N) ((N)<<13)
#define P4_CCCR_ENABLE (1<<12)
/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter
CRU_ESCR0 (with any non-null event selector) through a complemented
max threshold. [IA32-Vol3, Section 14.9.9] */
#define MSR_P4_IQ_COUNTER0 0x30C
#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR)
#define P4_NMI_IQ_CCCR0 \
(P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \
P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE)
static __init inline int nmi_known_cpu(void)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
return boot_cpu_data.x86 == 15;
case X86_VENDOR_INTEL:
return boot_cpu_data.x86 == 15;
}
return 0;
}
/* Run after command line and cpu_init init, but before all other checks */
void __init nmi_watchdog_default(void)
{
if (nmi_watchdog != NMI_DEFAULT)
return;
/* For some reason the IO APIC watchdog doesn't work on the AMD
8111 chipset. For now switch to local APIC mode using
perfctr0 there. On Intel CPUs we don't have code to handle
the perfctr and the IO-APIC seems to work, so use that. */
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
nmi_watchdog = NMI_LOCAL_APIC;
printk(KERN_INFO
"Using local APIC NMI watchdog using perfctr0\n");
} else {
printk(KERN_INFO "Using IO APIC NMI watchdog\n");
if (nmi_known_cpu())
nmi_watchdog = NMI_LOCAL_APIC;
else
nmi_watchdog = NMI_IO_APIC;
}
}
/* Why is there no CPUID flag for this? */
static __init int cpu_has_lapic(void)
#ifdef CONFIG_SMP
/* The performance counters used by NMI_LOCAL_APIC don't trigger when
* the CPU is idle. To make sure the NMI watchdog really ticks on all
* CPUs during the test make them busy.
*/
static __init void nmi_cpu_busy(void *data)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_INTEL:
case X86_VENDOR_AMD:
return boot_cpu_data.x86 >= 6;
/* .... add more cpus here or find a different way to figure this out. */
default:
return 0;
}
volatile int *endflag = data;
local_irq_enable();
/* Intentionally don't use cpu_relax here. This is
to make sure that the performance counter really ticks,
even if there is a simulator or similar that catches the
pause instruction. On a real HT machine this is fine because
all other CPUs are busy with "useless" delay loops and don't
care if they get somewhat less cycles. */
while (*endflag == 0)
barrier();
}
#endif
static int __init check_nmi_watchdog (void)
int __init check_nmi_watchdog (void)
{
int counts[NR_CPUS];
volatile int endflag = 0;
int *counts;
int cpu;
if (nmi_watchdog == NMI_NONE)
return 0;
counts = kmalloc(NR_CPUS * sizeof(int), GFP_KERNEL);
if (!counts)
return -1;
if (nmi_watchdog == NMI_LOCAL_APIC && !cpu_has_lapic()) {
nmi_watchdog = NMI_NONE;
return -1;
}
printk(KERN_INFO "testing NMI watchdog ... ");
printk(KERN_INFO "Testing NMI watchdog ... ");
if (nmi_watchdog == NMI_LOCAL_APIC)
smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0);
for (cpu = 0; cpu < NR_CPUS; cpu++)
counts[cpu] = cpu_pda[cpu].__nmi_count;
@ -133,15 +161,22 @@ static int __init check_nmi_watchdog (void)
mdelay((10*1000)/nmi_hz); // wait 10 ticks
for (cpu = 0; cpu < NR_CPUS; cpu++) {
if (!cpu_online(cpu))
continue;
if (cpu_pda[cpu].__nmi_count - counts[cpu] <= 5) {
printk("CPU#%d: NMI appears to be stuck (%d)!\n",
endflag = 1;
printk("CPU#%d: NMI appears to be stuck (%d->%d)!\n",
cpu,
counts[cpu],
cpu_pda[cpu].__nmi_count);
nmi_active = 0;
lapic_nmi_owner &= ~LAPIC_NMI_WATCHDOG;
nmi_perfctr_msr = 0;
kfree(counts);
return -1;
}
}
endflag = 1;
printk("OK.\n");
/* now that we know it works we can reduce NMI frequency to
@ -149,10 +184,9 @@ static int __init check_nmi_watchdog (void)
if (nmi_watchdog == NMI_LOCAL_APIC)
nmi_hz = 1;
kfree(counts);
return 0;
}
/* Have this called later during boot so counters are updating */
late_initcall(check_nmi_watchdog);
int __init setup_nmi_watchdog(char *str)
{
@ -170,7 +204,7 @@ int __init setup_nmi_watchdog(char *str)
if (nmi >= NMI_INVALID)
return 0;
nmi_watchdog = nmi;
nmi_watchdog = nmi;
return 1;
}
@ -185,7 +219,10 @@ static void disable_lapic_nmi_watchdog(void)
wrmsr(MSR_K7_EVNTSEL0, 0, 0);
break;
case X86_VENDOR_INTEL:
wrmsr(MSR_IA32_EVNTSEL0, 0, 0);
if (boot_cpu_data.x86 == 15) {
wrmsr(MSR_P4_IQ_CCCR0, 0, 0);
wrmsr(MSR_P4_CRU_ESCR0, 0, 0);
}
break;
}
nmi_active = -1;
@ -253,7 +290,7 @@ void enable_timer_nmi_watchdog(void)
static int nmi_pm_active; /* nmi_active before suspend */
static int lapic_nmi_suspend(struct sys_device *dev, pm_message_t state)
static int lapic_nmi_suspend(struct sys_device *dev, u32 state)
{
nmi_pm_active = nmi_active;
disable_lapic_nmi_watchdog();
@ -300,22 +337,27 @@ late_initcall(init_lapic_nmi_sysfs);
* Original code written by Keith Owens.
*/
static void clear_msr_range(unsigned int base, unsigned int n)
{
unsigned int i;
for(i = 0; i < n; ++i)
wrmsr(base+i, 0, 0);
}
static void setup_k7_watchdog(void)
{
int i;
unsigned int evntsel;
/* No check, so can start with slow frequency */
nmi_hz = 1;
/* XXX should check these in EFER */
nmi_perfctr_msr = MSR_K7_PERFCTR0;
for(i = 0; i < 4; ++i) {
/* Simulator may not support it */
if (checking_wrmsrl(MSR_K7_EVNTSEL0+i, 0UL))
if (checking_wrmsrl(MSR_K7_EVNTSEL0+i, 0UL)) {
nmi_perfctr_msr = 0;
return;
}
wrmsrl(MSR_K7_PERFCTR0+i, 0UL);
}
@ -325,12 +367,54 @@ static void setup_k7_watchdog(void)
| K7_NMI_EVENT;
wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
wrmsrl(MSR_K7_PERFCTR0, -((u64)cpu_khz*1000) / nmi_hz);
wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
apic_write(APIC_LVTPC, APIC_DM_NMI);
evntsel |= K7_EVNTSEL_ENABLE;
wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
}
static int setup_p4_watchdog(void)
{
unsigned int misc_enable, dummy;
rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy);
if (!(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL))
return 0;
nmi_perfctr_msr = MSR_P4_IQ_COUNTER0;
nmi_p4_cccr_val = P4_NMI_IQ_CCCR0;
#ifdef CONFIG_SMP
if (smp_num_siblings == 2)
nmi_p4_cccr_val |= P4_CCCR_OVF_PMI1;
#endif
if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL))
clear_msr_range(0x3F1, 2);
/* MSR 0x3F0 seems to have a default value of 0xFC00, but current
docs doesn't fully define it, so leave it alone for now. */
if (boot_cpu_data.x86_model >= 0x3) {
/* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */
clear_msr_range(0x3A0, 26);
clear_msr_range(0x3BC, 3);
} else {
clear_msr_range(0x3A0, 31);
}
clear_msr_range(0x3C0, 6);
clear_msr_range(0x3C8, 6);
clear_msr_range(0x3E0, 2);
clear_msr_range(MSR_P4_CCCR0, 18);
clear_msr_range(MSR_P4_PERFCTR0, 18);
wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0);
wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0);
Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000));
wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1);
apic_write(APIC_LVTPC, APIC_DM_NMI);
wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0);
return 1;
}
void setup_apic_nmi_watchdog(void)
{
switch (boot_cpu_data.x86_vendor) {
@ -341,6 +425,13 @@ void setup_apic_nmi_watchdog(void)
return;
setup_k7_watchdog();
break;
case X86_VENDOR_INTEL:
if (boot_cpu_data.x86 != 15)
return;
if (!setup_p4_watchdog())
return;
break;
default:
return;
}
@ -355,56 +446,67 @@ void setup_apic_nmi_watchdog(void)
*
* as these watchdog NMI IRQs are generated on every CPU, we only
* have to check the current processor.
*
* since NMIs don't listen to _any_ locks, we have to be extremely
* careful not to rely on unsafe variables. The printk might lock
* up though, so we have to break up any console locks first ...
* [when there will be more tty-related locks, break them up
* here too!]
*/
static unsigned int
last_irq_sums [NR_CPUS],
alert_counter [NR_CPUS];
static DEFINE_PER_CPU(unsigned, last_irq_sum);
static DEFINE_PER_CPU(local_t, alert_counter);
static DEFINE_PER_CPU(int, nmi_touch);
void touch_nmi_watchdog (void)
{
int i;
/*
* Just reset the alert counters, (other CPUs might be
* spinning on locks we hold):
* Tell other CPUs to reset their alert counters. We cannot
* do it ourselves because the alert count increase is not
* atomic.
*/
for (i = 0; i < NR_CPUS; i++)
alert_counter[i] = 0;
per_cpu(nmi_touch, i) = 1;
}
void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
{
int sum, cpu;
int sum;
int touched = 0;
cpu = safe_smp_processor_id();
sum = read_pda(apic_timer_irqs);
if (last_irq_sums[cpu] == sum) {
if (__get_cpu_var(nmi_touch)) {
__get_cpu_var(nmi_touch) = 0;
touched = 1;
}
if (!touched && __get_cpu_var(last_irq_sum) == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
alert_counter[cpu]++;
if (alert_counter[cpu] == 5*nmi_hz) {
local_inc(&__get_cpu_var(alert_counter));
if (local_read(&__get_cpu_var(alert_counter)) == 5*nmi_hz) {
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
== NOTIFY_STOP) {
alert_counter[cpu] = 0;
local_set(&__get_cpu_var(alert_counter), 0);
return;
}
die_nmi("NMI Watchdog detected LOCKUP on CPU%d", regs);
}
} else {
last_irq_sums[cpu] = sum;
alert_counter[cpu] = 0;
__get_cpu_var(last_irq_sum) = sum;
local_set(&__get_cpu_var(alert_counter), 0);
}
if (nmi_perfctr_msr)
if (nmi_perfctr_msr) {
if (nmi_perfctr_msr == MSR_P4_IQ_COUNTER0) {
/*
* P4 quirks:
* - An overflown perfctr will assert its interrupt
* until the OVF flag in its CCCR is cleared.
* - LVTPC is masked on interrupt and must be
* unmasked by the LVTPC handler.
*/
wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0);
apic_write(APIC_LVTPC, APIC_DM_NMI);
}
wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
}
}
static int dummy_nmi_callback(struct pt_regs * regs, int cpu)

View File

@ -0,0 +1,101 @@
/* Ported over from i386 by AK, original copyright was:
*
* (C) Dominik Brodowski <linux@brodo.de> 2003
*
* Driver to use the Power Management Timer (PMTMR) available in some
* southbridges as primary timing source for the Linux kernel.
*
* Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
* timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
*
* This file is licensed under the GPL v2.
*
* Dropped all the hardware bug workarounds for now. Hopefully they
* are not needed on 64bit chipsets.
*/
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/cpumask.h>
#include <asm/io.h>
#include <asm/proto.h>
#include <asm/msr.h>
#include <asm/vsyscall.h>
/* The I/O port the PMTMR resides at.
* The location is detected during setup_arch(),
* in arch/i386/kernel/acpi/boot.c */
u32 pmtmr_ioport;
/* value of the Power timer at last timer interrupt */
static u32 offset_delay;
static u32 last_pmtmr_tick;
#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
static inline u32 cyc2us(u32 cycles)
{
/* The Power Management Timer ticks at 3.579545 ticks per microsecond.
* 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%]
*
* Even with HZ = 100, delta is at maximum 35796 ticks, so it can
* easily be multiplied with 286 (=0x11E) without having to fear
* u32 overflows.
*/
cycles *= 286;
return (cycles >> 10);
}
int pmtimer_mark_offset(void)
{
static int first_run = 1;
unsigned long tsc;
u32 lost;
u32 tick = inl(pmtmr_ioport);
u32 delta;
delta = cyc2us((tick - last_pmtmr_tick) & ACPI_PM_MASK);
last_pmtmr_tick = tick;
monotonic_base += delta * NSEC_PER_USEC;
delta += offset_delay;
lost = delta / (USEC_PER_SEC / HZ);
offset_delay = delta % (USEC_PER_SEC / HZ);
rdtscll(tsc);
vxtime.last_tsc = tsc - offset_delay * cpu_khz;
/* don't calculate delay for first run,
or if we've got less then a tick */
if (first_run || (lost < 1)) {
first_run = 0;
offset_delay = 0;
}
return lost - 1;
}
unsigned int do_gettimeoffset_pm(void)
{
u32 now, offset, delta = 0;
offset = last_pmtmr_tick;
now = inl(pmtmr_ioport);
delta = (now - offset) & ACPI_PM_MASK;
return offset_delay + cyc2us(delta);
}
static int __init nopmtimer_setup(char *s)
{
pmtmr_ioport = 0;
return 0;
}
__setup("nopmtimer", nopmtimer_setup);

View File

@ -257,13 +257,13 @@ static int putreg(struct task_struct *child,
value &= 0xffff;
return 0;
case offsetof(struct user_regs_struct,fs_base):
if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
return -EIO;
if (value >= TASK_SIZE)
return -EIO;
child->thread.fs = value;
return 0;
case offsetof(struct user_regs_struct,gs_base):
if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
return -EIO;
if (value >= TASK_SIZE)
return -EIO;
child->thread.gs = value;
return 0;
case offsetof(struct user_regs_struct, eflags):
@ -277,6 +277,11 @@ static int putreg(struct task_struct *child,
return -EIO;
value &= 0xffff;
break;
case offsetof(struct user_regs_struct, rip):
/* Check if the new RIP address is canonical */
if (value >= TASK_SIZE)
return -EIO;
break;
}
put_stack_long(child, regno - sizeof(struct pt_regs), value);
return 0;

View File

@ -727,11 +727,12 @@ static void __init display_cacheinfo(struct cpuinfo_x86 *c)
static void __init amd_detect_cmp(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_SMP
int cpu = c->x86_apicid;
int cpu = smp_processor_id();
int node = 0;
if (c->x86_num_cores == 1)
return;
cpu_core_id[cpu] = cpu >> hweight32(c->x86_num_cores - 1);
/* Fix up the APIC ID following the AMD specification. */
cpu_core_id[cpu] >>= hweight32(c->x86_num_cores - 1);
#ifdef CONFIG_NUMA
/* When an ACPI SRAT table is available use the mappings from SRAT
@ -745,6 +746,9 @@ static void __init amd_detect_cmp(struct cpuinfo_x86 *c)
node = cpu_to_node[cpu];
}
#endif
/* For now: - better than BAD_APIC_ID at least*/
phys_proc_id[cpu] = cpu_core_id[cpu];
printk(KERN_INFO "CPU %d(%d) -> Node %d -> Core %d\n",
cpu, c->x86_num_cores, node, cpu_core_id[cpu]);
#endif
@ -925,7 +929,6 @@ void __init early_identify_cpu(struct cpuinfo_x86 *c)
c->x86_clflush_size = 64;
c->x86_cache_alignment = c->x86_clflush_size;
c->x86_num_cores = 1;
c->x86_apicid = c == &boot_cpu_data ? 0 : c - cpu_data;
c->extended_cpuid_level = 0;
memset(&c->x86_capability, 0, sizeof c->x86_capability);
@ -954,11 +957,15 @@ void __init early_identify_cpu(struct cpuinfo_x86 *c)
}
if (c->x86_capability[0] & (1<<19))
c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
c->x86_apicid = misc >> 24;
} else {
/* Have CPUID level 0 only - unheard of */
c->x86 = 4;
}
#ifdef CONFIG_SMP
phys_proc_id[smp_processor_id()] =
cpu_core_id[smp_processor_id()] = (cpuid_ebx(1) >> 24) & 0xff;
#endif
}
/*
@ -1088,7 +1095,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* Other (Linux-defined) */
"cxmmx", NULL, "cyrix_arr", "centaur_mcr", "k8c+",
"cxmmx", NULL, "cyrix_arr", "centaur_mcr", NULL,
"constant_tsc", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,

View File

@ -56,6 +56,7 @@
#include <asm/kdebug.h>
#include <asm/tlbflush.h>
#include <asm/proto.h>
#include <asm/nmi.h>
/* Change for real CPU hotplug. Note other files need to be fixed
first too. */
@ -125,97 +126,211 @@ static void __cpuinit smp_store_cpu_info(int id)
*c = boot_cpu_data;
identify_cpu(c);
print_cpu_info(c);
}
/*
* Synchronize TSCs of CPUs
* New Funky TSC sync algorithm borrowed from IA64.
* Main advantage is that it doesn't reset the TSCs fully and
* in general looks more robust and it works better than my earlier
* attempts. I believe it was written by David Mosberger. Some minor
* adjustments for x86-64 by me -AK
*
* This new algorithm is less accurate than the old "zero TSCs"
* one, but we cannot zero TSCs anymore in the new hotplug CPU
* model.
* Original comment reproduced below.
*
* Synchronize TSC of the current (slave) CPU with the TSC of the
* MASTER CPU (normally the time-keeper CPU). We use a closed loop to
* eliminate the possibility of unaccounted-for errors (such as
* getting a machine check in the middle of a calibration step). The
* basic idea is for the slave to ask the master what itc value it has
* and to read its own itc before and after the master responds. Each
* iteration gives us three timestamps:
*
* slave master
*
* t0 ---\
* ---\
* --->
* tm
* /---
* /---
* t1 <---
*
*
* The goal is to adjust the slave's TSC such that tm falls exactly
* half-way between t0 and t1. If we achieve this, the clocks are
* synchronized provided the interconnect between the slave and the
* master is symmetric. Even if the interconnect were asymmetric, we
* would still know that the synchronization error is smaller than the
* roundtrip latency (t0 - t1).
*
* When the interconnect is quiet and symmetric, this lets us
* synchronize the TSC to within one or two cycles. However, we can
* only *guarantee* that the synchronization is accurate to within a
* round-trip time, which is typically in the range of several hundred
* cycles (e.g., ~500 cycles). In practice, this means that the TSCs
* are usually almost perfectly synchronized, but we shouldn't assume
* that the accuracy is much better than half a micro second or so.
*
* [there are other errors like the latency of RDTSC and of the
* WRMSR. These can also account to hundreds of cycles. So it's
* probably worse. It claims 153 cycles error on a dual Opteron,
* but I suspect the numbers are actually somewhat worse -AK]
*/
static atomic_t __cpuinitdata tsc_flag;
#define MASTER 0
#define SLAVE (SMP_CACHE_BYTES/8)
/* Intentionally don't use cpu_relax() while TSC synchronization
because we don't want to go into funky power save modi or cause
hypervisors to schedule us away. Going to sleep would likely affect
latency and low latency is the primary objective here. -AK */
#define no_cpu_relax() barrier()
static __cpuinitdata DEFINE_SPINLOCK(tsc_sync_lock);
static unsigned long long __cpuinitdata bp_tsc, ap_tsc;
static volatile __cpuinitdata unsigned long go[SLAVE + 1];
static int notscsync __cpuinitdata;
#define NR_LOOPS 5
#undef DEBUG_TSC_SYNC
static void __cpuinit sync_tsc_bp_init(int init)
#define NUM_ROUNDS 64 /* magic value */
#define NUM_ITERS 5 /* likewise */
/* Callback on boot CPU */
static __cpuinit void sync_master(void *arg)
{
if (init)
_raw_spin_lock(&tsc_sync_lock);
else
_raw_spin_unlock(&tsc_sync_lock);
atomic_set(&tsc_flag, 0);
}
unsigned long flags, i;
/*
* Synchronize TSC on AP with BP.
*/
static void __cpuinit __sync_tsc_ap(void)
{
if (!cpu_has_tsc)
return;
Dprintk("AP %d syncing TSC\n", smp_processor_id());
while (atomic_read(&tsc_flag) != 0)
cpu_relax();
atomic_inc(&tsc_flag);
mb();
_raw_spin_lock(&tsc_sync_lock);
wrmsrl(MSR_IA32_TSC, bp_tsc);
_raw_spin_unlock(&tsc_sync_lock);
rdtscll(ap_tsc);
mb();
atomic_inc(&tsc_flag);
mb();
}
static void __cpuinit sync_tsc_ap(void)
{
int i;
for (i = 0; i < NR_LOOPS; i++)
__sync_tsc_ap();
}
/*
* Synchronize TSC from BP to AP.
*/
static void __cpuinit __sync_tsc_bp(int cpu)
{
if (!cpu_has_tsc)
if (smp_processor_id() != boot_cpu_id)
return;
/* Wait for AP */
while (atomic_read(&tsc_flag) == 0)
cpu_relax();
/* Save BPs TSC */
sync_core();
rdtscll(bp_tsc);
/* Don't do the sync core here to avoid too much latency. */
mb();
/* Start the AP */
_raw_spin_unlock(&tsc_sync_lock);
/* Wait for AP again */
while (atomic_read(&tsc_flag) < 2)
cpu_relax();
rdtscl(bp_tsc);
barrier();
}
go[MASTER] = 0;
static void __cpuinit sync_tsc_bp(int cpu)
{
int i;
for (i = 0; i < NR_LOOPS - 1; i++) {
__sync_tsc_bp(cpu);
sync_tsc_bp_init(1);
local_irq_save(flags);
{
for (i = 0; i < NUM_ROUNDS*NUM_ITERS; ++i) {
while (!go[MASTER])
no_cpu_relax();
go[MASTER] = 0;
rdtscll(go[SLAVE]);
}
}
__sync_tsc_bp(cpu);
printk(KERN_INFO "Synced TSC of CPU %d difference %Ld\n",
cpu, ap_tsc - bp_tsc);
local_irq_restore(flags);
}
/*
* Return the number of cycles by which our tsc differs from the tsc
* on the master (time-keeper) CPU. A positive number indicates our
* tsc is ahead of the master, negative that it is behind.
*/
static inline long
get_delta(long *rt, long *master)
{
unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
unsigned long tcenter, t0, t1, tm;
int i;
for (i = 0; i < NUM_ITERS; ++i) {
rdtscll(t0);
go[MASTER] = 1;
while (!(tm = go[SLAVE]))
no_cpu_relax();
go[SLAVE] = 0;
rdtscll(t1);
if (t1 - t0 < best_t1 - best_t0)
best_t0 = t0, best_t1 = t1, best_tm = tm;
}
*rt = best_t1 - best_t0;
*master = best_tm - best_t0;
/* average best_t0 and best_t1 without overflow: */
tcenter = (best_t0/2 + best_t1/2);
if (best_t0 % 2 + best_t1 % 2 == 2)
++tcenter;
return tcenter - best_tm;
}
static __cpuinit void sync_tsc(void)
{
int i, done = 0;
long delta, adj, adjust_latency = 0;
unsigned long flags, rt, master_time_stamp, bound;
#if DEBUG_TSC_SYNC
static struct syncdebug {
long rt; /* roundtrip time */
long master; /* master's timestamp */
long diff; /* difference between midpoint and master's timestamp */
long lat; /* estimate of tsc adjustment latency */
} t[NUM_ROUNDS] __cpuinitdata;
#endif
go[MASTER] = 1;
smp_call_function(sync_master, NULL, 1, 0);
while (go[MASTER]) /* wait for master to be ready */
no_cpu_relax();
spin_lock_irqsave(&tsc_sync_lock, flags);
{
for (i = 0; i < NUM_ROUNDS; ++i) {
delta = get_delta(&rt, &master_time_stamp);
if (delta == 0) {
done = 1; /* let's lock on to this... */
bound = rt;
}
if (!done) {
unsigned long t;
if (i > 0) {
adjust_latency += -delta;
adj = -delta + adjust_latency/4;
} else
adj = -delta;
rdtscll(t);
wrmsrl(MSR_IA32_TSC, t + adj);
}
#if DEBUG_TSC_SYNC
t[i].rt = rt;
t[i].master = master_time_stamp;
t[i].diff = delta;
t[i].lat = adjust_latency/4;
#endif
}
}
spin_unlock_irqrestore(&tsc_sync_lock, flags);
#if DEBUG_TSC_SYNC
for (i = 0; i < NUM_ROUNDS; ++i)
printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
t[i].rt, t[i].master, t[i].diff, t[i].lat);
#endif
printk(KERN_INFO
"CPU %d: synchronized TSC with CPU %u (last diff %ld cycles, "
"maxerr %lu cycles)\n",
smp_processor_id(), boot_cpu_id, delta, rt);
}
static void __cpuinit tsc_sync_wait(void)
{
if (notscsync || !cpu_has_tsc)
return;
printk(KERN_INFO "CPU %d: Syncing TSC to CPU %u.\n", smp_processor_id(),
boot_cpu_id);
sync_tsc();
}
static __init int notscsync_setup(char *s)
{
notscsync = 1;
return 0;
}
__setup("notscsync", notscsync_setup);
static atomic_t init_deasserted __cpuinitdata;
/*
@ -315,11 +430,6 @@ void __cpuinit start_secondary(void)
cpu_init();
smp_callin();
/*
* Synchronize the TSC with the BP
*/
sync_tsc_ap();
/* otherwise gcc will move up the smp_processor_id before the cpu_init */
barrier();
@ -334,7 +444,6 @@ void __cpuinit start_secondary(void)
enable_8259A_irq(0);
}
enable_APIC_timer();
/*
@ -343,6 +452,11 @@ void __cpuinit start_secondary(void)
cpu_set(smp_processor_id(), cpu_online_map);
mb();
/* Wait for TSC sync to not schedule things before.
We still process interrupts, which could see an inconsistent
time in that window unfortunately. */
tsc_sync_wait();
cpu_idle();
}
@ -531,7 +645,6 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid)
printk("failed fork for CPU %d\n", cpu);
return PTR_ERR(idle);
}
x86_cpu_to_apicid[cpu] = apicid;
cpu_pda[cpu].pcurrent = idle;
@ -600,8 +713,6 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid)
if (cpu_isset(cpu, cpu_callin_map)) {
/* number CPUs logically, starting from 1 (BSP is 0) */
Dprintk("OK.\n");
print_cpu_info(&cpu_data[cpu]);
Dprintk("CPU has booted.\n");
} else {
boot_error = 1;
@ -842,7 +953,6 @@ void __cpuinit smp_prepare_cpus(unsigned int max_cpus)
GET_APIC_ID(apic_read(APIC_ID)), boot_cpu_id);
/* Or can we switch back to PIC here? */
}
x86_cpu_to_apicid[0] = boot_cpu_id;
/*
* Now start the IO-APICs
@ -889,18 +999,14 @@ int __cpuinit __cpu_up(unsigned int cpu)
printk("__cpu_up: bad cpu %d\n", cpu);
return -EINVAL;
}
sync_tsc_bp_init(1);
/* Boot it! */
err = do_boot_cpu(cpu, apicid);
if (err < 0) {
sync_tsc_bp_init(0);
Dprintk("do_boot_cpu failed %d\n", err);
return err;
}
sync_tsc_bp(cpu);
/* Unleash the CPU! */
Dprintk("waiting for cpu %d\n", cpu);
@ -923,4 +1029,6 @@ void __cpuinit smp_cpus_done(unsigned int max_cpus)
detect_siblings();
time_init_gtod();
check_nmi_watchdog();
}

View File

@ -26,6 +26,8 @@
#include <linux/sysdev.h>
#include <linux/bcd.h>
#include <linux/kallsyms.h>
#include <linux/acpi.h>
#include <acpi/achware.h> /* for PM timer frequency */
#include <asm/8253pit.h>
#include <asm/pgtable.h>
#include <asm/vsyscall.h>
@ -396,6 +398,10 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
(offset - vxtime.last)*(NSEC_PER_SEC/HZ) / hpet_tick;
vxtime.last = offset;
#ifdef CONFIG_X86_PM_TIMER
} else if (vxtime.mode == VXTIME_PMTMR) {
lost = pmtimer_mark_offset();
#endif
} else {
offset = (((tsc - vxtime.last_tsc) *
vxtime.tsc_quot) >> 32) - (USEC_PER_SEC / HZ);
@ -898,6 +904,13 @@ void __init time_init(void)
hpet_period;
cpu_khz = hpet_calibrate_tsc();
timename = "HPET";
#ifdef CONFIG_X86_PM_TIMER
} else if (pmtmr_ioport) {
vxtime_hz = PM_TIMER_FREQUENCY;
timename = "PM";
pit_init();
cpu_khz = pit_calibrate_tsc();
#endif
} else {
pit_init();
cpu_khz = pit_calibrate_tsc();
@ -922,6 +935,27 @@ void __init time_init(void)
#endif
}
/*
* Make an educated guess if the TSC is trustworthy and synchronized
* over all CPUs.
*/
static __init int unsynchronized_tsc(void)
{
#ifdef CONFIG_SMP
if (oem_force_hpet_timer())
return 1;
/* Intel systems are normally all synchronized. Exceptions
are handled in the OEM check above. */
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
return 0;
/* All in a single socket - should be synchronized */
if (cpus_weight(cpu_core_map[0]) == num_online_cpus())
return 0;
#endif
/* Assume multi socket systems are not synchronized */
return num_online_cpus() > 1;
}
/*
* Decide after all CPUs are booted what mode gettimeofday should use.
*/
@ -929,29 +963,23 @@ void __init time_init_gtod(void)
{
char *timetype;
/*
* AMD systems with more than one CPU don't have fully synchronized
* TSCs. Always use HPET gettimeofday for these, although it is slower.
* Intel SMP systems usually have synchronized TSCs, so use always
* the TSC.
*
* Exceptions:
* IBM Summit2 checked by oem_force_hpet_timer().
* AMD dual core may also not need HPET. Check me.
*
* Can be turned off with "notsc".
*/
if (num_online_cpus() > 1 &&
boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
notsc = 1;
/* Some systems will want to disable TSC and use HPET. */
if (oem_force_hpet_timer())
if (unsynchronized_tsc())
notsc = 1;
if (vxtime.hpet_address && notsc) {
timetype = "HPET";
vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
vxtime.mode = VXTIME_HPET;
do_gettimeoffset = do_gettimeoffset_hpet;
#ifdef CONFIG_X86_PM_TIMER
/* Using PM for gettimeofday is quite slow, but we have no other
choice because the TSC is too unreliable on some systems. */
} else if (pmtmr_ioport && !vxtime.hpet_address && notsc) {
timetype = "PM";
do_gettimeoffset = do_gettimeoffset_pm;
vxtime.mode = VXTIME_PMTMR;
sysctl_vsyscall = 0;
printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n");
#endif
} else {
timetype = vxtime.hpet_address ? "HPET/TSC" : "PIT/TSC";
vxtime.mode = VXTIME_TSC;

View File

@ -65,7 +65,7 @@ static force_inline void do_vgettimeofday(struct timeval * tv)
usec = (__xtime.tv_nsec / 1000) +
(__jiffies - __wall_jiffies) * (1000000 / HZ);
if (__vxtime.mode == VXTIME_TSC) {
if (__vxtime.mode != VXTIME_HPET) {
sync_core();
rdtscll(t);
if (t < __vxtime.last_tsc)
@ -217,8 +217,9 @@ static int __init vsyscall_init(void)
BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
map_vsyscall();
sysctl_vsyscall = 1;
#ifdef CONFIG_SYSCTL
register_sysctl_table(kernel_root_table2, 0);
#endif
return 0;
}

View File

@ -234,6 +234,8 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
/*
* Handle a fault on the vmalloc or module mapping area
*
* This assumes no large pages in there.
*/
static int vmalloc_fault(unsigned long address)
{
@ -272,7 +274,10 @@ static int vmalloc_fault(unsigned long address)
if (!pte_present(*pte_ref))
return -1;
pte = pte_offset_kernel(pmd, address);
if (!pte_present(*pte) || pte_page(*pte) != pte_page(*pte_ref))
/* Don't use pte_page here, because the mappings can point
outside mem_map, and the NUMA hash lookup cannot handle
that. */
if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref))
BUG();
__flush_tlb_all();
return 0;
@ -346,7 +351,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
* protection error (error_code & 1) == 0.
*/
if (unlikely(address >= TASK_SIZE)) {
if (!(error_code & 5)) {
if (!(error_code & 5) &&
((address >= VMALLOC_START && address < VMALLOC_END) ||
(address >= MODULES_VADDR && address < MODULES_END))) {
if (vmalloc_fault(address) < 0)
goto bad_area_nosemaphore;
return;

View File

@ -272,7 +272,7 @@ void iounmap(volatile void __iomem *addr)
if ((p->flags >> 20) &&
p->phys_addr + p->size - 1 < virt_to_phys(high_memory)) {
/* p->size includes the guard page, but cpa doesn't like that */
change_page_attr(virt_to_page(__va(p->phys_addr)),
change_page_attr_addr((unsigned long)__va(p->phys_addr),
p->size >> PAGE_SHIFT,
PAGE_KERNEL);
global_flush_tlb();

View File

@ -21,6 +21,7 @@
#include <linux/mm.h>
#include <asm/scatterlist.h>
#include <linux/crypto.h>
#include <linux/string.h>
#define NULL_KEY_SIZE 0
#define NULL_BLOCK_SIZE 1
@ -28,11 +29,13 @@
static int null_compress(void *ctx, const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen)
{ return 0; }
static int null_decompress(void *ctx, const u8 *src, unsigned int slen,
u8 *dst, unsigned int *dlen)
{ return 0; }
{
if (slen > *dlen)
return -EINVAL;
memcpy(dst, src, slen);
*dlen = slen;
return 0;
}
static void null_init(void *ctx)
{ }
@ -47,11 +50,10 @@ static int null_setkey(void *ctx, const u8 *key,
unsigned int keylen, u32 *flags)
{ return 0; }
static void null_encrypt(void *ctx, u8 *dst, const u8 *src)
{ }
static void null_decrypt(void *ctx, u8 *dst, const u8 *src)
{ }
static void null_crypt(void *ctx, u8 *dst, const u8 *src)
{
memcpy(dst, src, NULL_BLOCK_SIZE);
}
static struct crypto_alg compress_null = {
.cra_name = "compress_null",
@ -62,7 +64,7 @@ static struct crypto_alg compress_null = {
.cra_list = LIST_HEAD_INIT(compress_null.cra_list),
.cra_u = { .compress = {
.coa_compress = null_compress,
.coa_decompress = null_decompress } }
.coa_decompress = null_compress } }
};
static struct crypto_alg digest_null = {
@ -90,8 +92,8 @@ static struct crypto_alg cipher_null = {
.cia_min_keysize = NULL_KEY_SIZE,
.cia_max_keysize = NULL_KEY_SIZE,
.cia_setkey = null_setkey,
.cia_encrypt = null_encrypt,
.cia_decrypt = null_decrypt } }
.cia_encrypt = null_crypt,
.cia_decrypt = null_crypt } }
};
MODULE_ALIAS("compress_null");

View File

@ -1,6 +1,6 @@
# Makefile for the Linux device tree
obj-y := core.o sys.o interface.o bus.o \
obj-y := core.o sys.o bus.o \
driver.o class.o class_simple.o platform.o \
cpu.o firmware.o init.o map.o dmapool.o \
attribute_container.o transport_class.o

View File

@ -390,7 +390,6 @@ void device_release_driver(struct device * dev)
sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
sysfs_remove_link(&dev->kobj, "driver");
list_del_init(&dev->driver_list);
device_detach_shutdown(dev);
if (drv->remove)
drv->remove(dev);
dev->driver = NULL;

View File

@ -31,8 +31,6 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
extern struct attribute * dev_default_attrs[];
static ssize_t
dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
@ -89,7 +87,6 @@ static void device_release(struct kobject * kobj)
static struct kobj_type ktype_device = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.default_attrs = dev_default_attrs,
};

View File

@ -1,51 +0,0 @@
/*
* drivers/base/interface.c - common driverfs interface that's exported to
* the world for all devices.
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
*
* This file is released under the GPLv2
*
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/stat.h>
#include <linux/string.h>
/**
* detach_state - control the default power state for the device.
*
* This is the state the device enters when it's driver module is
* unloaded. The value is an unsigned integer, in the range of 0-4.
* '0' indicates 'On', so no action will be taken when the driver is
* unloaded. This is the default behavior.
* '4' indicates 'Off', meaning the driver core will call the driver's
* shutdown method to quiesce the device.
* 1-3 indicate a low-power state for the device to enter via the
* driver's suspend method.
*/
static ssize_t detach_show(struct device * dev, char * buf)
{
return sprintf(buf, "%u\n", dev->detach_state);
}
static ssize_t detach_store(struct device * dev, const char * buf, size_t n)
{
u32 state;
state = simple_strtoul(buf, NULL, 10);
if (state > 4)
return -EINVAL;
dev->detach_state = state;
return n;
}
static DEVICE_ATTR(detach_state, 0644, detach_show, detach_store);
struct attribute * dev_default_attrs[] = {
&dev_attr_detach_state.attr,
NULL,
};

View File

@ -1,18 +1,7 @@
enum {
DEVICE_PM_ON,
DEVICE_PM1,
DEVICE_PM2,
DEVICE_PM3,
DEVICE_PM_OFF,
};
/*
* shutdown.c
*/
extern int device_detach_shutdown(struct device *);
extern void device_shutdown(void);

View File

@ -22,8 +22,17 @@ extern int sysdev_resume(void);
int resume_device(struct device * dev)
{
if (dev->bus && dev->bus->resume)
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state) {
dev_err(dev, "PM: resume from %d, parent %s still %d\n",
dev->power.power_state,
dev->power.pm_parent->bus_id,
dev->power.pm_parent->power.power_state);
}
if (dev->bus && dev->bus->resume) {
dev_dbg(dev,"resuming\n");
return dev->bus->resume(dev);
}
return 0;
}

View File

@ -19,20 +19,6 @@
extern struct subsystem devices_subsys;
int device_detach_shutdown(struct device * dev)
{
if (!dev->detach_state)
return 0;
if (dev->detach_state == DEVICE_PM_OFF) {
if (dev->driver && dev->driver->shutdown)
dev->driver->shutdown(dev);
return 0;
}
return dpm_runtime_suspend(dev, dev->detach_state);
}
/**
* We handle system devices differently - we suspend and shut them
* down last and resume them first. That way, we don't do anything stupid like
@ -52,13 +38,12 @@ void device_shutdown(void)
struct device * dev;
down_write(&devices_subsys.rwsem);
list_for_each_entry_reverse(dev, &devices_subsys.kset.list, kobj.entry) {
pr_debug("shutting down %s: ", dev->bus_id);
list_for_each_entry_reverse(dev, &devices_subsys.kset.list,
kobj.entry) {
if (dev->driver && dev->driver->shutdown) {
pr_debug("Ok\n");
dev_dbg(dev, "shutdown\n");
dev->driver->shutdown(dev);
} else
pr_debug("Ignored.\n");
}
}
up_write(&devices_subsys.rwsem);

View File

@ -39,12 +39,25 @@ int suspend_device(struct device * dev, pm_message_t state)
{
int error = 0;
dev_dbg(dev, "suspending\n");
if (dev->power.power_state) {
dev_dbg(dev, "PM: suspend %d-->%d\n",
dev->power.power_state, state);
}
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state) {
dev_err(dev,
"PM: suspend %d->%d, parent %s already %d\n",
dev->power.power_state, state,
dev->power.pm_parent->bus_id,
dev->power.pm_parent->power.power_state);
}
dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state)
if (dev->bus && dev->bus->suspend && !dev->power.power_state) {
dev_dbg(dev, "suspending\n");
error = dev->bus->suspend(dev, state);
}
return error;
}

View File

@ -914,8 +914,10 @@ static int pkt_handle_queue(struct pktcdvd_device *pd)
bio = node->bio;
zone = ZONE(bio->bi_sector, pd);
list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
if (p->sector == zone)
if (p->sector == zone) {
bio = NULL;
goto try_next_bio;
}
}
break;
try_next_bio:

View File

@ -122,7 +122,7 @@ raw_ioctl(struct inode *inode, struct file *filp,
{
struct block_device *bdev = filp->private_data;
return blkdev_ioctl(bdev->bd_inode, filp, command, arg);
return blkdev_ioctl(bdev->bd_inode, NULL, command, arg);
}
static void bind_device(struct raw_config_request *rq)

View File

@ -382,6 +382,7 @@ static struct pci_device_id i8xx_tco_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl);

View File

@ -516,6 +516,6 @@ void proc_ide_create(void)
void proc_ide_destroy(void)
{
remove_proc_entry("ide/drivers", proc_ide_root);
remove_proc_entry("drivers", proc_ide_root);
remove_proc_entry("ide", NULL);
}

View File

@ -84,11 +84,6 @@ config IEEE1394_PCILYNX
To compile this driver as a module, say M here: the
module will be called pcilynx.
# Non-maintained pcilynx options
# if [ "$CONFIG_IEEE1394_PCILYNX" != "n" ]; then
# bool ' Use PCILynx local RAM' CONFIG_IEEE1394_PCILYNX_LOCALRAM
# bool ' Support for non-IEEE1394 local ports' CONFIG_IEEE1394_PCILYNX_PORTS
# fi
config IEEE1394_OHCI1394
tristate "OHCI-1394 support"
depends on PCI && IEEE1394

View File

@ -520,7 +520,7 @@ int hpsb_send_packet(struct hpsb_packet *packet)
if (!packet->no_waiter || packet->expect_response) {
atomic_inc(&packet->refcnt);
packet->sendtime = jiffies;
packet->sendtime = jiffies + 10 * HZ;
skb_queue_tail(&host->pending_packet_queue, packet->skb);
}
@ -1248,7 +1248,6 @@ EXPORT_SYMBOL(hpsb_make_phypacket);
EXPORT_SYMBOL(hpsb_make_isopacket);
EXPORT_SYMBOL(hpsb_read);
EXPORT_SYMBOL(hpsb_write);
EXPORT_SYMBOL(hpsb_lock);
EXPORT_SYMBOL(hpsb_packet_success);
/** highlevel.c **/

View File

@ -535,6 +535,7 @@ hpsb_write_fail:
return retval;
}
#if 0
int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, int extcode, quadlet_t *data, quadlet_t arg)
@ -599,3 +600,5 @@ int hpsb_send_gasp(struct hpsb_host *host, int channel, unsigned int generation,
return retval;
}
#endif /* 0 */

View File

@ -53,12 +53,5 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, quadlet_t *buffer, size_t length);
int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, quadlet_t *buffer, size_t length);
int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, int extcode, quadlet_t *data, quadlet_t arg);
int hpsb_lock64(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, int extcode, octlet_t *data, octlet_t arg);
int hpsb_send_gasp(struct hpsb_host *host, int channel, unsigned int generation,
quadlet_t *buffer, size_t length, u32 specifier_id,
unsigned int version);
#endif /* _IEEE1394_TRANSACTIONS_H */

View File

@ -1005,8 +1005,7 @@ static struct unit_directory *nodemgr_process_unit_directory
return ud;
unit_directory_error:
if (ud != NULL)
kfree(ud);
kfree(ud);
return NULL;
}

View File

@ -2931,7 +2931,7 @@ static void free_dma_rcv_ctx(struct dma_rcv_ctx *d)
kfree(d->prg_cpu);
kfree(d->prg_bus);
}
if (d->spb) kfree(d->spb);
kfree(d->spb);
/* Mark this context as freed. */
d->ohci = NULL;

View File

@ -236,6 +236,9 @@ struct ti_ohci {
static inline int cross_bound(unsigned long addr, unsigned int size)
{
if (size == 0)
return 0;
if (size > PAGE_SIZE)
return 1;

View File

@ -43,6 +43,7 @@
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/kdev_t.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include <asm/io.h>
@ -834,327 +835,6 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
* IEEE-1394 functionality section END *
***************************************/
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
/* VFS functions for local bus / aux device access. Access to those
* is implemented as a character device instead of block devices
* because buffers are not wanted for this. Therefore llseek (from
* VFS) can be used for these char devices with obvious effects.
*/
static int mem_open(struct inode*, struct file*);
static int mem_release(struct inode*, struct file*);
static unsigned int aux_poll(struct file*, struct poll_table_struct*);
static loff_t mem_llseek(struct file*, loff_t, int);
static ssize_t mem_read (struct file*, char*, size_t, loff_t*);
static ssize_t mem_write(struct file*, const char*, size_t, loff_t*);
static struct file_operations aux_ops = {
.owner = THIS_MODULE,
.read = mem_read,
.write = mem_write,
.poll = aux_poll,
.llseek = mem_llseek,
.open = mem_open,
.release = mem_release,
};
static void aux_setup_pcls(struct ti_lynx *lynx)
{
struct ti_pcl pcl;
pcl.next = PCL_NEXT_INVALID;
pcl.user_data = pcl_bus(lynx, lynx->dmem_pcl);
put_pcl(lynx, lynx->dmem_pcl, &pcl);
}
static int mem_open(struct inode *inode, struct file *file)
{
int cid = iminor(inode);
enum { t_rom, t_aux, t_ram } type;
struct memdata *md;
if (cid < PCILYNX_MINOR_AUX_START) {
/* just for completeness */
return -ENXIO;
} else if (cid < PCILYNX_MINOR_ROM_START) {
cid -= PCILYNX_MINOR_AUX_START;
if (cid >= num_of_cards || !cards[cid].aux_port)
return -ENXIO;
type = t_aux;
} else if (cid < PCILYNX_MINOR_RAM_START) {
cid -= PCILYNX_MINOR_ROM_START;
if (cid >= num_of_cards || !cards[cid].local_rom)
return -ENXIO;
type = t_rom;
} else {
/* WARNING: Know what you are doing when opening RAM.
* It is currently used inside the driver! */
cid -= PCILYNX_MINOR_RAM_START;
if (cid >= num_of_cards || !cards[cid].local_ram)
return -ENXIO;
type = t_ram;
}
md = (struct memdata *)kmalloc(sizeof(struct memdata), SLAB_KERNEL);
if (md == NULL)
return -ENOMEM;
md->lynx = &cards[cid];
md->cid = cid;
switch (type) {
case t_rom:
md->type = rom;
break;
case t_ram:
md->type = ram;
break;
case t_aux:
atomic_set(&md->aux_intr_last_seen,
atomic_read(&cards[cid].aux_intr_seen));
md->type = aux;
break;
}
file->private_data = md;
return 0;
}
static int mem_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
static unsigned int aux_poll(struct file *file, poll_table *pt)
{
struct memdata *md = (struct memdata *)file->private_data;
int cid = md->cid;
unsigned int mask;
/* reading and writing is always allowed */
mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
if (md->type == aux) {
poll_wait(file, &cards[cid].aux_intr_wait, pt);
if (atomic_read(&md->aux_intr_last_seen)
!= atomic_read(&cards[cid].aux_intr_seen)) {
mask |= POLLPRI;
atomic_inc(&md->aux_intr_last_seen);
}
}
return mask;
}
loff_t mem_llseek(struct file *file, loff_t offs, int orig)
{
loff_t newoffs;
switch (orig) {
case 0:
newoffs = offs;
break;
case 1:
newoffs = offs + file->f_pos;
break;
case 2:
newoffs = PCILYNX_MAX_MEMORY + 1 + offs;
break;
default:
return -EINVAL;
}
if (newoffs < 0 || newoffs > PCILYNX_MAX_MEMORY + 1) return -EINVAL;
file->f_pos = newoffs;
return newoffs;
}
/*
* do not DMA if count is too small because this will have a serious impact
* on performance - the value 2400 was found by experiment and may not work
* everywhere as good as here - use mem_mindma option for modules to change
*/
static short mem_mindma = 2400;
module_param(mem_mindma, short, 0444);
MODULE_PARM_DESC(mem_mindma, "Minimum amount of data required to use DMA");
static ssize_t mem_dmaread(struct memdata *md, u32 physbuf, ssize_t count,
int offset)
{
pcltmp_t pcltmp;
struct ti_pcl *pcl;
size_t retval;
int i;
DECLARE_WAITQUEUE(wait, current);
count &= ~3;
count = min(count, 53196);
retval = count;
if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS))
& DMA_CHAN_CTRL_BUSY) {
PRINT(KERN_WARNING, md->lynx->id, "DMA ALREADY ACTIVE!");
}
reg_write(md->lynx, LBUS_ADDR, md->type | offset);
pcl = edit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp);
pcl->buffer[0].control = PCL_CMD_LBUS_TO_PCI | min(count, 4092);
pcl->buffer[0].pointer = physbuf;
count -= 4092;
i = 0;
while (count > 0) {
i++;
pcl->buffer[i].control = min(count, 4092);
pcl->buffer[i].pointer = physbuf + i * 4092;
count -= 4092;
}
pcl->buffer[i].control |= PCL_LAST_BUFF;
commit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&md->lynx->mem_dma_intr_wait, &wait);
run_sub_pcl(md->lynx, md->lynx->dmem_pcl, 2, CHANNEL_LOCALBUS);
schedule();
while (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS))
& DMA_CHAN_CTRL_BUSY) {
if (signal_pending(current)) {
retval = -EINTR;
break;
}
schedule();
}
reg_write(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS), 0);
remove_wait_queue(&md->lynx->mem_dma_intr_wait, &wait);
if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS))
& DMA_CHAN_CTRL_BUSY) {
PRINT(KERN_ERR, md->lynx->id, "DMA STILL ACTIVE!");
}
return retval;
}
static ssize_t mem_read(struct file *file, char *buffer, size_t count,
loff_t *offset)
{
struct memdata *md = (struct memdata *)file->private_data;
ssize_t bcount;
size_t alignfix;
loff_t off = *offset; /* avoid useless 64bit-arithmetic */
ssize_t retval;
void *membase;
if ((off + count) > PCILYNX_MAX_MEMORY+1) {
count = PCILYNX_MAX_MEMORY+1 - off;
}
if (count == 0 || off > PCILYNX_MAX_MEMORY) {
return -ENOSPC;
}
switch (md->type) {
case rom:
membase = md->lynx->local_rom;
break;
case ram:
membase = md->lynx->local_ram;
break;
case aux:
membase = md->lynx->aux_port;
break;
default:
panic("pcilynx%d: unsupported md->type %d in %s",
md->lynx->id, md->type, __FUNCTION__);
}
down(&md->lynx->mem_dma_mutex);
if (count < mem_mindma) {
memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, count);
goto out;
}
bcount = count;
alignfix = 4 - (off % 4);
if (alignfix != 4) {
if (bcount < alignfix) {
alignfix = bcount;
}
memcpy_fromio(md->lynx->mem_dma_buffer, membase+off,
alignfix);
if (bcount == alignfix) {
goto out;
}
bcount -= alignfix;
off += alignfix;
}
while (bcount >= 4) {
retval = mem_dmaread(md, md->lynx->mem_dma_buffer_dma
+ count - bcount, bcount, off);
if (retval < 0) return retval;
bcount -= retval;
off += retval;
}
if (bcount) {
memcpy_fromio(md->lynx->mem_dma_buffer + count - bcount,
membase+off, bcount);
}
out:
retval = copy_to_user(buffer, md->lynx->mem_dma_buffer, count);
up(&md->lynx->mem_dma_mutex);
if (retval) return -EFAULT;
*offset += count;
return count;
}
static ssize_t mem_write(struct file *file, const char *buffer, size_t count,
loff_t *offset)
{
struct memdata *md = (struct memdata *)file->private_data;
if (((*offset) + count) > PCILYNX_MAX_MEMORY+1) {
count = PCILYNX_MAX_MEMORY+1 - *offset;
}
if (count == 0 || *offset > PCILYNX_MAX_MEMORY) {
return -ENOSPC;
}
/* FIXME: dereferencing pointers to PCI mem doesn't work everywhere */
switch (md->type) {
case aux:
if (copy_from_user(md->lynx->aux_port+(*offset), buffer, count))
return -EFAULT;
break;
case ram:
if (copy_from_user(md->lynx->local_ram+(*offset), buffer, count))
return -EFAULT;
break;
case rom:
/* the ROM may be writeable */
if (copy_from_user(md->lynx->local_rom+(*offset), buffer, count))
return -EFAULT;
break;
}
file->f_pos += count;
return count;
}
#endif /* CONFIG_IEEE1394_PCILYNX_PORTS */
/********************************************************
* Global stuff (interrupt handler, init/shutdown code) *
@ -1181,18 +861,6 @@ static irqreturn_t lynx_irq_handler(int irq, void *dev_id,
reg_write(lynx, LINK_INT_STATUS, linkint);
reg_write(lynx, PCI_INT_STATUS, intmask);
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
if (intmask & PCI_INT_AUX_INT) {
atomic_inc(&lynx->aux_intr_seen);
wake_up_interruptible(&lynx->aux_intr_wait);
}
if (intmask & PCI_INT_DMA_HLT(CHANNEL_LOCALBUS)) {
wake_up_interruptible(&lynx->mem_dma_intr_wait);
}
#endif
if (intmask & PCI_INT_1394) {
if (linkint & LINK_INT_PHY_TIMEOUT) {
PRINT(KERN_INFO, lynx->id, "PHY timeout occurred");
@ -1484,15 +1152,9 @@ static void remove_card(struct pci_dev *dev)
pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
lynx->rcv_page_dma);
case have_aux_buf:
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer,
lynx->mem_dma_buffer_dma);
#endif
case have_pcl_mem:
#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
lynx->pcl_mem_dma);
#endif
case clear:
/* do nothing - already freed */
;
@ -1524,7 +1186,7 @@ static int __devinit add_card(struct pci_dev *dev,
error = -ENXIO;
if (pci_set_dma_mask(dev, 0xffffffff))
if (pci_set_dma_mask(dev, DMA_32BIT_MASK))
FAIL("DMA address limits not supported for PCILynx hardware");
if (pci_enable_device(dev))
FAIL("failed to enable PCILynx hardware");
@ -1546,7 +1208,6 @@ static int __devinit add_card(struct pci_dev *dev,
spin_lock_init(&lynx->lock);
spin_lock_init(&lynx->phy_reg_lock);
#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE,
&lynx->pcl_mem_dma);
@ -1558,16 +1219,6 @@ static int __devinit add_card(struct pci_dev *dev,
} else {
FAIL("failed to allocate PCL memory area");
}
#endif
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
lynx->mem_dma_buffer = pci_alloc_consistent(dev, 65536,
&lynx->mem_dma_buffer_dma);
if (lynx->mem_dma_buffer == NULL) {
FAIL("failed to allocate DMA buffer for aux");
}
lynx->state = have_aux_buf;
#endif
lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE,
&lynx->rcv_page_dma);
@ -1597,13 +1248,6 @@ static int __devinit add_card(struct pci_dev *dev,
FAIL("failed to remap registers - card not accessible");
}
#ifdef CONFIG_IEEE1394_PCILYNX_LOCALRAM
if (lynx->local_ram == NULL) {
FAIL("failed to remap local RAM which is required for "
"operation");
}
#endif
reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
/* Fix buggy cards with autoboot pin not tied low: */
reg_write(lynx, DMA0_CHAN_CTRL, 0);
@ -1624,13 +1268,6 @@ static int __devinit add_card(struct pci_dev *dev,
/* alloc_pcl return values are not checked, it is expected that the
* provided PCL space is sufficient for the initial allocations */
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
if (lynx->aux_port != NULL) {
lynx->dmem_pcl = alloc_pcl(lynx);
aux_setup_pcls(lynx);
sema_init(&lynx->mem_dma_mutex, 1);
}
#endif
lynx->rcv_pcl = alloc_pcl(lynx);
lynx->rcv_pcl_start = alloc_pcl(lynx);
lynx->async.pcl = alloc_pcl(lynx);
@ -1647,12 +1284,6 @@ static int __devinit add_card(struct pci_dev *dev,
reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_AUX_INT);
init_waitqueue_head(&lynx->mem_dma_intr_wait);
init_waitqueue_head(&lynx->aux_intr_wait);
#endif
tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh,
(unsigned long)lynx);
@ -1944,37 +1575,18 @@ static int __init pcilynx_init(void)
{
int ret;
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
PRINT_G(KERN_ERR, "allocation of char major number %d failed",
PCILYNX_MAJOR);
return -EBUSY;
}
#endif
ret = pci_register_driver(&lynx_pci_driver);
if (ret < 0) {
PRINT_G(KERN_ERR, "PCI module init failed");
goto free_char_dev;
return ret;
}
return 0;
free_char_dev:
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
#endif
return ret;
}
static void __exit pcilynx_cleanup(void)
{
pci_unregister_driver(&lynx_pci_driver);
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
#endif
}

View File

@ -55,16 +55,6 @@ struct ti_lynx {
void __iomem *aux_port;
quadlet_t bus_info_block[5];
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
atomic_t aux_intr_seen;
wait_queue_head_t aux_intr_wait;
void *mem_dma_buffer;
dma_addr_t mem_dma_buffer_dma;
struct semaphore mem_dma_mutex;
wait_queue_head_t mem_dma_intr_wait;
#endif
/*
* use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for
* LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes);
@ -72,11 +62,9 @@ struct ti_lynx {
*/
u8 pcl_bmap[LOCALRAM_SIZE / 1024];
#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
/* point to PCLs memory area if needed */
void *pcl_mem;
dma_addr_t pcl_mem_dma;
#endif
/* PCLs for local mem / aux transfers */
pcl_t dmem_pcl;
@ -378,39 +366,6 @@ struct ti_pcl {
#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER))
#ifdef CONFIG_IEEE1394_PCILYNX_LOCALRAM
static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid,
const struct ti_pcl *pcl)
{
int i;
u32 *in = (u32 *)pcl;
u32 *out = (u32 *)(lynx->local_ram + pclid * sizeof(struct ti_pcl));
for (i = 0; i < 32; i++, out++, in++) {
writel(*in, out);
}
}
static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid,
struct ti_pcl *pcl)
{
int i;
u32 *out = (u32 *)pcl;
u32 *in = (u32 *)(lynx->local_ram + pclid * sizeof(struct ti_pcl));
for (i = 0; i < 32; i++, out++, in++) {
*out = readl(in);
}
}
static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid)
{
return pci_resource_start(lynx->dev, 1) + pclid * sizeof(struct ti_pcl);
}
#else /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */
static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid,
const struct ti_pcl *pcl)
{
@ -431,10 +386,8 @@ static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid)
return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl);
}
#endif /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */
#if defined (CONFIG_IEEE1394_PCILYNX_LOCALRAM) || defined (__BIG_ENDIAN)
#if defined (__BIG_ENDIAN)
typedef struct ti_pcl pcltmp_t;
static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid,

View File

@ -35,6 +35,11 @@
*
*/
/* Markus Tavenrath <speedygoo@speedygoo.de> :
- fixed checks for valid buffer-numbers in video1394_icotl
- changed the ways the dma prg's are used, now it's possible to use
even a single dma buffer
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/list.h>
@ -112,6 +117,7 @@ struct dma_iso_ctx {
struct it_dma_prg **it_prg;
unsigned int *buffer_status;
unsigned int *buffer_prg_assignment;
struct timeval *buffer_time; /* time when the buffer was received */
unsigned int *last_used_cmd; /* For ISO Transmit with
variable sized packets only ! */
@ -180,23 +186,14 @@ static int free_dma_iso_ctx(struct dma_iso_ctx *d)
kfree(d->prg_reg);
}
if (d->ir_prg)
kfree(d->ir_prg);
if (d->it_prg)
kfree(d->it_prg);
if (d->buffer_status)
kfree(d->buffer_status);
if (d->buffer_time)
kfree(d->buffer_time);
if (d->last_used_cmd)
kfree(d->last_used_cmd);
if (d->next_buffer)
kfree(d->next_buffer);
kfree(d->ir_prg);
kfree(d->it_prg);
kfree(d->buffer_status);
kfree(d->buffer_prg_assignment);
kfree(d->buffer_time);
kfree(d->last_used_cmd);
kfree(d->next_buffer);
list_del(&d->link);
kfree(d);
return 0;
@ -230,7 +227,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
/* Init the regions for easy cleanup */
dma_region_init(&d->dma);
if (dma_region_alloc(&d->dma, d->num_desc * d->buf_size, ohci->dev,
if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev,
PCI_DMA_BIDIRECTIONAL)) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer");
free_dma_iso_ctx(d);
@ -342,6 +339,8 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int),
GFP_KERNEL);
d->buffer_prg_assignment = kmalloc(d->num_desc * sizeof(unsigned int),
GFP_KERNEL);
d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval),
GFP_KERNEL);
d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int),
@ -354,6 +353,11 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
free_dma_iso_ctx(d);
return NULL;
}
if (d->buffer_prg_assignment == NULL) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_prg_assignment");
free_dma_iso_ctx(d);
return NULL;
}
if (d->buffer_time == NULL) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_time");
free_dma_iso_ctx(d);
@ -370,6 +374,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
return NULL;
}
memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int));
memset(d->buffer_prg_assignment, 0, d->num_desc * sizeof(unsigned int));
memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval));
memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int));
memset(d->next_buffer, -1, d->num_desc * sizeof(int));
@ -379,7 +384,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
PRINT(KERN_INFO, ohci->host->id, "Iso %s DMA: %d buffers "
"of size %d allocated for a frame size %d, each with %d prgs",
(type == OHCI_ISO_RECEIVE) ? "receive" : "transmit",
d->num_desc, d->buf_size, d->frame_size, d->nb_cmd);
d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd);
return d;
}
@ -394,11 +399,36 @@ static void reset_ir_status(struct dma_iso_ctx *d, int n)
d->ir_prg[n][i].status = cpu_to_le32(d->left_size);
}
static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags)
{
struct dma_cmd *ir_prg = d->ir_prg[n];
unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
int i;
d->buffer_prg_assignment[n] = buffer;
ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
(unsigned long)d->dma.kvirt));
ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
(buf + 4) - (unsigned long)d->dma.kvirt));
for (i=2;i<d->nb_cmd-1;i++) {
ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
(buf+(i-1)*PAGE_SIZE) -
(unsigned long)d->dma.kvirt));
}
ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
(buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt));
}
static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags)
{
struct dma_cmd *ir_prg = d->ir_prg[n];
struct dma_prog_region *ir_reg = &d->prg_reg[n];
unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
unsigned long buf = (unsigned long)d->dma.kvirt;
int i;
/* the first descriptor will read only 4 bytes */
@ -508,7 +538,7 @@ static void wakeup_dma_ir_ctx(unsigned long l)
for (i = 0; i < d->num_desc; i++) {
if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) {
reset_ir_status(d, i);
d->buffer_status[i] = VIDEO1394_BUFFER_READY;
d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
do_gettimeofday(&d->buffer_time[i]);
}
}
@ -585,7 +615,7 @@ static void wakeup_dma_it_ctx(unsigned long l)
int next = d->next_buffer[i];
put_timestamp(ohci, d, next);
d->it_prg[i][d->last_used_cmd[i]].end.status = 0;
d->buffer_status[i] = VIDEO1394_BUFFER_READY;
d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
}
}
@ -595,11 +625,25 @@ static void wakeup_dma_it_ctx(unsigned long l)
wake_up_interruptible(&d->waitq);
}
static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer)
{
struct it_dma_prg *it_prg = d->it_prg[n];
unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
int i;
d->buffer_prg_assignment[n] = buffer;
for (i=0;i<d->nb_cmd;i++) {
it_prg[i].end.address =
cpu_to_le32(dma_region_offset_to_bus(&d->dma,
(buf+i*d->packet_size) - (unsigned long)d->dma.kvirt));
}
}
static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
{
struct it_dma_prg *it_prg = d->it_prg[n];
struct dma_prog_region *it_reg = &d->prg_reg[n];
unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
unsigned long buf = (unsigned long)d->dma.kvirt;
int i;
d->last_used_cmd[n] = d->nb_cmd - 1;
for (i=0;i<d->nb_cmd;i++) {
@ -796,7 +840,7 @@ static int __video1394_ioctl(struct file *file,
if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
v.nb_buffers, v.buf_size,
v.nb_buffers + 1, v.buf_size,
v.channel, 0);
if (d == NULL) {
@ -817,7 +861,7 @@ static int __video1394_ioctl(struct file *file,
}
else {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT,
v.nb_buffers, v.buf_size,
v.nb_buffers + 1, v.buf_size,
v.channel, v.packet_size);
if (d == NULL) {
@ -889,6 +933,7 @@ static int __video1394_ioctl(struct file *file,
{
struct video1394_wait v;
struct dma_iso_ctx *d;
int next_prg;
if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
@ -896,7 +941,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (d == NULL) return -EFAULT;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
if ((v.buffer<0) || (v.buffer>=d->num_desc - 1)) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;
@ -913,12 +958,14 @@ static int __video1394_ioctl(struct file *file,
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
next_prg = (d->last_buffer + 1) % d->num_desc;
if (d->last_buffer>=0)
d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0)
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0)
& 0xfffffff0) | 0x1);
d->last_buffer = v.buffer;
d->last_buffer = next_prg;
reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags);
d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0;
@ -930,7 +977,7 @@ static int __video1394_ioctl(struct file *file,
/* Tell the controller where the first program is */
reg_write(ohci, d->cmdPtr,
dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x1);
dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1);
/* Run IR context */
reg_write(ohci, d->ctrlSet, 0x8000);
@ -951,7 +998,7 @@ static int __video1394_ioctl(struct file *file,
{
struct video1394_wait v;
struct dma_iso_ctx *d;
int i;
int i = 0;
if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
@ -959,7 +1006,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (d == NULL) return -EFAULT;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
if ((v.buffer<0) || (v.buffer>d->num_desc - 1)) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;
@ -1005,9 +1052,9 @@ static int __video1394_ioctl(struct file *file,
* Look ahead to see how many more buffers have been received
*/
i=0;
while (d->buffer_status[(v.buffer+1)%d->num_desc]==
while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]==
VIDEO1394_BUFFER_READY) {
v.buffer=(v.buffer+1)%d->num_desc;
v.buffer=(v.buffer+1)%(d->num_desc - 1);
i++;
}
spin_unlock_irqrestore(&d->lock, flags);
@ -1023,6 +1070,7 @@ static int __video1394_ioctl(struct file *file,
struct video1394_wait v;
unsigned int *psizes = NULL;
struct dma_iso_ctx *d;
int next_prg;
if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
@ -1030,7 +1078,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
if (d == NULL) return -EFAULT;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
if ((v.buffer<0) || (v.buffer>=d->num_desc - 1)) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;
@ -1056,19 +1104,19 @@ static int __video1394_ioctl(struct file *file,
spin_lock_irqsave(&d->lock,flags);
// last_buffer is last_prg
next_prg = (d->last_buffer + 1) % d->num_desc;
if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is already used",v.buffer);
spin_unlock_irqrestore(&d->lock,flags);
if (psizes)
kfree(psizes);
kfree(psizes);
return -EBUSY;
}
if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
initialize_dma_it_prg_var_packet_queue(
d, v.buffer, psizes,
ohci);
d, next_prg, psizes, ohci);
}
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
@ -1076,16 +1124,17 @@ static int __video1394_ioctl(struct file *file,
if (d->last_buffer >= 0) {
d->it_prg[d->last_buffer]
[ d->last_used_cmd[d->last_buffer] ].end.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
0) & 0xfffffff0) | 0x3);
d->it_prg[d->last_buffer]
[ d->last_used_cmd[d->last_buffer] ].begin.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
0) & 0xfffffff0) | 0x3);
d->next_buffer[d->last_buffer] = v.buffer;
d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1);
}
d->last_buffer = v.buffer;
d->last_buffer = next_prg;
reprogram_dma_it_prg(d, d->last_buffer, v.buffer);
d->next_buffer[d->last_buffer] = -1;
d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0;
@ -1100,7 +1149,7 @@ static int __video1394_ioctl(struct file *file,
/* Tell the controller where the first program is */
reg_write(ohci, d->cmdPtr,
dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x3);
dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3);
/* Run IT context */
reg_write(ohci, d->ctrlSet, 0x8000);
@ -1116,9 +1165,7 @@ static int __video1394_ioctl(struct file *file,
}
}
if (psizes)
kfree(psizes);
kfree(psizes);
return 0;
}
@ -1133,7 +1180,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
if (d == NULL) return -EFAULT;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
if ((v.buffer<0) || (v.buffer>=d->num_desc-1)) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;

View File

@ -465,8 +465,10 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
if (atkbd->softrepeat) return 0;
i = j = 0;
while (i < 32 && period[i] < dev->rep[REP_PERIOD]) i++;
while (j < 4 && delay[j] < dev->rep[REP_DELAY]) j++;
while (i < 31 && period[i] < dev->rep[REP_PERIOD])
i++;
while (j < 3 && delay[j] < dev->rep[REP_DELAY])
j++;
dev->rep[REP_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5);

View File

@ -341,6 +341,8 @@ static int alps_reconnect(struct psmouse *psmouse)
unsigned char param[4];
int version;
psmouse_reset(psmouse);
if (!(priv->i = alps_get_model(psmouse, &version)))
return -1;
@ -395,7 +397,7 @@ int alps_init(struct psmouse *psmouse)
}
if (param[0] & 0x04) {
printk(KERN_INFO " Enabling hardware tapping\n");
printk(KERN_INFO "alps.c: Enabling hardware tapping\n");
if (alps_tap_mode(psmouse, 1))
printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
}

View File

@ -388,6 +388,24 @@ static ssize_t serio_show_id_extra(struct device *dev, char *buf)
return sprintf(buf, "%02x\n", serio->id.extra);
}
static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL);
static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL);
static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL);
static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL);
static struct attribute *serio_device_id_attrs[] = {
&dev_attr_type.attr,
&dev_attr_proto.attr,
&dev_attr_id.attr,
&dev_attr_extra.attr,
NULL
};
static struct attribute_group serio_id_attr_group = {
.name = "id",
.attrs = serio_device_id_attrs,
};
static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count)
{
struct serio *serio = to_serio_port(dev);
@ -444,10 +462,6 @@ static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t c
static struct device_attribute serio_device_attrs[] = {
__ATTR(description, S_IRUGO, serio_show_description, NULL),
__ATTR(id_type, S_IRUGO, serio_show_id_type, NULL),
__ATTR(id_proto, S_IRUGO, serio_show_id_proto, NULL),
__ATTR(id_id, S_IRUGO, serio_show_id_id, NULL),
__ATTR(id_extra, S_IRUGO, serio_show_id_extra, NULL),
__ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
__ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
__ATTR_NULL
@ -498,6 +512,7 @@ static void serio_add_port(struct serio *serio)
if (serio->start)
serio->start(serio);
device_add(&serio->dev);
sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);
serio->registered = 1;
}
@ -526,6 +541,7 @@ static void serio_destroy_port(struct serio *serio)
}
if (serio->registered) {
sysfs_remove_group(&serio->dev.kobj, &serio_id_attr_group);
device_del(&serio->dev);
list_del_init(&serio->node);
serio->registered = 0;
@ -779,7 +795,6 @@ static int serio_resume(struct device *dev)
struct serio *serio = to_serio_port(dev);
if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
serio_disconnect_port(serio);
/*
* Driver re-probing can take a while, so better let kseriod
* deal with it.

View File

@ -27,11 +27,15 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_MOUSE);
#define SERPORT_BUSY 1
#define SERPORT_ACTIVE 2
#define SERPORT_DEAD 3
struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
struct serio *serio;
struct serio_device_id id;
spinlock_t lock;
unsigned long flags;
};
@ -45,11 +49,29 @@ static int serport_serio_write(struct serio *serio, unsigned char data)
return -(serport->tty->driver->write(serport->tty, &data, 1) != 1);
}
static int serport_serio_open(struct serio *serio)
{
struct serport *serport = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&serport->lock, flags);
set_bit(SERPORT_ACTIVE, &serport->flags);
spin_unlock_irqrestore(&serport->lock, flags);
return 0;
}
static void serport_serio_close(struct serio *serio)
{
struct serport *serport = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&serport->lock, flags);
clear_bit(SERPORT_ACTIVE, &serport->flags);
set_bit(SERPORT_DEAD, &serport->flags);
spin_unlock_irqrestore(&serport->lock, flags);
serport->serio->id.type = 0;
wake_up_interruptible(&serport->wait);
}
@ -61,36 +83,21 @@ static void serport_serio_close(struct serio *serio)
static int serport_ldisc_open(struct tty_struct *tty)
{
struct serport *serport;
struct serio *serio;
char name[64];
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
serport = kmalloc(sizeof(struct serport), GFP_KERNEL);
serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (unlikely(!serport || !serio)) {
kfree(serport);
kfree(serio);
serport = kcalloc(1, sizeof(struct serport), GFP_KERNEL);
if (!serport)
return -ENOMEM;
}
memset(serport, 0, sizeof(struct serport));
serport->serio = serio;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
serport->tty = tty;
tty->disc_data = serport;
memset(serio, 0, sizeof(struct serio));
strlcpy(serio->name, "Serial port", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
serio->id.type = SERIO_RS232;
serio->write = serport_serio_write;
serio->close = serport_serio_close;
serio->port_data = serport;
spin_lock_init(&serport->lock);
init_waitqueue_head(&serport->wait);
tty->disc_data = serport;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return 0;
}
@ -100,7 +107,8 @@ static int serport_ldisc_open(struct tty_struct *tty)
static void serport_ldisc_close(struct tty_struct *tty)
{
struct serport *serport = (struct serport*) tty->disc_data;
struct serport *serport = (struct serport *) tty->disc_data;
kfree(serport);
}
@ -116,9 +124,19 @@ static void serport_ldisc_close(struct tty_struct *tty)
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
struct serport *serport = (struct serport*) tty->disc_data;
unsigned long flags;
int i;
spin_lock_irqsave(&serport->lock, flags);
if (!test_bit(SERPORT_ACTIVE, &serport->flags))
goto out;
for (i = 0; i < count; i++)
serio_interrupt(serport->serio, cp[i], 0, NULL);
out:
spin_unlock_irqrestore(&serport->lock, flags);
}
/*
@ -141,16 +159,33 @@ static int serport_ldisc_room(struct tty_struct *tty)
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
{
struct serport *serport = (struct serport*) tty->disc_data;
struct serio *serio;
char name[64];
if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
return -EBUSY;
serport->serio = serio = kcalloc(1, sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
strlcpy(serio->name, "Serial port", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
serio->id = serport->id;
serio->id.type = SERIO_RS232;
serio->write = serport_serio_write;
serio->open = serport_serio_open;
serio->close = serport_serio_close;
serio->port_data = serport;
serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
wait_event_interruptible(serport->wait, !serport->serio->id.type);
serio_unregister_port(serport->serio);
wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
serio_unregister_port(serport->serio);
serport->serio = NULL;
clear_bit(SERPORT_DEAD, &serport->flags);
clear_bit(SERPORT_BUSY, &serport->flags);
return 0;
@ -163,16 +198,15 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u
static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg)
{
struct serport *serport = (struct serport*) tty->disc_data;
struct serio *serio = serport->serio;
unsigned long type;
if (cmd == SPIOCSTYPE) {
if (get_user(type, (unsigned long __user *) arg))
return -EFAULT;
serio->id.proto = type & 0x000000ff;
serio->id.id = (type & 0x0000ff00) >> 8;
serio->id.extra = (type & 0x00ff0000) >> 16;
serport->id.proto = type & 0x000000ff;
serport->id.id = (type & 0x0000ff00) >> 8;
serport->id.extra = (type & 0x00ff0000) >> 16;
return 0;
}
@ -182,9 +216,13 @@ static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsi
static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{
struct serport *sp = (struct serport *) tty->disc_data;
struct serport *serport = (struct serport *) tty->disc_data;
unsigned long flags;
serio_drv_write_wakeup(sp->serio);
spin_lock_irqsave(&serport->lock, flags);
if (test_bit(SERPORT_ACTIVE, &serport->flags))
serio_drv_write_wakeup(serport->serio);
spin_unlock_irqrestore(&serport->lock, flags);
}
/*

View File

@ -269,9 +269,8 @@ static int linear_make_request (request_queue_t *q, struct bio *bio)
* split it.
*/
struct bio_pair *bp;
bp = bio_split(bio, bio_split_pool,
(bio->bi_sector + (bio->bi_size >> 9) -
(tmp_dev->offset + tmp_dev->size))<<1);
bp = bio_split(bio, bio_split_pool,
((tmp_dev->offset + tmp_dev->size)<<1) - bio->bi_sector);
if (linear_make_request(q, &bp->bio1))
generic_make_request(&bp->bio1);
if (linear_make_request(q, &bp->bio2))

View File

@ -462,10 +462,6 @@ static int multipath_run (mddev_t *mddev)
}
memset(conf->multipaths, 0, sizeof(struct multipath_info)*mddev->raid_disks);
mddev->queue->unplug_fn = multipath_unplug;
mddev->queue->issue_flush_fn = multipath_issue_flush;
conf->working_disks = 0;
ITERATE_RDEV(mddev,rdev,tmp) {
disk_idx = rdev->raid_disk;
@ -528,6 +524,10 @@ static int multipath_run (mddev_t *mddev)
* Ok, everything is just fine now
*/
mddev->array_size = mddev->size;
mddev->queue->unplug_fn = multipath_unplug;
mddev->queue->issue_flush_fn = multipath_issue_flush;
return 0;
out_free_conf:

View File

@ -1197,10 +1197,6 @@ static int run(mddev_t *mddev)
if (!conf->r1bio_pool)
goto out_no_mem;
mddev->queue->unplug_fn = raid1_unplug;
mddev->queue->issue_flush_fn = raid1_issue_flush;
ITERATE_RDEV(mddev, rdev, tmp) {
disk_idx = rdev->raid_disk;
if (disk_idx >= mddev->raid_disks
@ -1282,6 +1278,9 @@ static int run(mddev_t *mddev)
*/
mddev->array_size = mddev->size;
mddev->queue->unplug_fn = raid1_unplug;
mddev->queue->issue_flush_fn = raid1_issue_flush;
return 0;
out_no_mem:

View File

@ -1639,9 +1639,6 @@ static int run(mddev_t *mddev)
mdname(mddev));
goto out_free_conf;
}
mddev->queue->unplug_fn = raid10_unplug;
mddev->queue->issue_flush_fn = raid10_issue_flush;
ITERATE_RDEV(mddev, rdev, tmp) {
disk_idx = rdev->raid_disk;
@ -1713,6 +1710,9 @@ static int run(mddev_t *mddev)
mddev->array_size = size/2;
mddev->resync_max_sectors = size;
mddev->queue->unplug_fn = raid10_unplug;
mddev->queue->issue_flush_fn = raid10_issue_flush;
/* Calculate max read-ahead size.
* We need to readahead at least twice a whole stripe....
* maybe...

View File

@ -1620,9 +1620,6 @@ static int run (mddev_t *mddev)
atomic_set(&conf->active_stripes, 0);
atomic_set(&conf->preread_active_stripes, 0);
mddev->queue->unplug_fn = raid5_unplug_device;
mddev->queue->issue_flush_fn = raid5_issue_flush;
PRINTK("raid5: run(%s) called.\n", mdname(mddev));
ITERATE_RDEV(mddev,rdev,tmp) {
@ -1728,6 +1725,10 @@ memory = conf->max_nr_stripes * (sizeof(struct stripe_head) +
}
/* Ok, everything is just fine now */
mddev->queue->unplug_fn = raid5_unplug_device;
mddev->queue->issue_flush_fn = raid5_issue_flush;
mddev->array_size = mddev->size * (mddev->raid_disks - 1);
return 0;
abort:

View File

@ -1779,9 +1779,6 @@ static int run (mddev_t *mddev)
atomic_set(&conf->active_stripes, 0);
atomic_set(&conf->preread_active_stripes, 0);
mddev->queue->unplug_fn = raid6_unplug_device;
mddev->queue->issue_flush_fn = raid6_issue_flush;
PRINTK("raid6: run(%s) called.\n", mdname(mddev));
ITERATE_RDEV(mddev,rdev,tmp) {
@ -1895,6 +1892,9 @@ static int run (mddev_t *mddev)
/* Ok, everything is just fine now */
mddev->array_size = mddev->size * (mddev->raid_disks - 2);
mddev->queue->unplug_fn = raid6_unplug_device;
mddev->queue->issue_flush_fn = raid6_issue_flush;
return 0;
abort:
if (conf) {

View File

@ -23,9 +23,9 @@
LIST_HEAD(saa7146_devices);
DECLARE_MUTEX(saa7146_devices_lock);
static int saa7146_num = 0;
static int saa7146_num;
unsigned int saa7146_debug = 0;
unsigned int saa7146_debug;
module_param(saa7146_debug, int, 0644);
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");

View File

@ -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"

View File

@ -1,3 +1,40 @@
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
@ -11,16 +48,3 @@ config DVB_B2C2_SKYSTAR
for the B2C2/BBTI Air2PC-ATSC card.
Say Y if you own such a device and want to use it.
config DVB_B2C2_USB
tristate "B2C2/Technisat Air/Sky/Cable2PC USB"
depends on DVB_CORE && USB && EXPERIMENTAL
select DVB_STV0299
select DVB_MT352
help
Support for the Air/Sky/Cable2PC USB DVB device by B2C2. Currently
the does nothing, but providing basic function for the used usb
protocol.
Say Y if you own such a device and want to use it.

View File

@ -1,6 +1,14 @@
obj-b2c2-usb = b2c2-usb-core.o b2c2-common.o
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
obj-$(CONFIG_DVB_B2C2_USB) + = b2c2-usb.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/

View File

@ -1,214 +0,0 @@
/*
* b2c2-common.c - common methods for the B2C2/Technisat SkyStar2 PCI DVB card and
* for the B2C2/Technisat Sky/Cable/AirStar USB devices
* based on the FlexCopII/FlexCopIII by B2C2, Inc.
*
* Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
*
* FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
* FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
* Vincenzo Di Massa, hawk.it at tiscalinet.it
*
* Converted to Linux coding style
* Misc reorganization, polishing, restyling
* Roberto Ragusa, r.ragusa at libero.it
*
* Added hardware filtering support,
* Niklas Peinecke, peinecke at gdv.uni-hannover.de
*
* 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 "stv0299.h"
#include "mt352.h"
#include "mt312.h"
static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
{
u8 aclk = 0;
u8 bclk = 0;
if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
stv0299_writereg (fe, 0x13, aclk);
stv0299_writereg (fe, 0x14, bclk);
stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);
return 0;
}
static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
// struct adapter* adapter = (struct adapter*) fe->dvb->priv;
div = params->frequency / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x84; // 0xC4
buf[3] = 0x08;
if (params->frequency < 1500000) buf[3] |= 0x10;
// if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
return 0;
}
static u8 samsung_tbmu24112_inittab[] = {
0x01, 0x15,
0x02, 0x30,
0x03, 0x00,
0x04, 0x7D,
0x05, 0x35,
0x06, 0x02,
0x07, 0x00,
0x08, 0xC3,
0x0C, 0x00,
0x0D, 0x81,
0x0E, 0x23,
0x0F, 0x12,
0x10, 0x7E,
0x11, 0x84,
0x12, 0xB9,
0x13, 0x88,
0x14, 0x89,
0x15, 0xC9,
0x16, 0x00,
0x17, 0x5C,
0x18, 0x00,
0x19, 0x00,
0x1A, 0x00,
0x1C, 0x00,
0x1D, 0x00,
0x1E, 0x00,
0x1F, 0x3A,
0x20, 0x2E,
0x21, 0x80,
0x22, 0xFF,
0x23, 0xC1,
0x28, 0x00,
0x29, 0x1E,
0x2A, 0x14,
0x2B, 0x0F,
0x2C, 0x09,
0x2D, 0x05,
0x31, 0x1F,
0x32, 0x19,
0x33, 0xFE,
0x34, 0x93,
0xff, 0xff,
};
static struct stv0299_config samsung_tbmu24112_config = {
.demod_address = 0x68,
.inittab = samsung_tbmu24112_inittab,
.mclk = 88000000UL,
.invert = 0,
.enhanced_tuning = 0,
.skip_reinit = 0,
.lock_output = STV0229_LOCKOUTPUT_LK,
.volt13_op0_op1 = STV0299_VOLT13_OP1,
.min_delay_ms = 100,
.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
.pll_set = samsung_tbmu24112_pll_set,
};
static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x10, 0x2d };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
{
u32 div;
unsigned char bs = 0;
#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
pllbuf[1] = div >> 8;
pllbuf[2] = div & 0xff;
pllbuf[3] = 0xcc;
pllbuf[4] = bs;
return 0;
}
static struct mt352_config samsung_tdtc9251dh0_config = {
.demod_address = 0x0f,
.demod_init = samsung_tdtc9251dh0_demod_init,
.pll_set = samsung_tdtc9251dh0_pll_set,
};
static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
// struct adapter* adapter = (struct adapter*) fe->dvb->priv;
div = (params->frequency + (125/2)) / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = (div >> 0) & 0xff;
buf[2] = 0x84 | ((div >> 10) & 0x60);
buf[3] = 0x80;
if (params->frequency < 1550000)
buf[3] |= 0x02;
//if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
return 0;
}
static struct mt312_config skystar23_samsung_tbdu18132_config = {
.demod_address = 0x0e,
.pll_set = skystar23_samsung_tbdu18132_pll_set,
};

View File

@ -1,549 +0,0 @@
/*
* Copyright (C) 2004 Patrick Boettcher <patrick.boettcher@desy.de>,
* Luca Bertagnolio <>,
*
* based on information provided by John Jurrius from BBTI, Inc.
*
* 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, version 2.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/version.h>
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_filter.h"
#include "dvb_net.h"
#include "dvb_frontend.h"
/* debug */
#define dprintk(level,args...) \
do { if ((debug & level)) { printk(args); } } while (0)
#define debug_dump(b,l) if (debug) {\
int i; deb_xfer("%s: %d > ",__FUNCTION__,l); \
for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
deb_xfer("\n");\
}
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4 (or-able)).");
#define deb_info(args...) dprintk(0x01,args)
#define deb_ts(args...) dprintk(0x02,args)
#define deb_ctrl(args...) dprintk(0x04,args)
/* Version information */
#define DRIVER_VERSION "0.0"
#define DRIVER_DESC "Driver for B2C2/Technisat Air/Cable/Sky-2-PC USB devices"
#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
/* transfer parameters */
#define B2C2_USB_FRAMES_PER_ISO 4
#define B2C2_USB_NUM_ISO_URB 4 /* TODO check out a good value */
#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(b2c2->udev,0)
#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(b2c2->udev,0)
#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(b2c2->udev,0x81)
struct usb_b2c2_usb {
struct usb_device *udev;
struct usb_interface *uintf;
u8 *iso_buffer;
int buffer_size;
dma_addr_t iso_dma_handle;
struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
};
/*
* USB
* 10 90 34 12 78 56 04 00
* usb_control_msg(udev, usb_sndctrlpipe(udev,0),
* 0x90,
* 0x10,
* 0x1234,
* 0x5678,
* buf,
* 4,
* 5*HZ);
*
* extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
* __u8 request,
* __u8 requesttype,
* __u16 value,
* __u16 index,
* void *data,
* __u16 size,
* int timeout);
*
*/
/* request types */
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),
*/
RTYPE_READ_DW = (3 << 6),
RTYPE_WRITE_DW_1 = (1 << 6),
RTYPE_READ_V8_MEMORY = (6 << 6),
RTYPE_WRITE_V8_MEMORY = (7 << 6),
RTYPE_WRITE_V8_FLASH = (8 << 6),
RTYPE_GENERIC = (9 << 6),
} b2c2_usb_request_type_t;
/* 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,
} b2c2_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,
} b2c2_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,
} b2c2_usb_utility_function_t;
#define B2C2_WAIT_FOR_OPERATION_RW 1 // 1 s
#define B2C2_WAIT_FOR_OPERATION_RDW 3 // 3 s
#define B2C2_WAIT_FOR_OPERATION_WDW 1 // 1 s
#define B2C2_WAIT_FOR_OPERATION_V8READ 3 // 3 s
#define B2C2_WAIT_FOR_OPERATION_V8WRITE 3 // 3 s
#define B2C2_WAIT_FOR_OPERATION_V8FLASH 3 // 3 s
/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
* in the IBI address, to make the V8 code simpler.
* PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
* in general: 0000 0HHH 000L LL00
* IBI ADDRESS FORMAT: RHHH BLLL
*
* where R is the read(1)/write(0) bit, B is the busy bit
* and HHH and LLL are the two sets of three bits from the PCI address.
*/
#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
/*
* DKT 020228 - forget about this VENDOR_BUFFER_SIZE, read and write register
* deal with DWORD or 4 bytes, that should be should from now on
*/
static u32 b2c2_usb_read_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI)
{
u32 val;
u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | 0x0080;
int len = usb_control_msg(b2c2->udev,
B2C2_USB_CTRL_PIPE_IN,
B2C2_USB_READ_REG,
RTYPE_READ_DW,
wAddress,
0,
&val,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * 1000);
if (len != sizeof(u32)) {
err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
return -EIO;
} else
return val;
}
/*
* DKT 020228 - from now on, we don't support anything older than firm 1.00
* I eliminated the write register as a 2 trip of writing hi word and lo word
* and force this to write only 4 bytes at a time.
* NOTE: this should work with all the firmware from 1.00 and newer
*/
static int b2c2_usb_write_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI, u32 val)
{
u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI);
int len = usb_control_msg(b2c2->udev,
B2C2_USB_CTRL_PIPE_OUT,
B2C2_USB_WRITE_REG,
RTYPE_WRITE_DW_1,
wAddress,
0,
&val,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * 1000);
if (len != sizeof(u32)) {
err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
return -EIO;
} else
return 0;
}
/*
* DKT 010817 - add support for V8 memory read/write and flash update
*/
static int b2c2_usb_v8_memory_req(struct usb_b2c2_usb *b2c2,
b2c2_usb_request_t req, u8 page, u16 wAddress,
u16 buflen, u8 *pbBuffer)
{
u8 dwRequestType;
u16 wIndex;
int nWaitTime,pipe,len;
wIndex = page << 8;
switch (req) {
case B2C2_USB_READ_V8_MEM:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_IN;
break;
case B2C2_USB_WRITE_V8_MEM:
wIndex |= pbBuffer[0];
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
case B2C2_USB_FLASH_BLOCK:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
default:
deb_info("unsupported request for v8_mem_req %x.\n",req);
return -EINVAL;
}
len = usb_control_msg(b2c2->udev,pipe,
req,
dwRequestType,
wAddress,
wIndex,
pbBuffer,
buflen,
nWaitTime * 1000);
return len == buflen ? 0 : -EIO;
}
static int b2c2_usb_i2c_req(struct usb_b2c2_usb *b2c2,
b2c2_usb_request_t req, b2c2_usb_i2c_function_t func,
u8 port, u8 chipaddr, u8 addr, u8 buflen, u8 *buf)
{
u16 wValue, wIndex;
int nWaitTime,pipe,len;
u8 dwRequestType;
switch (func) {
case USB_FUNC_I2C_WRITE:
case USB_FUNC_I2C_MULTIWRITE:
case USB_FUNC_I2C_REPEATWRITE:
/* DKT 020208 - add this to support special case of DiSEqC */
case USB_FUNC_I2C_CHECKWRITE:
pipe = B2C2_USB_CTRL_PIPE_OUT;
nWaitTime = 2;
dwRequestType = (u8) RTYPE_GENERIC;
break;
case USB_FUNC_I2C_READ:
case USB_FUNC_I2C_REPEATREAD:
pipe = B2C2_USB_CTRL_PIPE_IN;
nWaitTime = 2;
dwRequestType = (u8) RTYPE_GENERIC;
break;
default:
deb_info("unsupported function for i2c_req %x\n",func);
return -EINVAL;
}
wValue = (func << 8 ) | port;
wIndex = (chipaddr << 8 ) | addr;
len = usb_control_msg(b2c2->udev,pipe,
req,
dwRequestType,
addr,
wIndex,
buf,
buflen,
nWaitTime * 1000);
return len == buflen ? 0 : -EIO;
}
int static b2c2_usb_utility_req(struct usb_b2c2_usb *b2c2, int set,
b2c2_usb_utility_function_t func, u8 extra, u16 wIndex,
u16 buflen, u8 *pvBuffer)
{
u16 wValue;
int nWaitTime = 2,
pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
len;
wValue = (func << 8) | extra;
len = usb_control_msg(b2c2->udev,pipe,
B2C2_USB_UTILITY,
(u8) RTYPE_GENERIC,
wValue,
wIndex,
pvBuffer,
buflen,
nWaitTime * 1000);
return len == buflen ? 0 : -EIO;
}
static void b2c2_dumpfourreg(struct usb_b2c2_usb *b2c2, u16 offs)
{
u32 r0,r1,r2,r3;
r0 = r1 = r2 = r3 = 0;
r0 = b2c2_usb_read_dw(b2c2,offs);
r1 = b2c2_usb_read_dw(b2c2,offs + 0x04);
r2 = b2c2_usb_read_dw(b2c2,offs + 0x08);
r3 = b2c2_usb_read_dw(b2c2,offs + 0x0c);
deb_ctrl("dump: offset: %03x, %08x, %08x, %08x, %08x\n",offs,r0,r1,r2,r3);
}
static void b2c2_urb_complete(struct urb *urb, struct pt_regs *ptregs)
{
struct usb_b2c2_usb *b2c2 = urb->context;
deb_ts("urb completed, bufsize: %d\n",urb->transfer_buffer_length);
// urb_submit_urb(urb,GFP_ATOMIC); enable for real action
}
static void b2c2_exit_usb(struct usb_b2c2_usb *b2c2)
{
int i;
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (b2c2->iso_urb[i] != NULL) { /* not sure about unlink_urb and iso-urbs TODO */
deb_info("unlinking/killing urb no. %d\n",i);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7)
usb_unlink_urb(b2c2->iso_urb[i]);
#else
usb_kill_urb(b2c2->iso_urb[i]);
#endif
usb_free_urb(b2c2->iso_urb[i]);
}
if (b2c2->iso_buffer != NULL)
pci_free_consistent(NULL,b2c2->buffer_size, b2c2->iso_buffer, b2c2->iso_dma_handle);
}
static int b2c2_init_usb(struct usb_b2c2_usb *b2c2)
{
u16 frame_size = le16_to_cpu(b2c2->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
int buffer_offset = 0;
deb_info("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
b2c2->iso_buffer = pci_alloc_consistent(NULL,bufsize,&b2c2->iso_dma_handle);
if (b2c2->iso_buffer == NULL)
return -ENOMEM;
memset(b2c2->iso_buffer, 0, bufsize);
b2c2->buffer_size = bufsize;
/* creating iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (!(b2c2->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
ret = -ENOMEM;
goto urb_error;
}
/* initialising and submitting iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
int frame_offset = 0;
struct urb *urb = b2c2->iso_urb[i];
deb_info("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
urb->dev = b2c2->udev;
urb->context = b2c2;
urb->complete = b2c2_urb_complete;
urb->pipe = B2C2_USB_DATA_PIPE;
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer = b2c2->iso_buffer + buffer_offset;
buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
deb_info("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length = frame_size;
frame_offset += frame_size;
}
if ((ret = usb_submit_urb(b2c2->iso_urb[i],GFP_ATOMIC))) {
err("submitting urb %d failed with %d.",i,ret);
goto urb_error;
}
deb_info("submitted urb no. %d.\n",i);
}
ret = 0;
goto success;
urb_error:
b2c2_exit_usb(b2c2);
success:
return ret;
}
static int b2c2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_b2c2_usb *b2c2 = NULL;
int ret;
b2c2 = kmalloc(sizeof(struct usb_b2c2_usb),GFP_KERNEL);
if (b2c2 == NULL) {
err("no memory");
return -ENOMEM;
}
b2c2->udev = udev;
b2c2->uintf = intf;
/* use the alternate setting with the larges buffer */
usb_set_interface(udev,0,1);
if ((ret = b2c2_init_usb(b2c2)))
goto usb_init_error;
usb_set_intfdata(intf,b2c2);
switch (udev->speed) {
case USB_SPEED_LOW:
err("cannot handle USB speed because it is to sLOW.");
break;
case USB_SPEED_FULL:
info("running at FULL speed.");
break;
case USB_SPEED_HIGH:
info("running at HIGH speed.");
break;
case USB_SPEED_UNKNOWN: /* fall through */
default:
err("cannot handle USB speed because it is unkown.");
break;
}
b2c2_dumpfourreg(b2c2,0x200);
b2c2_dumpfourreg(b2c2,0x300);
b2c2_dumpfourreg(b2c2,0x400);
b2c2_dumpfourreg(b2c2,0x700);
if (ret == 0)
info("%s successfully initialized and connected.",DRIVER_DESC);
else
info("%s error while loading driver (%d)",DRIVER_DESC,ret);
ret = 0;
goto success;
usb_init_error:
kfree(b2c2);
success:
return ret;
}
static void b2c2_usb_disconnect(struct usb_interface *intf)
{
struct usb_b2c2_usb *b2c2 = usb_get_intfdata(intf);
usb_set_intfdata(intf,NULL);
if (b2c2 != NULL) {
b2c2_exit_usb(b2c2);
kfree(b2c2);
}
info("%s successfully deinitialized and disconnected.",DRIVER_DESC);
}
static struct usb_device_id b2c2_usb_table [] = {
{ USB_DEVICE(0x0af7, 0x0101) }
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver b2c2_usb_driver = {
.owner = THIS_MODULE,
.name = "dvb_b2c2_usb",
.probe = b2c2_usb_probe,
.disconnect = b2c2_usb_disconnect,
.id_table = b2c2_usb_table,
};
/* module stuff */
static int __init b2c2_usb_init(void)
{
int result;
if ((result = usb_register(&b2c2_usb_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit b2c2_usb_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&b2c2_usb_driver);
}
module_init (b2c2_usb_init);
module_exit (b2c2_usb_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, b2c2_usb_table);

View File

@ -0,0 +1,164 @@
/*
* 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 */
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;
struct module *owner;
/* options and status */
int extra_feedcount;
int feedcount;
int pid_filtering;
int fullts_streaming_state;
/* 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

View File

@ -0,0 +1,149 @@
/*
* 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);

View File

@ -0,0 +1,153 @@
/*
* 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;
}
/* 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;
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->dvb_adapter.proposed_mac,buf,3);
mac[3] = 0xfe;
mac[4] = 0xff;
memcpy(&fc->dvb_adapter.proposed_mac[3],&buf[5],3); */
} else
memcpy(fc->dvb_adapter.proposed_mac,buf,6);
}
return ret;
}
EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr);

View File

@ -0,0 +1,403 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-fe-tuner.c - methods for attaching a frontend and controlling DiSEqC.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#include "stv0299.h"
#include "mt352.h"
#include "nxt2002.h"
#include "stv0297.h"
#include "mt312.h"
/* lnb control */
static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
struct flexcop_device *fc = fe->dvb->priv;
flexcop_ibi_value v;
deb_tuner("polarity/voltage = %u\n", voltage);
v = fc->read_ibi_reg(fc, misc_204);
switch (voltage) {
case SEC_VOLTAGE_OFF:
v.misc_204.ACPI1_sig = 1;
break;
case SEC_VOLTAGE_13:
v.misc_204.ACPI1_sig = 0;
v.misc_204.LNB_L_H_sig = 0;
break;
case SEC_VOLTAGE_18:
v.misc_204.ACPI1_sig = 0;
v.misc_204.LNB_L_H_sig = 1;
break;
default:
err("unknown SEC_VOLTAGE value");
return -EINVAL;
}
return fc->write_ibi_reg(fc, misc_204, v);
}
static int flexcop_sleep(struct dvb_frontend* fe)
{
struct flexcop_device *fc = fe->dvb->priv;
/* flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
if (fc->fe_sleep)
return fc->fe_sleep(fe);
/* v.misc_204.ACPI3_sig = 1;
fc->write_ibi_reg(fc,misc_204,v);*/
return 0;
}
static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
{
/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
struct flexcop_device *fc = fe->dvb->priv;
flexcop_ibi_value v;
u16 ax;
v.raw = 0;
deb_tuner("tone = %u\n",tone);
switch (tone) {
case SEC_TONE_ON:
ax = 0x01ff;
break;
case SEC_TONE_OFF:
ax = 0;
break;
default:
err("unknown SEC_TONE value");
return -EINVAL;
}
v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax;
return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
}
static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
{
flexcop_set_tone(fe, SEC_TONE_ON);
udelay(data ? 500 : 1000);
flexcop_set_tone(fe, SEC_TONE_OFF);
udelay(data ? 1000 : 500);
}
static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
{
int i, par = 1, d;
for (i = 7; i >= 0; i--) {
d = (data >> i) & 1;
par ^= d;
flexcop_diseqc_send_bit(fe, d);
}
flexcop_diseqc_send_bit(fe, par);
}
static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
{
int i;
flexcop_set_tone(fe, SEC_TONE_OFF);
mdelay(16);
for (i = 0; i < len; i++)
flexcop_diseqc_send_byte(fe,msg[i]);
mdelay(16);
if (burst != -1) {
if (burst)
flexcop_diseqc_send_byte(fe, 0xff);
else {
flexcop_set_tone(fe, SEC_TONE_ON);
udelay(12500);
flexcop_set_tone(fe, SEC_TONE_OFF);
}
msleep(20);
}
return 0;
}
static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
{
return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
}
static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
{
return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
}
/* dvb-s stv0299 */
static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
{
u8 aclk = 0;
u8 bclk = 0;
if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
stv0299_writereg (fe, 0x13, aclk);
stv0299_writereg (fe, 0x14, bclk);
stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);
return 0;
}
static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
struct flexcop_device *fc = fe->dvb->priv;
div = params->frequency / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x84; /* 0xC4 */
buf[3] = 0x08;
if (params->frequency < 1500000) buf[3] |= 0x10;
if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
return -EIO;
return 0;
}
static u8 samsung_tbmu24112_inittab[] = {
0x01, 0x15,
0x02, 0x30,
0x03, 0x00,
0x04, 0x7D,
0x05, 0x35,
0x06, 0x02,
0x07, 0x00,
0x08, 0xC3,
0x0C, 0x00,
0x0D, 0x81,
0x0E, 0x23,
0x0F, 0x12,
0x10, 0x7E,
0x11, 0x84,
0x12, 0xB9,
0x13, 0x88,
0x14, 0x89,
0x15, 0xC9,
0x16, 0x00,
0x17, 0x5C,
0x18, 0x00,
0x19, 0x00,
0x1A, 0x00,
0x1C, 0x00,
0x1D, 0x00,
0x1E, 0x00,
0x1F, 0x3A,
0x20, 0x2E,
0x21, 0x80,
0x22, 0xFF,
0x23, 0xC1,
0x28, 0x00,
0x29, 0x1E,
0x2A, 0x14,
0x2B, 0x0F,
0x2C, 0x09,
0x2D, 0x05,
0x31, 0x1F,
0x32, 0x19,
0x33, 0xFE,
0x34, 0x93,
0xff, 0xff,
};
static struct stv0299_config samsung_tbmu24112_config = {
.demod_address = 0x68,
.inittab = samsung_tbmu24112_inittab,
.mclk = 88000000UL,
.invert = 0,
.enhanced_tuning = 0,
.skip_reinit = 0,
.lock_output = STV0229_LOCKOUTPUT_LK,
.volt13_op0_op1 = STV0299_VOLT13_OP1,
.min_delay_ms = 100,
.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
.pll_set = samsung_tbmu24112_pll_set,
};
/* dvb-t mt352 */
static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
{
u32 div;
unsigned char bs = 0;
#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
pllbuf[0] = 0xc2; /* Note: non-linux standard PLL i2c address */
pllbuf[1] = div >> 8;
pllbuf[2] = div & 0xff;
pllbuf[3] = 0xcc;
pllbuf[4] = bs;
return 0;
}
static struct mt352_config samsung_tdtc9251dh0_config = {
.demod_address = 0x0f,
.demod_init = samsung_tdtc9251dh0_demod_init,
.pll_set = samsung_tdtc9251dh0_pll_set,
};
static int nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
struct flexcop_device *fc = fe->dvb->priv;
return request_firmware(fw, name, fc->dev);
}
static struct nxt2002_config samsung_tbmv_config = {
.demod_address = 0x0a,
.request_firmware = nxt2002_request_firmware,
};
static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
struct flexcop_device *fc = fe->dvb->priv;
div = (params->frequency + (125/2)) / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = (div >> 0) & 0xff;
buf[2] = 0x84 | ((div >> 10) & 0x60);
buf[3] = 0x80;
if (params->frequency < 1550000)
buf[3] |= 0x02;
if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
return -EIO;
return 0;
}
static struct mt312_config skystar23_samsung_tbdu18132_config = {
.demod_address = 0x0e,
.pll_set = skystar23_samsung_tbdu18132_pll_set,
};
static struct stv0297_config alps_tdee4_stv0297_config = {
.demod_address = 0x1c,
// .invert = 1,
// .pll_set = alps_tdee4_stv0297_pll_set,
};
/* try to figure out the frontend, each card/box can have on of the following list */
int flexcop_frontend_init(struct flexcop_device *fc)
{
/* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
fc->fe->ops->set_voltage = flexcop_set_voltage;
fc->fe_sleep = fc->fe->ops->sleep;
fc->fe->ops->sleep = flexcop_sleep;
fc->dev_type = FC_SKY;
info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
} else
/* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
fc->dev_type = FC_AIR_DVB;
info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
} else
/* try the air atsc (nxt2002) */
if ((fc->fe = nxt2002_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC;
info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
} else
/* try the cable dvb (stv0297) */
if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap, 0xf8)) != NULL) {
fc->dev_type = FC_CABLE;
info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
} else
/* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
if ((fc->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
fc->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
fc->fe->ops->diseqc_send_burst = flexcop_diseqc_send_burst;
fc->fe->ops->set_tone = flexcop_set_tone;
fc->fe->ops->set_voltage = flexcop_set_voltage;
fc->fe_sleep = fc->fe->ops->sleep;
fc->fe->ops->sleep = flexcop_sleep;
fc->dev_type = FC_SKY_OLD;
info("found the vp310 (aka mt312) at i2c address: 0x%02x",skystar23_samsung_tbdu18132_config.demod_address);
}
if (fc->fe == NULL) {
err("no frontend driver found for this B2C2/FlexCop adapter");
return -ENODEV;
} else {
if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
err("frontend registration failed!");
if (fc->fe->ops->release != NULL)
fc->fe->ops->release(fc->fe);
fc->fe = NULL;
return -EINVAL;
}
}
fc->init_state |= FC_STATE_FE_INIT;
return 0;
}
void flexcop_frontend_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_FE_INIT)
dvb_unregister_frontend(fc->fe);
fc->init_state &= ~FC_STATE_FE_INIT;
}

View File

@ -0,0 +1,204 @@
/*
* 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)
{
if (pid == 0x2000)
return;
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;
}
}
static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc,int onoff)
{
if (fc->fullts_streaming_state != onoff) {
deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling");
flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff));
flexcop_pid_group_filter_ctrl(fc,onoff);
fc->fullts_streaming_state = onoff;
}
return 0;
}
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;
if (dvbdmxfeed->index >= max_pid_filter)
fc->extra_feedcount += onoff ? 1 : -1;
/* toggle complete-TS-streaming when:
* - pid_filtering is not enabled and it is the first or last feed requested
* - pid_filtering is enabled,
* - but the number of requested feeds is exceeded
* - or the requested pid is 0x2000 */
if (!fc->pid_filtering && fc->feedcount == onoff)
flexcop_toggle_fullts_streaming(fc,onoff);
if (fc->pid_filtering) {
flexcop_pid_control(fc,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
if (fc->extra_feedcount > 0)
flexcop_toggle_fullts_streaming(fc,1);
else if (dvbdmxfeed->pid == 0x2000)
flexcop_toggle_fullts_streaming(fc,onoff);
else
flexcop_toggle_fullts_streaming(fc,0);
}
/* if it was the first or last feed request change the stream-status */
if (fc->feedcount == onoff) {
flexcop_rcv_data_ctrl(fc,onoff);
if (fc->stream_control)
fc->stream_control(fc,onoff);
}
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, 0, 0x1fe0);
flexcop_pid_group_filter_ctrl(fc,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);
flexcop_null_filter_ctrl(fc, 1);
}

View File

@ -0,0 +1,210 @@
/*
* 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 i;
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");
return -EREMOTEIO;
}
}
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)) != 0) {
/* The Cablestar needs a different kind of i2c-transfer (does not
* support "Repeat Start"):
* wait for the ACK failure,
* and do a subsequent read with the Bit 30 enabled
*/
r100.tw_sm_c_100.no_base_addr_ack_error = 1;
if ((ret = flexcop_i2c_operation(fc,&r100)) != 0) {
deb_i2c("no_base_addr read failed. %d\n",ret);
return ret;
}
}
buf[0] = r100.tw_sm_c_100.data1_reg;
if (len > 0) {
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 more byte, otherwise we wouldn't be here */
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);
}
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);
/* 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;
}
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;
}

View File

@ -0,0 +1,66 @@
/*
* 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);
}

View File

@ -0,0 +1,381 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-pci.c - covers the PCI part including DMA transfers.
*
* see flexcop.c for copyright information.
*/
#define FC_LOG_PREFIX "flexcop-pci"
#include "flexcop-common.h"
static int enable_pid_filtering = 1;
module_param(enable_pid_filtering, int, 0444);
MODULE_PARM_DESC(enable_pid_filtering, "enable hardware pid filtering: supported values: 0 (fullts), 1");
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((debug & level)) printk(args); } while (0)
#define DEBSTATUS ""
#else
#define dprintk(level,args...)
#define DEBSTATUS " (debugging is not enabled)"
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_reg(args...) dprintk(0x02,args)
#define deb_ts(args...) dprintk(0x04,args)
#define deb_irq(args...) dprintk(0x08,args)
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (1=info,2=regs,4=TS,8=irqdma (|-able))." DEBSTATUS);
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
struct flexcop_pci {
struct pci_dev *pdev;
#define FC_PCI_INIT 0x01
#define FC_PCI_DMA_INIT 0x02
int init_state;
void __iomem *io_mem;
u32 irq;
/* buffersize (at least for DMA1, need to be % 188 == 0,
* this logic is required */
#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
struct flexcop_dma dma[2];
int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
u32 last_dma1_cur_pos; /* position of the pointer last time the timer/packet irq occured */
int count;
spinlock_t irq_lock;
struct flexcop_device *fc_dev;
};
static int lastwreg,lastwval,lastrreg,lastrval;
static flexcop_ibi_value flexcop_pci_read_ibi_reg (struct flexcop_device *fc, flexcop_ibi_register r)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
flexcop_ibi_value v;
v.raw = readl(fc_pci->io_mem + r);
if (lastrreg != r || lastrval != v.raw) {
lastrreg = r; lastrval = v.raw;
deb_reg("new rd: %3x: %08x\n",r,v.raw);
}
return v;
}
static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register r, flexcop_ibi_value v)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (lastwreg != r || lastwval != v.raw) {
lastwreg = r; lastwval = v.raw;
deb_reg("new wr: %3x: %08x\n",r,v.raw);
}
writel(v.raw, fc_pci->io_mem + r);
return 0;
}
/* When PID filtering is turned on, we use the timer IRQ, because small amounts
* of data need to be passed to the user space instantly as well. When PID
* filtering is turned off, we use the page-change-IRQ */
static irqreturn_t flexcop_pci_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct flexcop_pci *fc_pci = dev_id;
struct flexcop_device *fc = fc_pci->fc_dev;
flexcop_ibi_value v = fc->read_ibi_reg(fc,irq_20c);
irqreturn_t ret = IRQ_HANDLED;
spin_lock_irq(&fc_pci->irq_lock);
if (v.irq_20c.DMA1_IRQ_Status == 1) {
if (fc_pci->active_dma1_addr == 0)
flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr0,fc_pci->dma[0].size / 188);
else
flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr1,fc_pci->dma[0].size / 188);
deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
} else if (v.irq_20c.DMA1_Timer_Status == 1) {
/* for the timer IRQ we only can use buffer dmx feeding, because we don't have
* complete TS packets when reading from the DMA memory */
dma_addr_t cur_addr =
fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
deb_irq("irq: %08x cur_addr: %08x: cur_pos: %08x, last_cur_pos: %08x ",
v.raw,cur_addr,cur_pos,fc_pci->last_dma1_cur_pos);
/* buffer end was reached, restarted from the beginning
* pass the data from last_cur_pos to the buffer end to the demux
*/
if (cur_pos < fc_pci->last_dma1_cur_pos) {
deb_irq(" end was reached: passing %d bytes ",(fc_pci->dma[0].size*2 - 1) - fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
(fc_pci->dma[0].size*2) - fc_pci->last_dma1_cur_pos);
fc_pci->last_dma1_cur_pos = 0;
fc_pci->count = 0;
}
if (cur_pos > fc_pci->last_dma1_cur_pos) {
deb_irq(" passing %d bytes ",cur_pos - fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
cur_pos - fc_pci->last_dma1_cur_pos);
}
deb_irq("\n");
fc_pci->last_dma1_cur_pos = cur_pos;
} else
ret = IRQ_NONE;
spin_unlock_irq(&fc_pci->irq_lock);
/* packet count would be ideal for hw filtering, but it isn't working. Either
* the data book is wrong, or I'm unable to read it correctly */
/* if (v.irq_20c.DMA1_Size_IRQ_Status == 1) { packet counter */
return ret;
}
static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (onoff) {
flexcop_dma_config(fc,&fc_pci->dma[0],FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1);
flexcop_dma_config(fc,&fc_pci->dma[1],FC_DMA_2,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1);
flexcop_dma_config_timer(fc,FC_DMA_1,1);
if (fc_pci->fc_dev->pid_filtering) {
fc_pci->last_dma1_cur_pos = 0;
flexcop_dma_control_timer_irq(fc,FC_DMA_1,1);
} else {
fc_pci->active_dma1_addr = 0;
flexcop_dma_control_size_irq(fc,FC_DMA_1,1);
}
/* flexcop_dma_config_packet_count(fc,FC_DMA_1,0xc0);
flexcop_dma_control_packet_irq(fc,FC_DMA_1,1); */
deb_irq("irqs enabled\n");
} else {
if (fc_pci->fc_dev->pid_filtering)
flexcop_dma_control_timer_irq(fc,FC_DMA_1,0);
else
flexcop_dma_control_size_irq(fc,FC_DMA_1,0);
// flexcop_dma_control_packet_irq(fc,FC_DMA_1,0);
deb_irq("irqs disabled\n");
}
return 0;
}
static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
{
int ret;
if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[0],FC_DEFAULT_DMA1_BUFSIZE)) != 0)
return ret;
if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0)
goto dma1_free;
flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
fc_pci->init_state |= FC_PCI_DMA_INIT;
goto success;
dma1_free:
flexcop_dma_free(&fc_pci->dma[0]);
success:
return ret;
}
static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_DMA_INIT) {
flexcop_dma_free(&fc_pci->dma[0]);
flexcop_dma_free(&fc_pci->dma[1]);
}
fc_pci->init_state &= ~FC_PCI_DMA_INIT;
}
static int flexcop_pci_init(struct flexcop_pci *fc_pci)
{
int ret;
u8 card_rev;
pci_read_config_byte(fc_pci->pdev, PCI_CLASS_REVISION, &card_rev);
info("card revision %x", card_rev);
if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
return ret;
pci_set_master(fc_pci->pdev);
/* enable interrupts */
// pci_write_config_dword(pdev, 0x6c, 0x8000);
if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
goto err_pci_disable_device;
fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);
if (!fc_pci->io_mem) {
err("cannot map io memory\n");
ret = -EIO;
goto err_pci_release_regions;
}
pci_set_drvdata(fc_pci->pdev, fc_pci);
if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_irq,
SA_SHIRQ, DRIVER_NAME, fc_pci)) != 0)
goto err_pci_iounmap;
spin_lock_init(&fc_pci->irq_lock);
fc_pci->init_state |= FC_PCI_INIT;
goto success;
err_pci_iounmap:
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
pci_set_drvdata(fc_pci->pdev, NULL);
err_pci_release_regions:
pci_release_regions(fc_pci->pdev);
err_pci_disable_device:
pci_disable_device(fc_pci->pdev);
success:
return ret;
}
static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_INIT) {
free_irq(fc_pci->pdev->irq, fc_pci);
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
pci_set_drvdata(fc_pci->pdev, NULL);
pci_release_regions(fc_pci->pdev);
pci_disable_device(fc_pci->pdev);
}
fc_pci->init_state &= ~FC_PCI_INIT;
}
static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct flexcop_device *fc;
struct flexcop_pci *fc_pci;
int ret = -ENOMEM;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_pci = fc->bus_specific;
fc_pci->fc_dev = fc;
fc->read_ibi_reg = flexcop_pci_read_ibi_reg;
fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
fc->i2c_request = flexcop_i2c_request;
fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
fc->stream_control = flexcop_pci_stream_control;
if (enable_pid_filtering)
info("will use the HW PID filter.");
else
info("will pass the complete TS to the demuxer.");
fc->pid_filtering = enable_pid_filtering;
fc->bus_type = FC_PCI;
fc->dev = &pdev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_pci->pdev = pdev;
if ((ret = flexcop_pci_init(fc_pci)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_pci_exit;
/* init dma */
if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
goto err_fc_exit;
goto success;
err_fc_exit:
flexcop_device_exit(fc);
err_pci_exit:
flexcop_pci_exit(fc_pci);
err_kfree:
flexcop_device_kfree(fc);
success:
return ret;
}
/* in theory every _exit function should be called exactly two times,
* here and in the bail-out-part of the _init-function
*/
static void flexcop_pci_remove(struct pci_dev *pdev)
{
struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
flexcop_pci_dma_exit(fc_pci);
flexcop_device_exit(fc_pci->fc_dev);
flexcop_pci_exit(fc_pci);
flexcop_device_kfree(fc_pci->fc_dev);
}
static struct pci_device_id flexcop_pci_tbl[] = {
{ PCI_DEVICE(0x13d0, 0x2103) },
/* { PCI_DEVICE(0x13d0, 0x2200) }, PCI FlexCopIII ? */
{ },
};
MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);
static struct pci_driver flexcop_pci_driver = {
.name = "Technisat/B2C2 FlexCop II/IIb/III PCI",
.id_table = flexcop_pci_tbl,
.probe = flexcop_pci_probe,
.remove = flexcop_pci_remove,
};
static int __init flexcop_pci_module_init(void)
{
return pci_register_driver(&flexcop_pci_driver);
}
static void __exit flexcop_pci_module_exit(void)
{
pci_unregister_driver(&flexcop_pci_driver);
}
module_init(flexcop_pci_module_init);
module_exit(flexcop_pci_module_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,701 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_REG_H__
#define __FLEXCOP_REG_H__
typedef enum {
FLEXCOP_UNK = 0,
FLEXCOP_II,
FLEXCOP_IIB,
FLEXCOP_III,
} flexcop_revision_t;
extern const char *flexcop_revision_names[];
typedef enum {
FC_UNK = 0,
FC_AIR_DVB,
FC_AIR_ATSC,
FC_SKY,
FC_SKY_OLD,
FC_CABLE,
} flexcop_device_type_t;
typedef enum {
FC_USB = 0,
FC_PCI,
} flexcop_bus_t;
extern const char *flexcop_device_names[];
/* FlexCop IBI Registers */
/* flexcop_ibi_reg - a huge union representing the register structure */
typedef union {
u32 raw;
/* DMA 0x000 to 0x01c
* DMA1 0x000 to 0x00c
* DMA2 0x010 to 0x01c
*/
struct {
u32 dma_0start : 1; /* set: data will be delivered to dma1_address0 */
u32 dma_0No_update : 1; /* set: dma1_cur_address will be updated, unset: no update */
u32 dma_address0 :30; /* physical/virtual host memory address0 DMA */
} dma_0x0;
struct {
u32 DMA_maxpackets : 8; /* (remapped) PCI DMA1 Packet Count Interrupt. This variable
is able to be read and written while bit(1) of register
0x00c (remap_enable) is set. This variable represents
the number of packets that will be transmitted to the PCI
host using PCI DMA1 before an interrupt to the PCI is
asserted. This functionality may be enabled using bit(20)
of register 0x208. N=0 disables the IRQ. */
u32 dma_addr_size :24; /* size of memory buffer in DWORDs (bytesize / 4) for DMA */
} dma_0x4_remap;
struct {
u32 dma1timer : 7; /* reading PCI DMA1 timer ... when remap_enable is 0 */
u32 unused : 1;
u32 dma_addr_size :24;
} dma_0x4_read;
struct {
u32 unused : 1;
u32 dmatimer : 7; /* writing PCI DMA1 timer ... when remap_enable is 0 */
u32 dma_addr_size :24;
} dma_0x4_write;
struct {
u32 unused : 2;
u32 dma_cur_addr :30; /* current physical host memory address pointer for DMA */
} dma_0x8;
struct {
u32 dma_1start : 1; /* set: data will be delivered to dma_address1, when dma_address0 is full */
u32 remap_enable : 1; /* remap enable for 0x0x4(7:0) */
u32 dma_address1 :30; /* Physical/virtual address 1 on DMA */
} dma_0xc;
/* Two-wire Serial Master and Clock 0x100-0x110 */
struct {
// u32 slave_transmitter : 1; /* ???*/
u32 chipaddr : 7; /* two-line serial address of the target slave */
u32 reserved1 : 1;
u32 baseaddr : 8; /* address of the location of the read/write operation */
u32 data1_reg : 8; /* first byte in two-line serial read/write operation */
u32 working_start : 1; /* when doing a write operation this indicator is 0 when ready
* set to 1 when doing a write operation */
u32 twoWS_rw : 1; /* read/write indicator (1 = read, 0 write) */
u32 total_bytes : 2; /* number of data bytes in each two-line serial transaction (0 = 1 byte, 11 = 4byte)*/
u32 twoWS_port_reg : 2; /* port selection: 01 - Front End/Demod, 10 - EEPROM, 11 - Tuner */
u32 no_base_addr_ack_error : 1; /* writing: write-req: frame is produced w/o baseaddr, read-req: read-cycles w/o
* preceding address assignment write frame
* ACK_ERROR = 1 when no ACK from slave in the last transaction */
u32 st_done : 1; /* indicator for transaction is done */
} tw_sm_c_100;
struct {
u32 data2_reg : 8; /* 2nd data byte */
u32 data3_reg : 8; /* 3rd data byte */
u32 data4_reg : 8; /* 4th data byte */
u32 exlicit_stops : 1; /* when set, transactions are produced w/o trailing STOP flag, then send isolated STOP flags */
u32 force_stop : 1; /* isolated stop flag */
u32 unused : 6;
} tw_sm_c_104;
/* Clock. The register allows the FCIII to convert an incoming Master clock
* (MCLK) signal into a lower frequency clock through the use of a LowCounter
* (TLO) and a High- Counter (THI). The time counts for THI and TLO are
* measured in MCLK; each count represents 4 MCLK input clock cycles.
*
* The default output for port #1 is set for Front End Demod communication. (0x108)
* The default output for port #2 is set for EEPROM communication. (0x10c)
* The default output for port #3 is set for Tuner communication. (0x110)
*/
struct {
u32 thi1 : 6; /* Thi for port #1 (def: 100110b; 38) */
u32 reserved1 : 2;
u32 tlo1 : 5; /* Tlo for port #1 (def: 11100b; 28) */
u32 reserved2 :19;
} tw_sm_c_108;
struct {
u32 thi1 : 6; /* Thi for port #2 (def: 111001b; 57) */
u32 reserved1 : 2;
u32 tlo1 : 5; /* Tlo for port #2 (def: 11100b; 28) */
u32 reserved2 :19;
} tw_sm_c_10c;
struct {
u32 thi1 : 6; /* Thi for port #3 (def: 111001b; 57) */
u32 reserved1 : 2;
u32 tlo1 : 5; /* Tlo for port #3 (def: 11100b; 28) */
u32 reserved2 :19;
} tw_sm_c_110;
/* LNB Switch Frequency 0x200
* Clock that creates the LNB switch tone. The default is set to have a fixed
* low output (not oscillating) to the LNB_CTL line.
*/
struct {
u32 LNB_CTLHighCount_sig :15; /* It is the number of pre-scaled clock cycles that will be low. */
u32 LNB_CTLLowCount_sig :15; /* For example, to obtain a 22KHz output given a 45 Mhz Master
Clock signal (MCLK), set PreScalar=01 and LowCounter value to 0x1ff. */
u32 LNB_CTLPrescaler_sig : 2; /* pre-scaler divides MCLK: 00 (no division), 01 by 2, 10 by 4, 11 by 12 */
} lnb_switch_freq_200;
/* ACPI, Peripheral Reset, LNB Polarity
* ACPI power conservation mode, LNB polarity selection (low or high voltage),
* and peripheral reset.
*/
struct {
u32 ACPI1_sig : 1; /* turn of the power of tuner and LNB, not implemented in FCIII */
u32 ACPI3_sig : 1; /* turn of power of the complete satelite receiver board (except FCIII) */
u32 LNB_L_H_sig : 1; /* low or high voltage for LNB. (0 = low, 1 = high) */
u32 Per_reset_sig : 1; /* misc. init reset (default: 1), to reset set to low and back to high */
u32 reserved :20;
u32 Rev_N_sig_revision_hi : 4;/* 0xc in case of FCIII */
u32 Rev_N_sig_reserved1 : 2;
u32 Rev_N_sig_caps : 1; /* if 1, FCIII has 32 PID- and MAC-filters and is capable of IP multicast */
u32 Rev_N_sig_reserved2 : 1;
} misc_204;
/* Control and Status 0x208 to 0x21c */
/* Gross enable and disable control */
struct {
u32 Stream1_filter_sig : 1; /* Stream1 PID filtering */
u32 Stream2_filter_sig : 1; /* Stream2 PID filtering */
u32 PCR_filter_sig : 1; /* PCR PID filter */
u32 PMT_filter_sig : 1; /* PMT PID filter */
u32 EMM_filter_sig : 1; /* EMM PID filter */
u32 ECM_filter_sig : 1; /* ECM PID filter */
u32 Null_filter_sig : 1; /* Filters null packets, PID=0x1fff. */
u32 Mask_filter_sig : 1; /* mask PID filter */
u32 WAN_Enable_sig : 1; /* WAN output line through V8 memory space is activated. */
u32 WAN_CA_Enable_sig : 1; /* not in FCIII */
u32 CA_Enable_sig : 1; /* not in FCIII */
u32 SMC_Enable_sig : 1; /* CI stream data (CAI) goes directly to the smart card intf (opposed IBI 0x600 or SC-cmd buf). */
u32 Per_CA_Enable_sig : 1; /* not in FCIII */
u32 Multi2_Enable_sig : 1; /* ? */
u32 MAC_filter_Mode_sig : 1; /* (MAC_filter_enable) Globally enables MAC filters for Net PID filteres. */
u32 Rcv_Data_sig : 1; /* PID filtering module enable. When this bit is a one, the PID filter will
examine and process packets according to all other (individual) PID
filtering controls. If it a zero, no packet processing of any kind will
take place. All data from the tuner will be thrown away. */
u32 DMA1_IRQ_Enable_sig : 1; /* When set, a DWORD counter is enabled on PCI DMA1 that asserts the PCI
* interrupt after the specified count for filling the buffer. */
u32 DMA1_Timer_Enable_sig : 1; /* When set, a timer is enabled on PCI DMA1 that asserts the PCI interrupt
after a specified amount of time. */
u32 DMA2_IRQ_Enable_sig : 1; /* same as DMA1_IRQ_Enable_sig but for DMA2 */
u32 DMA2_Timer_Enable_sig : 1; /* same as DMA1_Timer_Enable_sig but for DMA2 */
u32 DMA1_Size_IRQ_Enable_sig : 1; /* When set, a packet count detector is enabled on PCI DMA1 that asserts the PCI interrupt. */
u32 DMA2_Size_IRQ_Enable_sig : 1; /* When set, a packet count detector is enabled on PCI DMA2 that asserts the PCI interrupt. */
u32 Mailbox_from_V8_Enable_sig: 1; /* When set, writes to the mailbox register produce an interrupt to the
PCI host to indicate that mailbox data is available. */
u32 unused : 9;
} ctrl_208;
/* General status. When a PCI interrupt occurs, this register is read to
* discover the reason for the interrupt.
*/
struct {
u32 DMA1_IRQ_Status : 1; /* When set(1) the DMA1 counter had generated an IRQ. Read Only. */
u32 DMA1_Timer_Status : 1; /* When set(1) the DMA1 timer had generated an IRQ. Read Only. */
u32 DMA2_IRQ_Status : 1; /* When set(1) the DMA2 counter had generated an IRQ. Read Only. */
u32 DMA2_Timer_Status : 1; /* When set(1) the DMA2 timer had generated an IRQ. Read Only. */
u32 DMA1_Size_IRQ_Status : 1; /* (Read only). This register is read after an interrupt to */
u32 DMA2_Size_IRQ_Status : 1; /* find out why we had an IRQ. Reading this register will clear this bit. Packet count*/
u32 Mailbox_from_V8_Status_sig: 1; /* Same as above. Reading this register will clear this bit. */
u32 Data_receiver_error : 1; /* 1 indicate an error in the receiver Front End (Tuner module) */
u32 Continuity_error_flag : 1; /* 1 indicates a continuity error in the TS stream. */
u32 LLC_SNAP_FLAG_set : 1; /* 1 indicates that the LCC_SNAP_FLAG was set. */
u32 Transport_Error : 1; /* When set indicates that an unexpected packet was received. */
u32 reserved :21;
} irq_20c;
/* Software reset register */
struct {
u32 reset_blocks : 8; /* Enabled when Block_reset_enable = 0xB2 and 0x208 bits 15:8 = 0x00.
Each bit location represents a 0x100 block of registers. Writing
a one in a bit location resets that block of registers and the logic
that it controls. */
u32 Block_reset_enable : 8; /* This variable is set to 0xB2 when the register is written. */
u32 Special_controls :16; /* Asserts Reset_V8 => 0xC258; Turns on pci encryption => 0xC25A;
Turns off pci encryption => 0xC259 Note: pci_encryption default
at power-up is ON. */
} sw_reset_210;
struct {
u32 vuart_oe_sig : 1; /* When clear, the V8 processor has sole control of the serial UART
(RS-232 Smart Card interface). When set, the IBI interface
defined by register 0x600 controls the serial UART. */
u32 v2WS_oe_sig : 1; /* When clear, the V8 processor has direct control of the Two-line
Serial Master EEPROM target. When set, the Two-line Serial Master
EEPROM target interface is controlled by IBI register 0x100. */
u32 halt_V8_sig : 1; /* When set, contiguous wait states are applied to the V8-space
bus masters. Once this signal is cleared, normal V8-space
operations resume. */
u32 section_pkg_enable_sig: 1; /* When set, this signal enables the front end translation circuitry
to process section packed transport streams. */
u32 s2p_sel_sig : 1; /* Serial to parallel conversion. When set, polarized transport data
within the FlexCop3 front end circuitry is converted from a serial
stream into parallel data before downstream processing otherwise
interprets the data. */
u32 unused1 : 3;
u32 polarity_PS_CLK_sig: 1; /* This signal is used to invert the input polarity of the tranport
stream CLOCK signal before any processing occurs on the transport
stream within FlexCop3. */
u32 polarity_PS_VALID_sig: 1; /* This signal is used to invert the input polarity of the tranport
stream VALID signal before any processing occurs on the transport
stream within FlexCop3. */
u32 polarity_PS_SYNC_sig: 1; /* This signal is used to invert the input polarity of the tranport
stream SYNC signal before any processing occurs on the transport
stream within FlexCop3. */
u32 polarity_PS_ERR_sig: 1; /* This signal is used to invert the input polarity of the tranport
stream ERROR signal before any processing occurs on the transport
stream within FlexCop3. */
u32 unused2 :20;
} misc_214;
/* Mailbox from V8 to host */
struct {
u32 Mailbox_from_V8 :32; /* When this register is written by either the V8 processor or by an
end host, an interrupt is generated to the PCI host to indicate
that mailbox data is available. Reading register 20c will clear
the IRQ. */
} mbox_v8_to_host_218;
/* Mailbox from host to v8 Mailbox_to_V8
* Mailbox_to_V8 mailbox storage register
* used to send messages from PCI to V8. Writing to this register will send an
* IRQ to the V8. Then it can read the data from here. Reading this register
* will clear the IRQ. If the V8 is halted and bit 31 of this register is set,
* then this register is used instead as a direct interface to access the
* V8space memory.
*/
struct {
u32 sysramaccess_data : 8; /* Data byte written or read from the specified address in V8 SysRAM. */
u32 sysramaccess_addr :15; /* 15 bit address used to access V8 Sys-RAM. */
u32 unused : 7;
u32 sysramaccess_write: 1; /* Write flag used to latch data into the V8 SysRAM. */
u32 sysramaccess_busmuster: 1; /* Setting this bit when the V8 is halted at 0x214 Bit(2) allows
this IBI register interface to directly drive the V8-space memory. */
} mbox_host_to_v8_21c;
/* PIDs, Translation Bit, SMC Filter Select 0x300 to 0x31c */
struct {
u32 Stream1_PID :13; /* Primary use is receiving Net data, so these 13 bits normally
hold the PID value for the desired network stream. */
u32 Stream1_trans : 1; /* When set, Net translation will take place for Net data ferried in TS packets. */
u32 MAC_Multicast_filter : 1; /* When clear, multicast MAC filtering is not allowed for Stream1 and PID_n filters. */
u32 debug_flag_pid_saved : 1;
u32 Stream2_PID :13; /* 13 bits for Stream 2 PID filter value. General use. */
u32 Stream2_trans : 1; /* When set Tables/CAI translation will take place for the data ferried in
Stream2_PID TS packets. */
u32 debug_flag_write_status00 : 1;
u32 debug_fifo_problem : 1;
} pid_filter_300;
struct {
u32 PCR_PID :13; /* PCR stream PID filter value. Primary use is Program Clock Reference stream filtering. */
u32 PCR_trans : 1; /* When set, Tables/CAI translation will take place for these packets. */
u32 debug_overrun3 : 1;
u32 debug_overrun2 : 1;
u32 PMT_PID :13; /* stream PID filter value. Primary use is Program Management Table segment filtering. */
u32 PMT_trans : 1; /* When set, Tables/CAI translation will take place for these packets. */
u32 reserved : 2;
} pid_filter_304;
struct {
u32 EMM_PID :13; /* EMM PID filter value. Primary use is Entitlement Management Messaging for
conditional access-related data. */
u32 EMM_trans : 1; /* When set, Tables/CAI translation will take place for these packets. */
u32 EMM_filter_4 : 1; /* When set will pass only EMM data possessing the same ID code as the
first four bytes (32 bits) of the end-user s 6-byte Smart Card ID number Select */
u32 EMM_filter_6 : 1; /* When set will pass only EMM data possessing the same 6-byte code as the end-users
complete 6-byte Smart Card ID number. */
u32 ECM_PID :13; /* ECM PID filter value. Primary use is Entitlement Control Messaging for conditional
access-related data. */
u32 ECM_trans : 1; /* When set, Tables/CAI translation will take place for these packets. */
u32 reserved : 2;
} pid_filter_308;
struct {
u32 Group_PID :13; /* PID value for group filtering. */
u32 Group_trans : 1; /* When set, Tables/CAI translation will take place for these packets. */
u32 unused1 : 2;
u32 Group_mask :13; /* Mask value used in logical "and" equation that defines group filtering */
u32 unused2 : 3;
} pid_filter_30c_ext_ind_0_7;
struct {
u32 net_master_read :17;
u32 unused :15;
} pid_filter_30c_ext_ind_1;
struct {
u32 net_master_write :17;
u32 unused :15;
} pid_filter_30c_ext_ind_2;
struct {
u32 next_net_master_write :17;
u32 unused :15;
} pid_filter_30c_ext_ind_3;
struct {
u32 unused1 : 1;
u32 state_write :10;
u32 reserved1 : 6; /* default: 000100 */
u32 stack_read :10;
u32 reserved2 : 5; /* default: 00100 */
} pid_filter_30c_ext_ind_4;
struct {
u32 stack_cnt :10;
u32 unused :22;
} pid_filter_30c_ext_ind_5;
struct {
u32 pid_fsm_save_reg0 : 2;
u32 pid_fsm_save_reg1 : 2;
u32 pid_fsm_save_reg2 : 2;
u32 pid_fsm_save_reg3 : 2;
u32 pid_fsm_save_reg4 : 2;
u32 pid_fsm_save_reg300 : 2;
u32 write_status1 : 2;
u32 write_status4 : 2;
u32 data_size_reg :12;
u32 unused : 4;
} pid_filter_30c_ext_ind_6;
struct {
u32 index_reg : 5; /* (Index pointer) Points at an internal PIDn register. A binary code
representing one of 32 internal PIDn registers as well as its
corresponding internal MAC_lown register. */
u32 extra_index_reg : 3; /* This vector is used to select between sets of debug signals routed to register 0x30c. */
u32 AB_select : 1; /* Used in conjunction with 0x31c. read/write to the MAC_highA or MAC_highB register
0=MAC_highB register, 1=MAC_highA */
u32 pass_alltables : 1; /* 1=Net packets are not filtered against the Network Table ID found in register 0x400.
All types of networks (DVB, ATSC, ISDB) are passed. */
u32 unused :22;
} index_reg_310;
struct {
u32 PID :13; /* PID value */
u32 PID_trans : 1; /* translation will take place for packets filtered */
u32 PID_enable_bit : 1; /* When set this PID filter is enabled */
u32 reserved :17;
} pid_n_reg_314;
struct {
u32 A4_byte : 8;
u32 A5_byte : 8;
u32 A6_byte : 8;
u32 Enable_bit : 1; /* enabled (1) or disabled (1) */
u32 HighAB_bit : 1; /* use MAC_highA (1) or MAC_highB (0) as MSB */
u32 reserved : 6;
} mac_low_reg_318;
struct {
u32 A1_byte : 8;
u32 A2_byte : 8;
u32 A3_byte : 8;
u32 reserved : 8;
} mac_high_reg_31c;
/* Table, SMCID,MACDestination Filters 0x400 to 0x41c */
struct {
u32 reserved :16;
#define fc_data_Tag_ID_DVB 0x3e
#define fc_data_Tag_ID_ATSC 0x3f
#define fc_data_Tag_ID_IDSB 0x8b
u32 data_Tag_ID :16;
} data_tag_400;
struct {
u32 Card_IDbyte6 : 8;
u32 Card_IDbyte5 : 8;
u32 Card_IDbyte4 : 8;
u32 Card_IDbyte3 : 8;
} card_id_408;
struct {
u32 Card_IDbyte2 : 8;
u32 Card_IDbyte1 : 8;
} card_id_40c;
/* holding the unique mac address of the receiver which houses the FlexCopIII */
struct {
u32 MAC1 : 8;
u32 MAC2 : 8;
u32 MAC3 : 8;
u32 MAC6 : 8;
} mac_address_418;
struct {
u32 MAC7 : 8;
u32 MAC8 : 8;
u32 reserved : 16;
} mac_address_41c;
struct {
u32 transmitter_data_byte : 8;
u32 ReceiveDataReady : 1;
u32 ReceiveByteFrameError: 1;
u32 txbuffempty : 1;
u32 reserved :21;
} ci_600;
struct {
u32 pi_d : 8;
u32 pi_ha :20;
u32 pi_rw : 1;
u32 pi_component_reg : 3;
} pi_604;
struct {
u32 serialReset : 1;
u32 oncecycle_read : 1;
u32 Timer_Read_req : 1;
u32 Timer_Load_req : 1;
u32 timer_data : 7;
u32 unused : 1; /* ??? not mentioned in data book */
u32 Timer_addr : 5;
u32 reserved : 3;
u32 pcmcia_a_mod_pwr_n : 1;
u32 pcmcia_b_mod_pwr_n : 1;
u32 config_Done_stat : 1;
u32 config_Init_stat : 1;
u32 config_Prog_n : 1;
u32 config_wr_n : 1;
u32 config_cs_n : 1;
u32 config_cclk : 1;
u32 pi_CiMax_IRQ_n : 1;
u32 pi_timeout_status : 1;
u32 pi_wait_n : 1;
u32 pi_busy_n : 1;
} pi_608;
struct {
u32 PID :13;
u32 key_enable : 1;
#define fc_key_code_default 0x1
#define fc_key_code_even 0x2
#define fc_key_code_odd 0x3
u32 key_code : 2;
u32 key_array_col : 3;
u32 key_array_row : 5;
u32 dvb_en : 1; /* 0=TS bypasses the Descrambler */
u32 rw_flag : 1;
u32 reserved : 6;
} dvb_reg_60c;
/* SRAM and Output Destination 0x700 to 0x714 */
struct {
u32 sram_addr :15;
u32 sram_rw : 1; /* 0=write, 1=read */
u32 sram_data : 8;
u32 sc_xfer_bit : 1;
u32 reserved1 : 3;
u32 oe_pin_reg : 1;
u32 ce_pin_reg : 1;
u32 reserved2 : 1;
u32 start_sram_ibi : 1;
} sram_ctrl_reg_700;
struct {
u32 net_addr_read :16;
u32 net_addr_write :16;
} net_buf_reg_704;
struct {
u32 cai_read :11;
u32 reserved1 : 5;
u32 cai_write :11;
u32 reserved2 : 6;
u32 cai_cnt : 4;
} cai_buf_reg_708;
struct {
u32 cao_read :11;
u32 reserved1 : 5;
u32 cap_write :11;
u32 reserved2 : 6;
u32 cao_cnt : 4;
} cao_buf_reg_70c;
struct {
u32 media_read :11;
u32 reserved1 : 5;
u32 media_write :11;
u32 reserved2 : 6;
u32 media_cnt : 4;
} media_buf_reg_710;
struct {
u32 NET_Dest : 2;
u32 CAI_Dest : 2;
u32 CAO_Dest : 2;
u32 MEDIA_Dest : 2;
u32 net_ovflow_error : 1;
u32 media_ovflow_error : 1;
u32 cai_ovflow_error : 1;
u32 cao_ovflow_error : 1;
u32 ctrl_usb_wan : 1;
u32 ctrl_sramdma : 1;
u32 ctrl_maximumfill : 1;
u32 reserved :17;
} sram_dest_reg_714;
struct {
u32 net_cnt :12;
u32 reserved1 : 4;
u32 net_addr_read : 1;
u32 reserved2 : 3;
u32 net_addr_write : 1;
u32 reserved3 :11;
} net_buf_reg_718;
struct {
u32 wan_speed_sig : 2;
u32 reserved1 : 6;
u32 wan_wait_state : 8;
u32 sram_chip : 2;
u32 sram_memmap : 2;
u32 reserved2 : 4;
u32 wan_pkt_frame : 4;
u32 reserved3 : 4;
} wan_ctrl_reg_71c;
} flexcop_ibi_value;
extern flexcop_ibi_value ibi_zero;
typedef enum {
FC_I2C_PORT_DEMOD = 1,
FC_I2C_PORT_EEPROM = 2,
FC_I2C_PORT_TUNER = 3,
} flexcop_i2c_port_t;
typedef enum {
FC_WRITE = 0,
FC_READ = 1,
} flexcop_access_op_t;
typedef enum {
FC_SRAM_DEST_NET = 1,
FC_SRAM_DEST_CAI = 2,
FC_SRAM_DEST_CAO = 4,
FC_SRAM_DEST_MEDIA = 8
} flexcop_sram_dest_t;
typedef enum {
FC_SRAM_DEST_TARGET_WAN_USB = 0,
FC_SRAM_DEST_TARGET_DMA1 = 1,
FC_SRAM_DEST_TARGET_DMA2 = 2,
FC_SRAM_DEST_TARGET_FC3_CA = 3
} flexcop_sram_dest_target_t;
typedef enum {
FC_SRAM_2_32KB = 0, /* 64KB */
FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */
FC_SRAM_1_128KB = 2, /* 128KB */
FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */
} flexcop_sram_type_t;
typedef enum {
FC_WAN_SPEED_4MBITS = 0,
FC_WAN_SPEED_8MBITS = 1,
FC_WAN_SPEED_12MBITS = 2,
FC_WAN_SPEED_16MBITS = 3,
} flexcop_wan_speed_t;
typedef enum {
FC_DMA_1 = 1,
FC_DMA_2 = 2,
} flexcop_dma_index_t;
typedef enum {
FC_DMA_SUBADDR_0 = 1,
FC_DMA_SUBADDR_1 = 2,
} flexcop_dma_addr_index_t;
/* names of the particular registers */
typedef enum {
dma1_000 = 0x000,
dma1_004 = 0x004,
dma1_008 = 0x008,
dma1_00c = 0x00c,
dma2_010 = 0x010,
dma2_014 = 0x014,
dma2_018 = 0x018,
dma2_01c = 0x01c,
tw_sm_c_100 = 0x100,
tw_sm_c_104 = 0x104,
tw_sm_c_108 = 0x108,
tw_sm_c_10c = 0x10c,
tw_sm_c_110 = 0x110,
lnb_switch_freq_200 = 0x200,
misc_204 = 0x204,
ctrl_208 = 0x208,
irq_20c = 0x20c,
sw_reset_210 = 0x210,
misc_214 = 0x214,
mbox_v8_to_host_218 = 0x218,
mbox_host_to_v8_21c = 0x21c,
pid_filter_300 = 0x300,
pid_filter_304 = 0x304,
pid_filter_308 = 0x308,
pid_filter_30c = 0x30c,
index_reg_310 = 0x310,
pid_n_reg_314 = 0x314,
mac_low_reg_318 = 0x318,
mac_high_reg_31c = 0x31c,
data_tag_400 = 0x400,
card_id_408 = 0x408,
card_id_40c = 0x40c,
mac_address_418 = 0x418,
mac_address_41c = 0x41c,
ci_600 = 0x600,
pi_604 = 0x604,
pi_608 = 0x608,
dvb_reg_60c = 0x60c,
sram_ctrl_reg_700 = 0x700,
net_buf_reg_704 = 0x704,
cai_buf_reg_708 = 0x708,
cao_buf_reg_70c = 0x70c,
media_buf_reg_710 = 0x710,
sram_dest_reg_714 = 0x714,
net_buf_reg_718 = 0x718,
wan_ctrl_reg_71c = 0x71c,
} flexcop_ibi_register;
#define flexcop_set_ibi_value(reg,attr,val) { \
flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \
v.reg.attr = val; \
fc->write_ibi_reg(fc,reg,v); \
}
#endif

View File

@ -0,0 +1,403 @@
/*
* 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

View File

@ -0,0 +1,577 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-usb.c - covers the USB part.
*
* see flexcop.c for copyright information.
*/
#define FC_LOG_PREFIX "flexcop_usb"
#include "flexcop-usb.h"
#include "flexcop-common.h"
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((debug & level)) { printk(args); } } while (0)
#define debug_dump(b,l,method) {\
int i; \
for (i = 0; i < l; i++) method("%02x ", b[i]); \
method("\n");\
}
#define DEBSTATUS ""
#else
#define dprintk(level,args...)
#define debug_dump(b,l,method)
#define DEBSTATUS " (debugging is not enabled)"
#endif
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
#undef DEBSTATUS
#define deb_info(args...) dprintk(0x01,args)
#define deb_ts(args...) dprintk(0x02,args)
#define deb_ctrl(args...) dprintk(0x04,args)
#define deb_i2c(args...) dprintk(0x08,args)
#define deb_v8(args...) dprintk(0x10,args)
/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
* in the IBI address, to make the V8 code simpler.
* PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
* in general: 0000 0HHH 000L LL00
* IBI ADDRESS FORMAT: RHHH BLLL
*
* where R is the read(1)/write(0) bit, B is the busy bit
* and HHH and LLL are the two sets of three bits from the PCI address.
*/
#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
/*
* DKT 020228
* - forget about this VENDOR_BUFFER_SIZE, read and write register
* deal with DWORD or 4 bytes, that should be should from now on
* - from now on, we don't support anything older than firm 1.00
* I eliminated the write register as a 2 trip of writing hi word and lo word
* and force this to write only 4 bytes at a time.
* NOTE: this should work with all the firmware from 1.00 and newer
*/
static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
{
struct flexcop_usb *fc_usb = fc->bus_specific;
u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | (read ? 0x80 : 0);
int len = usb_control_msg(fc_usb->udev,
read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
request,
request_type, /* 0xc0 read or 0x40 write*/
wAddress,
0,
val,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * HZ);
if (len != sizeof(u32)) {
err("error while %s dword from %d (%d).",read ? "reading" : "writing",
wAddress,wRegOffsPCI);
return -EIO;
}
return 0;
}
/*
* DKT 010817 - add support for V8 memory read/write and flash update
*/
static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, u8 page, u16 wAddress,
u8 *pbBuffer,u32 buflen)
{
// u8 dwRequestType;
u8 request_type = USB_TYPE_VENDOR;
u16 wIndex;
int nWaitTime,pipe,len;
wIndex = page << 8;
switch (req) {
case B2C2_USB_READ_V8_MEM:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
request_type |= USB_DIR_IN;
// dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_IN;
break;
case B2C2_USB_WRITE_V8_MEM:
wIndex |= pbBuffer[0];
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
// dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
case B2C2_USB_FLASH_BLOCK:
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
// dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
default:
deb_info("unsupported request for v8_mem_req %x.\n",req);
return -EINVAL;
}
deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n",request_type,req,
wAddress,wIndex,buflen);
len = usb_control_msg(fc_usb->udev,pipe,
req,
request_type,
wAddress,
wIndex,
pbBuffer,
buflen,
nWaitTime * HZ);
debug_dump(pbBuffer,len,deb_v8);
return len == buflen ? 0 : -EIO;
}
#define bytes_left_to_read_on_page(paddr,buflen) \
((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,flexcop_usb_request_t req,
flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len)
{
int i,ret = 0;
u16 wMax;
u32 pagechunk = 0;
switch(req) {
case B2C2_USB_READ_V8_MEM: wMax = USB_MEM_READ_MAX; break;
case B2C2_USB_WRITE_V8_MEM: wMax = USB_MEM_WRITE_MAX; break;
case B2C2_USB_FLASH_BLOCK: wMax = USB_FLASH_MAX; break;
default:
return -EINVAL;
break;
}
for (i = 0; i < len;) {
pagechunk = wMax < bytes_left_to_read_on_page(addr,len) ? wMax : bytes_left_to_read_on_page(addr,len);
deb_info("%x\n",(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended));
if ((ret = flexcop_usb_v8_memory_req(fc_usb,req,
page_start + (addr / V8_MEMORY_PAGE_SIZE), /* actual page */
(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended),
&buf[i],pagechunk)) < 0)
return ret;
addr += pagechunk;
len -= pagechunk;
}
return 0;
}
static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
{
return flexcop_usb_memory_req(fc->bus_specific,B2C2_USB_READ_V8_MEM,
V8_MEMORY_PAGE_FLASH,0x1f010,1,fc->dvb_adapter.proposed_mac,6);
}
#if 0
static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
u16 buflen, u8 *pvBuffer)
{
u16 wValue;
u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
// u8 dwRequestType = (u8) RTYPE_GENERIC,
int nWaitTime = 2,
pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
len;
wValue = (func << 8) | extra;
len = usb_control_msg(fc_usb->udev,pipe,
B2C2_USB_UTILITY,
request_type,
wValue,
wIndex,
pvBuffer,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EIO;
}
#endif
/* usb i2c stuff */
static int flexcop_usb_i2c_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
{
u16 wValue, wIndex;
int nWaitTime,pipe,len;
// u8 dwRequestType;
u8 request_type = USB_TYPE_VENDOR;
switch (func) {
case USB_FUNC_I2C_WRITE:
case USB_FUNC_I2C_MULTIWRITE:
case USB_FUNC_I2C_REPEATWRITE:
/* DKT 020208 - add this to support special case of DiSEqC */
case USB_FUNC_I2C_CHECKWRITE:
pipe = B2C2_USB_CTRL_PIPE_OUT;
nWaitTime = 2;
// dwRequestType = (u8) RTYPE_GENERIC;
request_type |= USB_DIR_OUT;
break;
case USB_FUNC_I2C_READ:
case USB_FUNC_I2C_REPEATREAD:
pipe = B2C2_USB_CTRL_PIPE_IN;
nWaitTime = 2;
// dwRequestType = (u8) RTYPE_GENERIC;
request_type |= USB_DIR_IN;
break;
default:
deb_info("unsupported function for i2c_req %x\n",func);
return -EINVAL;
}
wValue = (func << 8 ) | (port << 4);
wIndex = (chipaddr << 8 ) | addr;
deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",func,request_type,req,
((wValue && 0xff) << 8),wValue >> 8,((wIndex && 0xff) << 8),wIndex >> 8);
len = usb_control_msg(fc_usb->udev,pipe,
req,
request_type,
wValue,
wIndex,
buf,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EREMOTEIO;
}
/* actual bus specific access functions, make sure prototype are/will be equal to pci */
static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg)
{
flexcop_ibi_value val;
val.raw = 0;
flexcop_usb_readwrite_dw(fc,reg, &val.raw, 1);
return val;
}
static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg, flexcop_ibi_value val)
{
return flexcop_usb_readwrite_dw(fc,reg, &val.raw, 0);
}
static int flexcop_usb_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
{
if (op == FC_READ)
return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_READ,port,chipaddr,addr,buf,len);
else
return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_WRITE,port,chipaddr,addr,buf,len);
}
static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, u8 *buffer, int buffer_length)
{
u8 *b;
int l;
deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", fc_usb->tmp_buffer_length, buffer_length);
if (fc_usb->tmp_buffer_length > 0) {
memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, buffer_length);
fc_usb->tmp_buffer_length += buffer_length;
b = fc_usb->tmp_buffer;
l = fc_usb->tmp_buffer_length;
} else {
b=buffer;
l=buffer_length;
}
while (l >= 190) {
if (*b == 0xff)
switch (*(b+1) & 0x03) {
case 0x01: /* media packet */
if ( *(b+2) == 0x47 )
flexcop_pass_dmx_packets(fc_usb->fc_dev, b+2, 1);
else
deb_ts("not ts packet %02x %02x %02x %02x \n", *(b+2), *(b+3), *(b+4), *(b+5) );
b += 190;
l -= 190;
break;
default:
deb_ts("wrong packet type\n");
l = 0;
break;
}
else {
deb_ts("wrong header\n");
l = 0;
}
}
if (l>0)
memcpy(fc_usb->tmp_buffer, b, l);
fc_usb->tmp_buffer_length = l;
}
static void flexcop_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
{
struct flexcop_usb *fc_usb = urb->context;
int i;
if (urb->actual_length > 0)
deb_ts("urb completed, bufsize: %d actlen; %d\n",urb->transfer_buffer_length, urb->actual_length);
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status < 0) {
err("iso frame descriptor %d has an error: %d\n",i,urb->iso_frame_desc[i].status);
} else
if (urb->iso_frame_desc[i].actual_length > 0) {
deb_ts("passed %d bytes to the demux\n",urb->iso_frame_desc[i].actual_length);
flexcop_usb_process_frame(fc_usb,
urb->transfer_buffer + urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
usb_submit_urb(urb,GFP_ATOMIC);
}
static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
{
/* submit/kill iso packets */
return 0;
}
static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
{
int i;
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (fc_usb->iso_urb[i] != NULL) {
deb_ts("unlinking/killing urb no. %d\n",i);
usb_kill_urb(fc_usb->iso_urb[i]);
usb_free_urb(fc_usb->iso_urb[i]);
}
if (fc_usb->iso_buffer != NULL)
pci_free_consistent(NULL,fc_usb->buffer_size, fc_usb->iso_buffer, fc_usb->dma_addr);
}
static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
{
u16 frame_size = fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize;
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
int buffer_offset = 0;
deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
fc_usb->iso_buffer = pci_alloc_consistent(NULL,bufsize,&fc_usb->dma_addr);
if (fc_usb->iso_buffer == NULL)
return -ENOMEM;
memset(fc_usb->iso_buffer, 0, bufsize);
fc_usb->buffer_size = bufsize;
/* creating iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (!(fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
ret = -ENOMEM;
goto urb_error;
}
/* initialising and submitting iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
int frame_offset = 0;
struct urb *urb = fc_usb->iso_urb[i];
deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
urb->dev = fc_usb->udev;
urb->context = fc_usb;
urb->complete = flexcop_usb_urb_complete;
urb->pipe = B2C2_USB_DATA_PIPE;
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length = frame_size;
frame_offset += frame_size;
}
if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
err("submitting urb %d failed with %d.",i,ret);
goto urb_error;
}
deb_ts("submitted urb no. %d.\n",i);
}
/* SRAM */
flexcop_sram_set_dest(fc_usb->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET |
FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_WAN_USB);
flexcop_wan_set_speed(fc_usb->fc_dev,FC_WAN_SPEED_8MBITS);
flexcop_sram_ctrl(fc_usb->fc_dev,1,1,1);
ret = 0;
goto success;
urb_error:
flexcop_usb_transfer_exit(fc_usb);
success:
return ret;
}
static int flexcop_usb_init(struct flexcop_usb *fc_usb)
{
/* use the alternate setting with the larges buffer */
usb_set_interface(fc_usb->udev,0,1);
switch (fc_usb->udev->speed) {
case USB_SPEED_LOW:
err("cannot handle USB speed because it is to sLOW.");
return -ENODEV;
break;
case USB_SPEED_FULL:
info("running at FULL speed.");
break;
case USB_SPEED_HIGH:
info("running at HIGH speed.");
break;
case USB_SPEED_UNKNOWN: /* fall through */
default:
err("cannot handle USB speed because it is unkown.");
return -ENODEV;
}
usb_set_intfdata(fc_usb->uintf, fc_usb);
return 0;
}
static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
{
usb_set_intfdata(fc_usb->uintf, NULL);
}
static int flexcop_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct flexcop_usb *fc_usb = NULL;
struct flexcop_device *fc = NULL;
int ret;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_usb = fc->bus_specific;
fc_usb->fc_dev = fc;
fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
fc->i2c_request = flexcop_usb_i2c_request;
fc->get_mac_addr = flexcop_usb_get_mac_addr;
fc->stream_control = flexcop_usb_stream_control;
fc->pid_filtering = 1;
fc->bus_type = FC_USB;
fc->dev = &udev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_usb->udev = udev;
fc_usb->uintf = intf;
if ((ret = flexcop_usb_init(fc_usb)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_usb_exit;
/* xfer init */
if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
goto err_fc_exit;
info("%s successfully initialized and connected.",DRIVER_NAME);
ret = 0;
goto success;
err_fc_exit:
flexcop_device_exit(fc);
err_usb_exit:
flexcop_usb_exit(fc_usb);
err_kfree:
flexcop_device_kfree(fc);
success:
return ret;
}
static void flexcop_usb_disconnect(struct usb_interface *intf)
{
struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
flexcop_usb_transfer_exit(fc_usb);
flexcop_device_exit(fc_usb->fc_dev);
flexcop_usb_exit(fc_usb);
flexcop_device_kfree(fc_usb->fc_dev);
info("%s successfully deinitialized and disconnected.",DRIVER_NAME);
}
static struct usb_device_id flexcop_usb_table [] = {
{ USB_DEVICE(0x0af7, 0x0101) },
{ }
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver flexcop_usb_driver = {
.owner = THIS_MODULE,
.name = "Technisat/B2C2 FlexCop II/IIb/III USB",
.probe = flexcop_usb_probe,
.disconnect = flexcop_usb_disconnect,
.id_table = flexcop_usb_table,
};
/* module stuff */
static int __init flexcop_usb_module_init(void)
{
int result;
if ((result = usb_register(&flexcop_usb_driver))) {
err("usb_register failed. (%d)",result);
return result;
}
return 0;
}
static void __exit flexcop_usb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&flexcop_usb_driver);
}
module_init(flexcop_usb_module_init);
module_exit(flexcop_usb_module_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,119 @@
#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;
u8 tmp_buffer[1023+190];
int tmp_buffer_length;
};
#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

View File

@ -0,0 +1,286 @@
/*
* 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",fc->owner)) < 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);
}
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 ((ret = flexcop_dvb_init(fc)))
goto error;
/* do the MAC address reading after initializing the dvb_adapter */
if (fc->get_mac_addr(fc, 0) == 0) {
u8 *b = fc->dvb_adapter.proposed_mac;
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,b);
flexcop_mac_filter_ctrl(fc,1);
} else
warn("reading of MAC address failed.\n");
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");

View File

@ -0,0 +1,30 @@
/*
* 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

View File

@ -97,7 +97,7 @@ struct adapter {
u8 mac_addr[8];
u32 dw_sram_type;
struct dvb_adapter *dvb_adapter;
struct dvb_adapter dvb_adapter;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend hw_frontend;
@ -2461,7 +2461,7 @@ static void frontend_init(struct adapter *skystar2)
skystar2->pdev->subsystem_vendor,
skystar2->pdev->subsystem_device);
} else {
if (dvb_register_frontend(skystar2->dvb_adapter, skystar2->fe)) {
if (dvb_register_frontend(&skystar2->dvb_adapter, skystar2->fe)) {
printk("skystar2: Frontend registration failed!\n");
if (skystar2->fe->ops->release)
skystar2->fe->ops->release(skystar2->fe);
@ -2486,17 +2486,17 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret < 0)
goto out;
ret = dvb_register_adapter(&dvb_adapter, skystar2_pci_driver.name,
adapter = pci_get_drvdata(pdev);
dvb_adapter = &adapter->dvb_adapter;
ret = dvb_register_adapter(dvb_adapter, skystar2_pci_driver.name,
THIS_MODULE);
if (ret < 0) {
printk("%s: Error registering DVB adapter\n", __FUNCTION__);
goto err_halt;
}
adapter = pci_get_drvdata(pdev);
dvb_adapter->priv = adapter;
adapter->dvb_adapter = dvb_adapter;
init_MUTEX(&adapter->i2c_sem);
@ -2541,7 +2541,7 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->dmxdev.demux = dmx;
adapter->dmxdev.capabilities = 0;
ret = dvb_dmxdev_init(&adapter->dmxdev, adapter->dvb_adapter);
ret = dvb_dmxdev_init(&adapter->dmxdev, &adapter->dvb_adapter);
if (ret < 0)
goto err_dmx_release;
@ -2559,7 +2559,7 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret < 0)
goto err_remove_mem_frontend;
dvb_net_init(adapter->dvb_adapter, &adapter->dvbnet, &dvbdemux->dmx);
dvb_net_init(&adapter->dvb_adapter, &adapter->dvbnet, &dvbdemux->dmx);
frontend_init(adapter);
out:
@ -2576,7 +2576,7 @@ err_dmx_release:
err_i2c_del:
i2c_del_adapter(&adapter->i2c_adap);
err_dvb_unregister:
dvb_unregister_adapter(adapter->dvb_adapter);
dvb_unregister_adapter(&adapter->dvb_adapter);
err_halt:
driver_halt(pdev);
goto out;
@ -2605,7 +2605,7 @@ static void skystar2_remove(struct pci_dev *pdev)
if (adapter->fe != NULL)
dvb_unregister_frontend(adapter->fe);
dvb_unregister_adapter(adapter->dvb_adapter);
dvb_unregister_adapter(&adapter->dvb_adapter);
i2c_del_adapter(&adapter->i2c_adap);

View File

@ -11,9 +11,8 @@ config DVB_BT8XX
the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards and
pcHDTV HD2000 cards.
Since these cards have no MPEG decoder onboard, they transmit
Since these cards have no MPEG decoder onboard, they transmit
only compressed MPEG data over the PCI bus, so you need
an external software decoder to watch TV on your computer.
Say Y if you own such a device and want to use it.

View File

@ -1,5 +1,3 @@
obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o
obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video -Idrivers/media/dvb/frontends

View File

@ -4,27 +4,27 @@
* Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
*
* large parts based on the bttv driver
* Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
* & Marcus Metzler (mocm@thp.uni-koeln.de)
* Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de)
* & Marcus Metzler (mocm@metzlerbros.de)
* (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
*/
#include <linux/module.h>
@ -58,7 +58,7 @@ module_param_named(verbose, bt878_verbose, int, 0444);
MODULE_PARM_DESC(verbose,
"verbose startup messages, default is 1 (yes)");
module_param_named(debug, bt878_debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off).");
int bt878_num;
struct bt878 bt878[BT878_MAX];
@ -128,21 +128,21 @@ static int bt878_mem_alloc(struct bt878 *bt)
}
/* RISC instructions */
#define RISC_WRITE (0x01 << 28)
#define RISC_JUMP (0x07 << 28)
#define RISC_SYNC (0x08 << 28)
#define RISC_WRITE (0x01 << 28)
#define RISC_JUMP (0x07 << 28)
#define RISC_SYNC (0x08 << 28)
/* RISC bits */
#define RISC_WR_SOL (1 << 27)
#define RISC_WR_EOL (1 << 26)
#define RISC_IRQ (1 << 24)
#define RISC_WR_SOL (1 << 27)
#define RISC_WR_EOL (1 << 26)
#define RISC_IRQ (1 << 24)
#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))
#define RISC_SYNC_RESYNC (1 << 15)
#define RISC_SYNC_FM1 0x06
#define RISC_SYNC_VRO 0x0C
#define RISC_SYNC_RESYNC (1 << 15)
#define RISC_SYNC_FM1 0x06
#define RISC_SYNC_VRO 0x0C
#define RISC_FLUSH() bt->risc_pos = 0
#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
static int bt878_make_risc(struct bt878 *bt)
{
@ -173,7 +173,7 @@ static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin)
RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin);
RISC_INSTR(0);
dprintk("bt878: risc len lines %u, bytes per line %u\n",
dprintk("bt878: risc len lines %u, bytes per line %u\n",
bt->line_count, bt->line_bytes);
for (line = 0; line < bt->line_count; line++) {
// At the beginning of every block we issue an IRQ with previous (finished) block number set
@ -228,14 +228,14 @@ void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
* Hacked for DST to:
* SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI
*/
int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT |
BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT |
int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT |
BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT |
BT878_AFBUS | BT878_ARISCI;
/* ignore pesky bits */
int_mask &= ~irq_err_ignore;
btwrite(int_mask, BT878_AINT_MASK);
btwrite(controlreg, BT878_AGPIO_DMA_CTL);
}
@ -461,9 +461,9 @@ static int __devinit bt878_probe(struct pci_dev *dev,
pci_set_drvdata(dev, bt);
/* if(init_bt878(btv) < 0) {
bt878_remove(dev);
return -EIO;
}
bt878_remove(dev);
return -EIO;
}
*/
if ((result = bt878_mem_alloc(bt))) {
@ -536,10 +536,10 @@ static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
static struct pci_driver bt878_pci_driver = {
.name = "bt878",
.name = "bt878",
.id_table = bt878_pci_tbl,
.probe = bt878_probe,
.remove = bt878_remove,
.probe = bt878_probe,
.remove = bt878_remove,
};
static int bt878_pci_driver_registered = 0;
@ -558,7 +558,7 @@ static int bt878_init_module(void)
(BT878_VERSION_CODE >> 8) & 0xff,
BT878_VERSION_CODE & 0xff);
/*
bt878_check_chipset();
bt878_check_chipset();
*/
/* later we register inside of bt878_find_audio_dma()
* because we may want to ignore certain cards */

View File

@ -1,4 +1,4 @@
/*
/*
bt878.h - Bt878 audio module (register offsets)
Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
@ -120,14 +120,14 @@ struct bt878 {
u32 risc_pos;
struct tasklet_struct tasklet;
int shutdown;
int shutdown;
};
extern struct bt878 bt878[BT878_MAX];
void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
u32 irq_err_ignore);
void bt878_stop(struct bt878 *bt);
void bt878_stop(struct bt878 *bt);
#if defined(__powerpc__) /* big-endian */
extern __inline__ void io_st_le32(volatile unsigned __iomem *addr, unsigned val)

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
/*
Frontend-driver for TwinHan DST Frontend
Copyright (C) 2003 Jamie Honan
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 DST_H
#define DST_H
#include <linux/dvb/frontend.h>
#include <linux/device.h>
#include "bt878.h"
struct dst_config
{
/* the demodulator's i2c address */
u8 demod_address;
};
extern struct dvb_frontend* dst_attach(const struct dst_config* config,
struct i2c_adapter* i2c,
struct bt878 *bt);
#endif // DST_H

View File

@ -0,0 +1,861 @@
/*
CA-driver for TwinHan DST Frontend/Card
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/dvb/ca.h>
#include "dvbdev.h"
#include "dvb_frontend.h"
#include "dst_ca.h"
#include "dst_common.h"
static unsigned int verbose = 1;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
static unsigned int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug messages, default is 1 (yes)");
#define dprintk if (debug) printk
/* Need some more work */
static int ca_set_slot_descr(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
/* Need some more work */
static int ca_set_pid(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
static int put_checksum(u8 *check_string, int length)
{
u8 i = 0, checksum = 0;
if (verbose > 3) {
dprintk("%s: ========================= Checksum calculation ===========================\n", __FUNCTION__);
dprintk("%s: String Length=[0x%02x]\n", __FUNCTION__, length);
dprintk("%s: String=[", __FUNCTION__);
}
while (i < length) {
if (verbose > 3)
dprintk(" %02x", check_string[i]);
checksum += check_string[i];
i++;
}
if (verbose > 3) {
dprintk(" ]\n");
dprintk("%s: Sum=[%02x]\n", __FUNCTION__, checksum);
}
check_string[length] = ~checksum + 1;
if (verbose > 3) {
dprintk("%s: Checksum=[%02x]\n", __FUNCTION__, check_string[length]);
dprintk("%s: ==========================================================================\n", __FUNCTION__);
}
return 0;
}
static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
{
u8 reply;
dst_comm_init(state);
msleep(65);
if (write_dst(state, data, len)) {
dprintk("%s: Write not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
if ((dst_pio_disable(state)) < 0) {
dprintk("%s: DST PIO disable failed.\n", __FUNCTION__);
return -1;
}
if (read_dst(state, &reply, GET_ACK) < 0) {
dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
if (read) {
if (! dst_wait_dst_ready(state, LONG_DELAY)) {
dprintk("%s: 8820 not ready\n", __FUNCTION__);
return -1;
}
if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
}
return 0;
}
static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
{
u8 dst_ca_comm_err = 0;
while (dst_ca_comm_err < RETRIES) {
dst_comm_init(state);
if (verbose > 2)
dprintk("%s: Put Command\n", __FUNCTION__);
if (dst_ci_command(state, data, ca_string, len, read)) { // If error
dst_error_recovery(state);
dst_ca_comm_err++; // work required here.
}
break;
}
return 0;
}
static int ca_get_app_info(struct dst_state *state)
{
static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
put_checksum(&command[0], command[0]);
if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1) {
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
dprintk("%s: ================================ CI Module Application Info ======================================\n", __FUNCTION__);
dprintk("%s: Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]\n",
__FUNCTION__, state->messages[7], (state->messages[8] << 8) | state->messages[9],
(state->messages[10] << 8) | state->messages[11], __FUNCTION__, (char *)(&state->messages[12]));
dprintk("%s: ==================================================================================================\n", __FUNCTION__);
}
return 0;
}
static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void *arg)
{
int i;
u8 slot_cap[256];
static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
put_checksum(&slot_command[0], slot_command[0]);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
/* Will implement the rest soon */
if (verbose > 1) {
dprintk("%s: Slot cap = [%d]\n", __FUNCTION__, slot_cap[7]);
dprintk("===================================\n");
for (i = 0; i < 8; i++)
dprintk(" %d", slot_cap[i]);
dprintk("\n");
}
p_ca_caps->slot_num = 1;
p_ca_caps->slot_type = 1;
p_ca_caps->descr_num = slot_cap[7];
p_ca_caps->descr_type = 1;
if (copy_to_user((struct ca_caps *)arg, p_ca_caps, sizeof (struct ca_caps))) {
return -EFAULT;
}
return 0;
}
/* Need some more work */
static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
return -EOPNOTSUPP;
}
static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void *arg)
{
int i;
static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
u8 *slot_info = state->rxbuffer;
put_checksum(&slot_command[0], 7);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
/* Will implement the rest soon */
if (verbose > 1) {
dprintk("%s: Slot info = [%d]\n", __FUNCTION__, slot_info[3]);
dprintk("===================================\n");
for (i = 0; i < 8; i++)
dprintk(" %d", slot_info[i]);
dprintk("\n");
}
if (slot_info[4] & 0x80) {
p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
}
else if (slot_info[4] & 0x40) {
p_ca_slot_info->flags = CA_CI_MODULE_READY;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
}
else {
p_ca_slot_info->flags = 0;
}
if (copy_to_user((struct ca_slot_info *)arg, p_ca_slot_info, sizeof (struct ca_slot_info))) {
return -EFAULT;
}
return 0;
}
static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
u8 i = 0;
u32 command = 0;
if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
return -EFAULT;
if (p_ca_message->msg) {
if (verbose > 3)
dprintk("Message = [%02x %02x %02x]\n", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]);
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
if (verbose > 3)
dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
switch (command) {
case CA_APP_INFO:
memcpy(p_ca_message->msg, state->messages, 128);
if (copy_to_user((void *)arg, p_ca_message, sizeof (struct ca_msg)) )
return -EFAULT;
break;
}
}
return 0;
}
static int handle_en50221_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
{
if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
}
else {
hw_buffer->msg[2] = 0x03;
hw_buffer->msg[3] = 0x00;
}
return 0;
}
static int debug_8820_buffer(struct ca_msg *hw_buffer)
{
unsigned int i;
dprintk("%s:Debug=[", __FUNCTION__);
for (i = 0; i < (hw_buffer->msg[0] + 1); i++)
dprintk(" %02x", hw_buffer->msg[i]);
dprintk("]\n");
return 0;
}
static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 reply)
{
if ((dst_put_ci(state, hw_buffer->msg, (hw_buffer->length + 1), hw_buffer->msg, reply)) < 0) {
dprintk("%s: DST-CI Command failed.\n", __FUNCTION__);
dprintk("%s: Resetting DST.\n", __FUNCTION__);
rdc_reset_state(state);
return -1;
}
if (verbose > 2)
dprintk("%s: DST-CI Command succes.\n", __FUNCTION__);
return 0;
}
static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
{
u32 hw_offset, buf_offset, i, k;
u32 program_info_length = 0, es_info_length = 0, length = 0, words = 0;
u8 found_prog_ca_desc = 0, found_stream_ca_desc = 0, error_condition = 0, hw_buffer_length = 0;
if (verbose > 3)
dprintk("%s, p_ca_message length %d (0x%x)\n", __FUNCTION__,p_ca_message->length,p_ca_message->length );
handle_en50221_tag(state, p_ca_message, hw_buffer); /* EN50221 tag */
/* Handle the length field (variable) */
if (!(p_ca_message->msg[3] & 0x80)) { /* Length = 1 */
length = p_ca_message->msg[3] & 0x7f;
words = 0; /* domi's suggestion */
}
else { /* Length = words */
words = p_ca_message->msg[3] & 0x7f;
for (i = 0; i < words; i++) {
length = length << 8;
length = length | p_ca_message->msg[4 + i];
}
}
if (verbose > 4) {
dprintk("%s:Length=[%d (0x%x)], Words=[%d]\n", __FUNCTION__, length,length, words);
/* Debug Input string */
for (i = 0; i < length; i++)
dprintk(" %02x", p_ca_message->msg[i]);
dprintk("]\n");
}
hw_offset = 7;
buf_offset = words + 4;
/* Program Header */
if (verbose > 4)
dprintk("\n%s:Program Header=[", __FUNCTION__);
for (i = 0; i < 6; i++) {
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
if (verbose > 4)
dprintk(" %02x", p_ca_message->msg[buf_offset]);
hw_offset++, buf_offset++, hw_buffer_length++;
}
if (verbose > 4)
dprintk("]\n");
program_info_length = 0;
program_info_length = (((program_info_length | p_ca_message->msg[words + 8]) & 0x0f) << 8) | p_ca_message->msg[words + 9];
if (verbose > 4)
dprintk("%s:Program info Length=[%d][%02x], hw_offset=[%d], buf_offset=[%d] \n",
__FUNCTION__, program_info_length, program_info_length, hw_offset, buf_offset);
if (program_info_length && (program_info_length < 256)) { /* If program_info_length */
hw_buffer->msg[11] = hw_buffer->msg[11] & 0x0f; /* req only 4 bits */
hw_buffer->msg[12] = hw_buffer->msg[12] + 1; /* increment! ASIC bug! */
if (p_ca_message->msg[buf_offset + 1] == 0x09) { /* Check CA descriptor */
found_prog_ca_desc = 1;
if (verbose > 4)
dprintk("%s: Found CA descriptor @ Program level\n", __FUNCTION__);
}
if (found_prog_ca_desc) { /* Command only if CA descriptor */
hw_buffer->msg[13] = p_ca_message->msg[buf_offset]; /* CA PMT command ID */
hw_offset++, buf_offset++, hw_buffer_length++;
}
/* Program descriptors */
if (verbose > 4) {
dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
dprintk("%s:Program descriptors=[", __FUNCTION__);
}
while (program_info_length && !error_condition) { /* Copy prog descriptors */
if (program_info_length > p_ca_message->length) { /* Error situation */
dprintk ("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d]\n",
__FUNCTION__, __LINE__, program_info_length);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
error_condition = 1;
break;
}
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
dprintk(" %02x", p_ca_message->msg[buf_offset]);
hw_offset++, buf_offset++, hw_buffer_length++, program_info_length--;
}
if (verbose > 4) {
dprintk("]\n");
dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
}
if (found_prog_ca_desc) {
if (!reply) {
hw_buffer->msg[13] = 0x01; /* OK descrambling */
if (verbose > 1)
dprintk("CA PMT Command = OK Descrambling\n");
}
else {
hw_buffer->msg[13] = 0x02; /* Ok MMI */
if (verbose > 1)
dprintk("CA PMT Command = Ok MMI\n");
}
if (query) {
hw_buffer->msg[13] = 0x03; /* Query */
if (verbose > 1)
dprintk("CA PMT Command = CA PMT query\n");
}
}
}
else {
hw_buffer->msg[11] = hw_buffer->msg[11] & 0xf0; /* Don't write to ASIC */
hw_buffer->msg[12] = hw_buffer->msg[12] = 0x00;
}
if (verbose > 4)
dprintk("%s:**********>p_ca_message->length=[%d], buf_offset=[%d], hw_offset=[%d]\n",
__FUNCTION__, p_ca_message->length, buf_offset, hw_offset);
while ((buf_offset < p_ca_message->length) && !error_condition) {
/* Bail out in case of an indefinite loop */
if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
dprintk("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d], buf_offset=[%d]\n",
__FUNCTION__, __LINE__, program_info_length, buf_offset);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
error_condition = 1;
break;
}
/* Stream Header */
for (k = 0; k < 5; k++) {
hw_buffer->msg[hw_offset + k] = p_ca_message->msg[buf_offset + k];
}
es_info_length = 0;
es_info_length = (es_info_length | (p_ca_message->msg[buf_offset + 3] & 0x0f)) << 8 | p_ca_message->msg[buf_offset + 4];
if (verbose > 4) {
dprintk("\n%s:----->Stream header=[%02x %02x %02x %02x %02x]\n", __FUNCTION__,
p_ca_message->msg[buf_offset + 0], p_ca_message->msg[buf_offset + 1],
p_ca_message->msg[buf_offset + 2], p_ca_message->msg[buf_offset + 3],
p_ca_message->msg[buf_offset + 4]);
dprintk("%s:----->Stream type=[%02x], es length=[%d (0x%x)], Chars=[%02x] [%02x], buf_offset=[%d]\n", __FUNCTION__,
p_ca_message->msg[buf_offset + 0], es_info_length, es_info_length,
p_ca_message->msg[buf_offset + 3], p_ca_message->msg[buf_offset + 4], buf_offset);
}
hw_buffer->msg[hw_offset + 3] &= 0x0f; /* req only 4 bits */
if (found_prog_ca_desc) {
hw_buffer->msg[hw_offset + 3] = 0x00;
hw_buffer->msg[hw_offset + 4] = 0x00;
}
hw_offset += 5, buf_offset += 5, hw_buffer_length += 5;
/* Check for CA descriptor */
if (p_ca_message->msg[buf_offset + 1] == 0x09) {
if (verbose > 4)
dprintk("%s:Found CA descriptor @ Stream level\n", __FUNCTION__);
found_stream_ca_desc = 1;
}
/* ES descriptors */
if (es_info_length && !error_condition && !found_prog_ca_desc && found_stream_ca_desc) {
// if (!ca_pmt_done) {
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset]; /* CA PMT cmd(es) */
if (verbose > 4)
printk("%s:----->CA PMT Command ID=[%02x]\n", __FUNCTION__, p_ca_message->msg[buf_offset]);
// hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--, ca_pmt_done = 1;
hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
// }
if (verbose > 4)
dprintk("%s:----->ES descriptors=[", __FUNCTION__);
while (es_info_length && !error_condition) { /* ES descriptors */
if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
if (verbose > 4) {
dprintk("%s:\"WARNING\" ES Length error, line=[%d], es_info_length=[%d], buf_offset=[%d]\n",
__FUNCTION__, __LINE__, es_info_length, buf_offset);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
}
error_condition = 1;
break;
}
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
if (verbose > 3)
dprintk("%02x ", hw_buffer->msg[hw_offset]);
hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
}
found_stream_ca_desc = 0; /* unset for new streams */
dprintk("]\n");
}
}
/* MCU Magic words */
hw_buffer_length += 7;
hw_buffer->msg[0] = hw_buffer_length;
hw_buffer->msg[1] = 64;
hw_buffer->msg[4] = 3;
hw_buffer->msg[5] = hw_buffer->msg[0] - 7;
hw_buffer->msg[6] = 0;
/* Fix length */
hw_buffer->length = hw_buffer->msg[0];
put_checksum(&hw_buffer->msg[0], hw_buffer->msg[0]);
/* Do the actual write */
if (verbose > 4) {
dprintk("%s:======================DEBUGGING================================\n", __FUNCTION__);
dprintk("%s: Actual Length=[%d]\n", __FUNCTION__, hw_buffer_length);
}
/* Only for debugging! */
if (verbose > 2)
debug_8820_buffer(hw_buffer);
if (verbose > 3)
dprintk("%s: Reply = [%d]\n", __FUNCTION__, reply);
write_to_8820(state, hw_buffer, reply);
return 0;
}
/* Board supports CA PMT reply ? */
static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
{
int ca_pmt_reply_test = 0;
/* Do test board */
/* Not there yet but soon */
/* CA PMT Reply capable */
if (ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
return -1;
}
/* Process CA PMT Reply */
/* will implement soon */
dprintk("%s: Not there yet\n", __FUNCTION__);
}
/* CA PMT Reply not capable */
if (!ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: ca_set_pmt.. success !\n", __FUNCTION__);
/* put a dummy message */
}
return 0;
}
static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
int i = 0;
unsigned int ca_message_header_len;
u32 command = 0;
struct ca_msg *hw_buffer;
if ((hw_buffer = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if (verbose > 3)
dprintk("%s\n", __FUNCTION__);
if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
return -EFAULT;
if (p_ca_message->msg) {
ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */
/* EN50221 tag */
command = 0;
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
if (verbose > 3)
dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
switch (command) {
case CA_PMT:
if (verbose > 3)
dprintk("Command = SEND_CA_PMT\n");
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) {
dprintk("%s: -->CA_PMT Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: -->CA_PMT Success !\n", __FUNCTION__);
// retval = dummy_set_pmt(state, p_ca_message, hw_buffer, 0, 0);
break;
case CA_PMT_REPLY:
if (verbose > 3)
dprintk("Command = CA_PMT_REPLY\n");
/* Have to handle the 2 basic types of cards here */
if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
dprintk("%s: -->CA_PMT_REPLY Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: -->CA_PMT_REPLY Success !\n", __FUNCTION__);
/* Certain boards do behave different ? */
// retval = ca_set_pmt(state, p_ca_message, hw_buffer, 1, 1);
case CA_APP_INFO_ENQUIRY: // only for debugging
if (verbose > 3)
dprintk("%s: Getting Cam Application information\n", __FUNCTION__);
if ((ca_get_app_info(state)) < 0) {
dprintk("%s: -->CA_APP_INFO_ENQUIRY Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
printk("%s: -->CA_APP_INFO_ENQUIRY Success !\n", __FUNCTION__);
break;
}
}
return 0;
}
static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
{
struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
struct dst_state* state = (struct dst_state*) dvbdev->priv;
struct ca_slot_info *p_ca_slot_info;
struct ca_caps *p_ca_caps;
struct ca_msg *p_ca_message;
if ((p_ca_message = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if ((p_ca_slot_info = (struct ca_slot_info *) kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if ((p_ca_caps = (struct ca_caps *) kmalloc(sizeof (struct ca_caps), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
/* We have now only the standard ioctl's, the driver is upposed to handle internals. */
switch (cmd) {
case CA_SEND_MSG:
if (verbose > 1)
dprintk("%s: Sending message\n", __FUNCTION__);
if ((ca_send_message(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_SEND_MSG Failed !\n", __FUNCTION__);
return -1;
}
break;
case CA_GET_MSG:
if (verbose > 1)
dprintk("%s: Getting message\n", __FUNCTION__);
if ((ca_get_message(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_GET_MSG Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_MSG Success !\n", __FUNCTION__);
break;
case CA_RESET:
if (verbose > 1)
dprintk("%s: Resetting DST\n", __FUNCTION__);
dst_error_bailout(state);
msleep(4000);
break;
case CA_GET_SLOT_INFO:
if (verbose > 1)
dprintk("%s: Getting Slot info\n", __FUNCTION__);
if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
dprintk("%s: -->CA_GET_SLOT_INFO Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_SLOT_INFO Success !\n", __FUNCTION__);
break;
case CA_GET_CAP:
if (verbose > 1)
dprintk("%s: Getting Slot capabilities\n", __FUNCTION__);
if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
dprintk("%s: -->CA_GET_CAP Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_CAP Success !\n", __FUNCTION__);
break;
case CA_GET_DESCR_INFO:
if (verbose > 1)
dprintk("%s: Getting descrambler description\n", __FUNCTION__);
if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_GET_DESCR_INFO Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_DESCR_INFO Success !\n", __FUNCTION__);
break;
case CA_SET_DESCR:
if (verbose > 1)
dprintk("%s: Setting descrambler\n", __FUNCTION__);
if ((ca_set_slot_descr()) < 0) {
dprintk("%s: -->CA_SET_DESCR Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_SET_DESCR Success !\n", __FUNCTION__);
break;
case CA_SET_PID:
if (verbose > 1)
dprintk("%s: Setting PID\n", __FUNCTION__);
if ((ca_set_pid()) < 0) {
dprintk("%s: -->CA_SET_PID Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_SET_PID Success !\n", __FUNCTION__);
default:
return -EOPNOTSUPP;
};
return 0;
}
static int dst_ca_open(struct inode *inode, struct file *file)
{
if (verbose > 4)
dprintk("%s:Device opened [%p]\n", __FUNCTION__, file);
try_module_get(THIS_MODULE);
return 0;
}
static int dst_ca_release(struct inode *inode, struct file *file)
{
if (verbose > 4)
dprintk("%s:Device closed.\n", __FUNCTION__);
module_put(THIS_MODULE);
return 0;
}
static int dst_ca_read(struct file *file, char __user * buffer, size_t length, loff_t * offset)
{
int bytes_read = 0;
if (verbose > 4)
dprintk("%s:Device read.\n", __FUNCTION__);
return bytes_read;
}
static int dst_ca_write(struct file *file, const char __user * buffer, size_t length, loff_t * offset)
{
if (verbose > 4)
dprintk("%s:Device write.\n", __FUNCTION__);
return 0;
}
static struct file_operations dst_ca_fops = {
.owner = THIS_MODULE,
.ioctl = (void *)dst_ca_ioctl,
.open = dst_ca_open,
.release = dst_ca_release,
.read = dst_ca_read,
.write = dst_ca_write
};
static struct dvb_device dvbdev_ca = {
.priv = NULL,
.users = 1,
.readers = 1,
.writers = 1,
.fops = &dst_ca_fops
};
int dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
{
struct dvb_device *dvbdev;
if (verbose > 4)
dprintk("%s:registering DST-CA device\n", __FUNCTION__);
dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA);
return 0;
}
EXPORT_SYMBOL(dst_ca_attach);
MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
MODULE_AUTHOR("Manu Abraham");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,58 @@
/*
CA-driver for TwinHan DST Frontend/Card
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, 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 _DST_CA_H_
#define _DST_CA_H_
#define RETRIES 5
#define CA_APP_INFO_ENQUIRY 0x9f8020
#define CA_APP_INFO 0x9f8021
#define CA_ENTER_MENU 0x9f8022
#define CA_INFO_ENQUIRY 0x9f8030
#define CA_INFO 0x9f8031
#define CA_PMT 0x9f8032
#define CA_PMT_REPLY 0x9f8033
#define CA_CLOSE_MMI 0x9f8800
#define CA_DISPLAY_CONTROL 0x9f8801
#define CA_DISPLAY_REPLY 0x9f8802
#define CA_TEXT_LAST 0x9f8803
#define CA_TEXT_MORE 0x9f8804
#define CA_KEYPAD_CONTROL 0x9f8805
#define CA_KEYPRESS 0x9f8806
#define CA_ENQUIRY 0x9f8807
#define CA_ANSWER 0x9f8808
#define CA_MENU_LAST 0x9f8809
#define CA_MENU_MORE 0x9f880a
#define CA_MENU_ANSWER 0x9f880b
#define CA_LIST_LAST 0x9f880c
#define CA_LIST_MORE 0x9f880d
struct dst_ca_private {
struct dst_state *dst;
struct dvb_device *dvbdev;
};
#endif

View File

@ -0,0 +1,153 @@
/*
Frontend-driver for TwinHan DST Frontend
Copyright (C) 2003 Jamie Honan
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, 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 DST_COMMON_H
#define DST_COMMON_H
#include <linux/dvb/frontend.h>
#include <linux/device.h>
#include "bt878.h"
#include "dst_ca.h"
#define NO_DELAY 0
#define LONG_DELAY 1
#define DEVICE_INIT 2
#define DELAY 1
#define DST_TYPE_IS_SAT 0
#define DST_TYPE_IS_TERR 1
#define DST_TYPE_IS_CABLE 2
#define DST_TYPE_IS_ATSC 3
#define DST_TYPE_HAS_NEWTUNE 1
#define DST_TYPE_HAS_TS204 2
#define DST_TYPE_HAS_SYMDIV 4
#define DST_TYPE_HAS_FW_1 8
#define DST_TYPE_HAS_FW_2 16
#define DST_TYPE_HAS_FW_3 32
#define DST_TYPE_HAS_FW_BUILD 64
/* Card capability list */
#define DST_TYPE_HAS_MAC 1
#define DST_TYPE_HAS_DISEQC3 2
#define DST_TYPE_HAS_DISEQC4 4
#define DST_TYPE_HAS_DISEQC5 8
#define DST_TYPE_HAS_MOTO 16
#define DST_TYPE_HAS_CA 32
#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */
#define DST_TYPE_HAS_SESSION 128
#define RDC_8820_PIO_0_DISABLE 0
#define RDC_8820_PIO_0_ENABLE 1
#define RDC_8820_INT 2
#define RDC_8820_RESET 4
/* DST Communication */
#define GET_REPLY 1
#define NO_REPLY 0
#define GET_ACK 1
#define FIXED_COMM 8
#define ACK 0xff
struct dst_state {
struct i2c_adapter* i2c;
struct bt878* bt;
struct dvb_frontend_ops ops;
/* configuration settings */
const struct dst_config* config;
struct dvb_frontend frontend;
/* private ASIC data */
u8 tx_tuna[10];
u8 rx_tuna[10];
u8 rxbuffer[10];
u8 diseq_flags;
u8 dst_type;
u32 type_flags;
u32 frequency; /* intermediate frequency in kHz for QPSK */
fe_spectral_inversion_t inversion;
u32 symbol_rate; /* symbol rate in Symbols per second */
fe_code_rate_t fec;
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone;
u32 decode_freq;
u8 decode_lock;
u16 decode_strength;
u16 decode_snr;
unsigned long cur_jiff;
u8 k22;
fe_bandwidth_t bandwidth;
u32 dst_hw_cap;
u8 dst_fw_version;
fe_sec_mini_cmd_t minicmd;
u8 messages[256];
};
struct dst_types {
char *device_id;
int offset;
u8 dst_type;
u32 type_flags;
u32 dst_feature;
};
struct dst_config
{
/* the ASIC i2c address */
u8 demod_address;
};
int rdc_reset_state(struct dst_state *state);
int rdc_8820_reset(struct dst_state *state);
int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
int dst_pio_enable(struct dst_state *state);
int dst_pio_disable(struct dst_state *state);
int dst_error_recovery(struct dst_state* state);
int dst_error_bailout(struct dst_state *state);
int dst_comm_init(struct dst_state* state);
int write_dst(struct dst_state *state, u8 * data, u8 len);
int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
int dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
int dst_command(struct dst_state* state, u8 * data, u8 len);
#endif // DST_COMMON_H

View File

@ -33,4 +33,3 @@ union dst_gpio_packet {
struct bt878;
int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);

View File

@ -142,7 +142,7 @@ static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
@ -161,7 +161,7 @@ static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_
else if (params->frequency < 771000000) cp = 0xbc;
else cp = 0xf4;
if (params->frequency == 0) bs = 0x03;
if (params->frequency == 0) bs = 0x03;
else if (params->frequency < 443250000) bs = 0x02;
else bs = 0x08;
@ -190,44 +190,44 @@ static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete
u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
1576000,1718000,1856000,2036000,2150000};
1576000,1718000,1856000,2036000,2150000};
u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
0x00102000,0x00104000,0x00108000,0x00110000,
0x00120000,0x00140000};
0x00102000,0x00104000,0x00108000,0x00110000,
0x00120000,0x00140000};
#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
/* This is really the bit driving the tuner chip cx24108 */
/* This is really the bit driving the tuner chip cx24108 */
if(freq<950000) freq=950000; /* kHz */
if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
if(freq<950000) freq=950000; /* kHz */
if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
/* decide which VCO to use for the input frequency */
for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
/* compute the pll divider needed, from Conexant data sheet,
resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
depending on the divider bit. It is set to /4 on the 2 lowest
bands */
n=((i<=2?2:1)*freq*10L)/(XTAL/100);
a=n%32; n/=32; if(a==0) n--;
pump=(freq<(osci[i-1]+osci[i])/2);
pll=0xf8000000|
((pump?1:2)<<(14+11))|
((n&0x1ff)<<(5+11))|
((a&0x1f)<<11);
/* everything is shifted left 11 bits to left-align the bits in the
32bit word. Output to the tuner goes MSB-aligned, after all */
printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
cx24110_pll_write(fe,band);
/* set vga and vca to their widest-band settings, as a precaution.
SetSymbolrate might not be called to set this up */
cx24110_pll_write(fe,0x500c0000);
cx24110_pll_write(fe,0x83f1f800);
cx24110_pll_write(fe,pll);
/* decide which VCO to use for the input frequency */
for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
/* compute the pll divider needed, from Conexant data sheet,
resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
depending on the divider bit. It is set to /4 on the 2 lowest
bands */
n=((i<=2?2:1)*freq*10L)/(XTAL/100);
a=n%32; n/=32; if(a==0) n--;
pump=(freq<(osci[i-1]+osci[i])/2);
pll=0xf8000000|
((pump?1:2)<<(14+11))|
((n&0x1ff)<<(5+11))|
((a&0x1f)<<11);
/* everything is shifted left 11 bits to left-align the bits in the
32bit word. Output to the tuner goes MSB-aligned, after all */
printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
cx24110_pll_write(fe,band);
/* set vga and vca to their widest-band settings, as a precaution.
SetSymbolrate might not be called to set this up */
cx24110_pll_write(fe,0x500c0000);
cx24110_pll_write(fe,0x83f1f800);
cx24110_pll_write(fe,pll);
/* writereg(client,0x56,0x7f);*/
return 0;
@ -299,7 +299,7 @@ static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0x40, 0x40 };
0x00, 0xFF, 0x00, 0x40, 0x40 };
static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
@ -463,6 +463,9 @@ static struct nxt6000_config vp3021_alps_tded4_config = {
static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
{
int ret;
struct dst_state* state = NULL;
switch(type) {
#ifdef BTTV_DVICO_DVBT_LITE
case BTTV_DVICO_DVBT_LITE:
@ -503,7 +506,25 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
break;
case BTTV_TWINHAN_DST:
card->fe = dst_attach(&dst_config, card->i2c_adapter, card->bt);
/* DST is not a frontend driver !!! */
state = (struct dst_state *) kmalloc(sizeof (struct dst_state), GFP_KERNEL);
/* Setup the Card */
state->config = &dst_config;
state->i2c = card->i2c_adapter;
state->bt = card->bt;
/* DST is not a frontend, attaching the ASIC */
if ((dst_attach(state, &card->dvb_adapter)) == NULL) {
printk("%s: Could not find a Twinhan DST.\n", __FUNCTION__);
break;
}
card->fe = &state->frontend;
/* Attach other DST peripherals if any */
/* Conditional Access device */
if (state->dst_hw_cap & DST_TYPE_HAS_CA) {
ret = dst_ca_attach(state, &card->dvb_adapter);
}
if (card->fe != NULL) {
break;
}
@ -531,7 +552,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
card->bt->dev->subsystem_vendor,
card->bt->dev->subsystem_device);
} else {
if (dvb_register_frontend(card->dvb_adapter, card->fe)) {
if (dvb_register_frontend(&card->dvb_adapter, card->fe)) {
printk("dvb-bt8xx: Frontend registration failed!\n");
if (card->fe->ops->release)
card->fe->ops->release(card->fe);
@ -550,7 +571,7 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
return result;
}
card->dvb_adapter->priv = card;
card->dvb_adapter.priv = card;
card->bt->adapter = card->i2c_adapter;
@ -568,7 +589,7 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
if ((result = dvb_dmx_init(&card->demux)) < 0) {
printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
@ -576,11 +597,11 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
card->dmxdev.demux = &card->demux.dmx;
card->dmxdev.capabilities = 0;
if ((result = dvb_dmxdev_init(&card->dmxdev, card->dvb_adapter)) < 0) {
if ((result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter)) < 0) {
printk("dvb_bt8xx: dvb_dmxdev_init failed (errno = %d)\n", result);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
@ -591,7 +612,7 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
@ -603,7 +624,7 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
@ -614,11 +635,11 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
dvb_net_init(card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
@ -648,7 +669,7 @@ static int dvb_bt8xx_probe(struct device *dev)
case BTTV_PINNACLESAT:
card->gpio_mode = 0x0400c060;
/* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
card->op_sync_orin = 0;
card->irq_err_ignore = 0;
break;
@ -759,7 +780,7 @@ static int dvb_bt8xx_remove(struct device *dev)
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
if (card->fe) dvb_unregister_frontend(card->fe);
dvb_unregister_adapter(card->dvb_adapter);
dvb_unregister_adapter(&card->dvb_adapter);
kfree(card);

View File

@ -31,7 +31,7 @@
#include "bttv.h"
#include "mt352.h"
#include "sp887x.h"
#include "dst.h"
#include "dst_common.h"
#include "nxt6000.h"
#include "cx24110.h"
#include "or51211.h"
@ -40,7 +40,7 @@ struct dvb_bt8xx_card {
struct semaphore lock;
int nfeeds;
char card_name[32];
struct dvb_adapter *dvb_adapter;
struct dvb_adapter dvb_adapter;
struct bt878 *bt;
unsigned int bttv_nr;
struct dvb_demux demux;

Some files were not shown because too many files have changed in this diff Show More