forked from Minki/linux
Merge with master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
This commit is contained in:
commit
7063e6c717
5
CREDITS
5
CREDITS
@ -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
|
||||
|
205
Documentation/dvb/README.flexcop
Normal file
205
Documentation/dvb/README.flexcop
Normal 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.
|
@ -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
|
||||
|
219
Documentation/dvb/ci.txt
Normal file
219
Documentation/dvb/ci.txt
Normal 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.
|
@ -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);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
2
Makefile
2
Makefile
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#define INCLUDES
|
||||
#include "compat_ioctl.c"
|
||||
#include <asm/ioctl32.h>
|
||||
|
||||
#define IOCTL_NR(a) ((a) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
|
101
arch/x86_64/kernel/pmtimer.c
Normal file
101
arch/x86_64/kernel/pmtimer.c
Normal 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);
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
@ -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,
|
||||
};
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 **/
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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...
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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)");
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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/
|
||||
|
@ -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,
|
||||
};
|
@ -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);
|
164
drivers/media/dvb/b2c2/flexcop-common.h
Normal file
164
drivers/media/dvb/b2c2/flexcop-common.h
Normal 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
|
149
drivers/media/dvb/b2c2/flexcop-dma.c
Normal file
149
drivers/media/dvb/b2c2/flexcop-dma.c
Normal 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);
|
153
drivers/media/dvb/b2c2/flexcop-eeprom.c
Normal file
153
drivers/media/dvb/b2c2/flexcop-eeprom.c
Normal 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);
|
403
drivers/media/dvb/b2c2/flexcop-fe-tuner.c
Normal file
403
drivers/media/dvb/b2c2/flexcop-fe-tuner.c
Normal 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;
|
||||
}
|
204
drivers/media/dvb/b2c2/flexcop-hw-filter.c
Normal file
204
drivers/media/dvb/b2c2/flexcop-hw-filter.c
Normal 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);
|
||||
}
|
210
drivers/media/dvb/b2c2/flexcop-i2c.c
Normal file
210
drivers/media/dvb/b2c2/flexcop-i2c.c
Normal 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;
|
||||
}
|
66
drivers/media/dvb/b2c2/flexcop-misc.c
Normal file
66
drivers/media/dvb/b2c2/flexcop-misc.c
Normal 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);
|
||||
}
|
381
drivers/media/dvb/b2c2/flexcop-pci.c
Normal file
381
drivers/media/dvb/b2c2/flexcop-pci.c
Normal 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");
|
701
drivers/media/dvb/b2c2/flexcop-reg.h
Normal file
701
drivers/media/dvb/b2c2/flexcop-reg.h
Normal 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
|
403
drivers/media/dvb/b2c2/flexcop-sram.c
Normal file
403
drivers/media/dvb/b2c2/flexcop-sram.c
Normal 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
|
577
drivers/media/dvb/b2c2/flexcop-usb.c
Normal file
577
drivers/media/dvb/b2c2/flexcop-usb.c
Normal 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");
|
119
drivers/media/dvb/b2c2/flexcop-usb.h
Normal file
119
drivers/media/dvb/b2c2/flexcop-usb.h
Normal 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
|
286
drivers/media/dvb/b2c2/flexcop.c
Normal file
286
drivers/media/dvb/b2c2/flexcop.c
Normal 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");
|
30
drivers/media/dvb/b2c2/flexcop.h
Normal file
30
drivers/media/dvb/b2c2/flexcop.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
@ -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
|
861
drivers/media/dvb/bt8xx/dst_ca.c
Normal file
861
drivers/media/dvb/bt8xx/dst_ca.c
Normal 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");
|
58
drivers/media/dvb/bt8xx/dst_ca.h
Normal file
58
drivers/media/dvb/bt8xx/dst_ca.h
Normal 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
|
153
drivers/media/dvb/bt8xx/dst_common.h
Normal file
153
drivers/media/dvb/bt8xx/dst_common.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user