diff options
| author | Len Brown <len.brown@intel.com> | 2004-12-20 02:40:03 -0500 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2004-12-20 02:40:03 -0500 |
| commit | d5d4cbf800d2def1dc937afdf510b968e5beb0c0 (patch) | |
| tree | 0045ebeede42a289892e8a8bb4e85c44c029b197 | |
| parent | a3c76a77886d824ee896b1548104552bbb47e7e5 (diff) | |
| parent | 6484567f9f24417999208f2fbb018be555b1d810 (diff) | |
Merge intel.com:/home/lenb/bk/26-latest-ref
into intel.com:/home/lenb/src/26-latest-dev
422 files changed, 26366 insertions, 20919 deletions
diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 8787c4d099a6..6ee3cd6134df 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -160,7 +160,7 @@ pci_set_dma_mask(struct pci_device *dev, u64 mask) Checks to see if the mask is possible and updates the device parameters if it is. -Returns: 1 if successful and 0 if not +Returns: 0 if successful and a negative error if not. u64 dma_get_required_mask(struct device *dev) diff --git a/Documentation/arm/Sharp-LH/IOBarrier b/Documentation/arm/Sharp-LH/IOBarrier index bf34e046ea10..c0d8853672dc 100644 --- a/Documentation/arm/Sharp-LH/IOBarrier +++ b/Documentation/arm/Sharp-LH/IOBarrier @@ -5,7 +5,7 @@ Due to an unfortunate oversight when the Card Engines were designed, the signals that control access to some peripherals, most notably the SMC91C9111 ethernet controller, are not properly handled. -The symptom is that back to back IO with the peripheral returns +The symptom is that some back to back IO with the peripheral returns unreliable data. With the SMC chip, you'll see errors about the bank register being 'screwed'. @@ -13,20 +13,33 @@ The cause is that the AEN signal to the SMC chip does not transition for every memory access. It is driven through the CPLD from the CS7 line of the CPU's static memory controller which is optimized to eliminate unnecessary transitions. Yet, the SMC requires a transition -for every access. The Sharp website has more information on the -effect of this power conservation feature on peripheral interfacing. +for every write access. The Sharp website has more information about +the effect this power-conserving feature has on peripheral +interfacing. -The solution is to follow every access to the SMC chip with an access -to another memory region that will force the CPU to release the chip -select line. Note that it is important to guarantee that the access -will force the CPU off-chip. We map a page of SDRAM as if it were an -uncacheable IO device and read from it after every SMC IO operation. +The solution is to follow every write access to the SMC chip with an +access to another memory region that will force the CPU to release the +chip select line. It is important to guarantee that this access +forces the CPU off-chip. We map a page of SDRAM as if it were an +uncacheable IO device and read from it after every SMC IO write +operation. SMC IO BARRIER IO -You might be tempted to believe that we must access another device +Only this sequence is important. It does not matter that there is no +BARRIER IO before the access to the SMC chip because the AEN latch +only needs occurs after the SMC IO write cycle. The routines that +implement this work-around make an additional concession which is to +disable interrupts during the IO sequence. Other hardware devices +(the LogicPD CPLD) have registers in the same the physical memory +region as the SMC chip. An interrupt might allow an access to one of +those registers while SMC IO is being performed. + +You might be tempted to think that we have to access another device attached to the static memory controller, but the empirical evidence indicates that this is not so. Mapping 0x00000000 (flash) and 0xc0000000 (SDRAM) appear to have the same effect. Using SDRAM seems -to be faster. +to be faster. Choosing to access an undecoded memory region is not +desirable as there is no way to know how that chip select will be used +in the future. diff --git a/Documentation/dvb/README.dibusb b/Documentation/dvb/README.dibusb index d6449ec80999..e3d650b814dc 100644 --- a/Documentation/dvb/README.dibusb +++ b/Documentation/dvb/README.dibusb @@ -1,43 +1,91 @@ +Documentation for dib3000mb frontend driver and dibusb device driver +==================================================================== +Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de), -Documentation for dib3000mb frontend driver and dibusb device driver +dibusb and dib3000mb/mc drivers based on GPL code, which has + +Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) -The drivers should work with +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. -- Twinhan VisionPlus VisionDTV USB-Ter DVB-T Device (VP7041) - http://www.twinhan.com/ -- CTS Portable (Chinese Television System) - http://www.2cts.tv/ctsportable/ +Supported devices USB1.1 +======================== -- KWorld V-Stream XPERT DTV - DVB-T USB - http://www.kworld.com.tw/asp/pindex.asp?id=4&pid=13 +Produced and reselled by Twinhan: +--------------------------------- +- TwinhanDTV USB-Ter DVB-T Device (VP7041) + http://www.twinhan.com/product_terrestrial_3.asp + +- TwinhanDTV Magic Box (VP7041e) + http://www.twinhan.com/product_terrestrial_4.asp - HAMA DVB-T USB device http://www.hama.de/portal/articleId*110620/action*2598 -- DiBcom USB DVB-T reference device +- CTS Portable (Chinese Television System) + http://www.2cts.tv/ctsportable/ + +- Unknown USB DVB-T device with vendor ID Hyper-Paltek + + +Produced and reselled by KWorld: +-------------------------------- +- KWorld V-Stream XPERT DTV DVB-T USB + http://www.kworld.com.tw/en/product/DVBT-USB/DVBT-USB.html + +- JetWay DTV DVB-T USB + http://www.jetway.com.tw/evisn/product/lcd-tv/DVT-USB/dtv-usb.htm + +- ADSTech Instant TV DVB-T USB + http://www.adstech.com/products/PTV-333/intro/PTV-333_intro.asp?pid=PTV-333 -- Ultima Electronic/Artec T1 USB TVBOX - http://www.arteceuro.com/products-tvbox.html + +Others: +------- +- Ultima Electronic/Artec T1 USB TVBOX (AN2135 and AN2235) + http://82.161.246.249/products-tvbox.html - Compro Videomate DVB-U2000 - DVB-T USB http://www.comprousa.com/products/vmu2000.htm -- Unknown USB DVB-T device with vendor ID Hyper-Paltek +- Grandtec USB DVB-T + http://www.grand.com.tw/ -Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de), +- Avermedia AverTV DVBT USB + http://www.avermedia.com/ -both drivers based on GPL code, which has +- DiBcom USB DVB-T reference device (non-public) -Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) -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. +Supported devices USB2.0 +======================== +- Twinhan MagicBox II + http://www.twinhan.com/product_terrestrial_7.asp + +- Yakumo DVB-T mobile + http://www.yakumo.de/produkte/index.php?pid=1&ag=DVB-T + +- DiBcom USB2.0 DVB-T reference device (non-public) -NEWS: +0. NEWS: + 2004-12-06 - possibility for demod i2c-address probing + - new usb IDs (Compro,Artec) + 2004-11-23 - merged changes from DiB3000MC_ver2.1 + - revised the debugging + - possibility to deliver the complete TS for USB2.0 + 2004-11-21 - first working version of the dib3000mc/p frontend driver. + 2004-11-12 - added additional remote control keys. Thanks to Uwe Hanke. + 2004-11-07 - added remote control support. Thanks to David Matthews. + 2004-11-05 - added support for a new devices (Grandtec/Avermedia/Artec) + - merged my changes (for dib3000mb/dibusb) to the FE_REFACTORING, because it became HEAD + - moved transfer control (pid filter, fifo control) from usb driver to frontend, it seems + better settled there (added xfer_ops-struct) + - created a common files for frontends (mc/p/mb) 2004-09-28 - added support for a new device (Unkown, vendor ID is Hyper-Paltek) 2004-09-20 - added support for a new device (Compro DVB-U2000), thanks to Amaury Demol for reporting @@ -49,7 +97,7 @@ NEWS: (old news for vp7041.c) 2004-07-15 - found out, by accident, that the device has a TUA6010XS for - frequency generator + PLL 2004-07-12 - figured out, that the driver should also work with the CTS Portable (Chinese Television System) 2004-07-08 - firmware-extraction-2.422-problem solved, driver is now working @@ -67,7 +115,7 @@ NEWS: 1. How to use? NOTE: This driver was developed using Linux 2.6.6., -it is working with 2.6.7, 2.6.8.1. +it is working with 2.6.7, 2.6.8.1, 2.6.9 . Linux 2.4.x support is not planned, but patches are very welcome. @@ -81,8 +129,15 @@ The USB driver needs to download a firmware to start working. You can either use "get_dvb_firmware dibusb" to download the firmware or you can get it directly via +for USB1.1 (AN2135) http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-5.0.0.11.fw?rev=1.1&content-type=text/plain +for USB1.1 (AN2235) (a few Artec T1 devices) +http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-an2235-1.fw?rev=1.1&content-type=text/plain + +for USB2.0 (FX2) +http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-6.0.0.5.fw?rev=1.1&content-type=text/plain + 1.2. Compiling Since the driver is in the linux kernel, activating the driver in @@ -94,15 +149,16 @@ to compile the driver as module. Hotplug does the rest. Hotplug is able to load the driver, when it is needed (because you plugged in the device). -If you want to enable debug output, you have to load the driver manually. +If you want to enable debug output, you have to load the driver manually and +from withing the dvb-kernel cvs repository. first have a look, which debug level are available: -modinfo dvb-dibusb modinfo dib3000mb +modinfo dvb-dibusb -modprobe dvb-dibusb debug=<level> modprobe dib3000mb debug=<level> +modprobe dvb-dibusb debug=<level> should do the trick. @@ -118,21 +174,17 @@ as a slave device in vdr, was not working for me. Some work has to be done 2. Known problems and bugs TODO: -- remote control tasklet - signal-quality and strength calculations -- debug messages restructure -- i2c address probing -- 2.1. Adding support for devices It is not possible to determine the range of devices based on the DiBcom -reference design. This is because the reference design of DiBcom can be sold -to third persons, without telling DiBcom (so done with the Twinhan VP7041 and +reference designs. This is because the reference design of DiBcom can be sold +to thirds, without telling DiBcom (so done with the Twinhan VP7041 and the HAMA device). When you think you have a device like this and the driver does not recognizes it, -please send the ****load.inf and the ****cap.inf of the Windows driver to me. +please send the ****load*.inf and the ****cap*.inf of the Windows driver to me. Sometimes the Vendor or Product ID is identical to the ones of Twinhan, even though it is not a Twinhan device (e.g. HAMA), then please send me the name @@ -144,7 +196,29 @@ the dvb-dibusb.h-file and create a patch and send it over to me or to the linux-dvb mailing list, _after_ you have tried compiling and modprobing it. -2.2. Comments +2.2. USB1.1 Bandwidth limitation + +Most of the current supported devices are USB1.1 and thus they have a +maximum bandwidth of about 5-6 MBit/s when connected to a USB2.0 hub. +This is not enough for receiving the complete transport stream of a +DVB-T channel (which can be about 16 MBit/s). Normally this is not a +problem, if you only want to watch TV, but watching a channel while +recording another channel on the same frequency simply does not work. +This applies to all USB1.1 DVB-T devices. + +A special problem of the dibusb for the USB1.1 is, that the USB control +IC has a problem with write accesses while having MPEG2-streaming +enabled. When you set another pid while receiving MPEG2-TS it happens, that +the stream is disturbed and probably data is lost (results in distortions of +the video or strange beeps within the audio stream). DiBcom is preparing a +firmware especially for Linux which perhaps solves the problem. + +Especially VDR users are victoms of this bug. VDR frequently requests new PIDs +due the automatic scanning (introduced in 1.3.x, afaik) and epg-scan. Disabling +these features is maybe a solution. Additionally this behaviour of VDR exceeds +the USB1.1 bandwidth. + +2.3. Comments Patches, comments and suggestions are very very welcome @@ -153,6 +227,9 @@ Patches, comments and suggestions are very very welcome providing specs, code and help, on which the dvb-dibusb and dib3000mb are based. + David Matthews for identifying a new device type (Artec T1 with AN2235) + and for extending dibusb with remote control event handling. Thank you. + Alex Woods for frequently answering question about usb and dvb stuff, a big thank you diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt index 43e1a8a97881..efdc4ee9d40c 100644 --- a/Documentation/dvb/cards.txt +++ b/Documentation/dvb/cards.txt @@ -38,7 +38,7 @@ o Frontends drivers: Comtech DVBT-6k07 (SP5730 PLL) (NxtWave Communications NXT6000 demodulator) - sp887x : Microtune 7202D - - dib3000mb : DiBcom 3000-MB Frontend + - dib3000mb : DiBcom 3000-MB demodulator DVB-S/C/T: - dst : TwinHan DST Frontend @@ -69,7 +69,17 @@ o Technotrend / Hauppauge DVB USB devices: o DiBcom DVB-T USB based devices: - Twinhan VisionPlus VisionDTV USB-Ter DVB-T Device - - KWorld V-Stream XPERT DTV - DVB-T USB - HAMA DVB-T USB device + - CTS Portable (Chinese Television System) + - KWorld V-Stream XPERT DTV DVB-T USB + - JetWay DTV DVB-T USB + - ADSTech Instant TV DVB-T USB + - Ultima Electronic/Artec T1 USB TVBOX (AN2135 and AN2235) + - Compro Videomate DVB-U2000 - DVB-T USB + - Grandtec USB DVB-T + - Avermedia AverTV DVBT USB + - DiBcom USB DVB-T reference device (non-public) + - Yakumo DVB-T mobile USB2.0 + - DiBcom USB2.0 DVB-T reference device (non-public) o Experimental support for the analog module of the Siemens DVB-C PCI card diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index f3a8fef98192..e9964b71e124 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -21,7 +21,7 @@ use File::Temp qw/ tempdir /; use IO::Handle; -@components = ( "alps_tdlb7", "sp887x", "tda10045", "tda10046", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb" ); +@components = ( "sp8870", "sp887x", "tda10045", "tda10046", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb" ); # Check args syntax() if (scalar(@ARGV) != 1); @@ -32,7 +32,7 @@ for($i=0; $i < scalar(@components); $i++) { if ($cid eq $components[$i]) { $outfile = eval($cid); die $@ if $@; - print STDERR "Firmware $outfile extracted successfully. Now copy it to /usr/lib/hotplug/firmware/.\n"; + print STDERR "Firmware $outfile extracted successfully. Now copy it to either /lib/firmware or /usr/lib/hotplug/firmware/ (depending on your hotplug version).\n"; exit(0); } } @@ -47,11 +47,11 @@ syntax(); # --------------------------------------------------------------- # Firmware-specific extraction subroutines -sub alps_tdlb7 { +sub sp8870 { my $sourcefile = "tt_Premium_217g.zip"; my $url = "http://www.technotrend.de/new/217g/$sourcefile"; my $hash = "53970ec17a538945a6d8cb608a7b3899"; - my $outfile = "dvb-fe-tdlb7.fw"; + my $outfile = "dvb-fe-sp8870.fw"; my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); checkstandard(); diff --git a/Documentation/ia64/serial.txt b/Documentation/ia64/serial.txt new file mode 100644 index 000000000000..f51eb4bc2ff1 --- /dev/null +++ b/Documentation/ia64/serial.txt @@ -0,0 +1,144 @@ +SERIAL DEVICE NAMING + + As of 2.6.10, serial devices on ia64 are named based on the + order of ACPI and PCI enumeration. The first device in the + ACPI namespace (if any) becomes /dev/ttyS0, the second becomes + /dev/ttyS1, etc., and PCI devices are named sequentially + starting after the ACPI devices. + + Prior to 2.6.10, there were confusing exceptions to this: + + - Firmware on some machines (mostly from HP) provides an HCDP + table[1] that tells the kernel about devices that can be used + as a serial console. If the user specified "console=ttyS0" + or the EFI ConOut path contained only UART devices, the + kernel registered the device described by the HCDP as + /dev/ttyS0. + + - If there was no HCDP, we assumed there were UARTs at the + legacy COM port addresses (I/O ports 0x3f8 and 0x2f8), so + the kernel registered those as /dev/ttyS0 and /dev/ttyS1. + + Any additional ACPI or PCI devices were registered sequentially + after /dev/ttyS0 as they were discovered. + + With an HCDP, device names changed depending on EFI configuration + and "console=" arguments. Without an HCDP, device names didn't + change, but we registered devices that might not really exist. + + For example, an HP rx1600 with a single built-in serial port + (described in the ACPI namespace) plus an MP[2] (a PCI device) has + these ports: + + pre-2.6.10 pre-2.6.10 + MMIO (EFI console (EFI console + address on builtin) on MP port) 2.6.10 + ========== ========== ========== ====== + builtin 0xff5e0000 ttyS0 ttyS1 ttyS0 + MP UPS 0xf8031000 ttyS1 ttyS2 ttyS1 + MP Console 0xf8030000 ttyS2 ttyS0 ttyS2 + MP 2 0xf8030010 ttyS3 ttyS3 ttyS3 + MP 3 0xf8030038 ttyS4 ttyS4 ttyS4 + +CONSOLE SELECTION + + EFI knows what your console devices are, but it doesn't tell the + kernel quite enough to actually locate them. The DIG64 HCDP + table[1] does tell the kernel where potential serial console + devices are, but not all firmware supplies it. Also, EFI supports + multiple simultaneous consoles and doesn't tell the kernel which + should be the "primary" one. + + So how do you tell Linux which console device to use? + + - If your firmware supplies the HCDP, it is simplest to + configure EFI with a single device (either a UART or a VGA + card) as the console. Then you don't need to tell Linux + anything; the kernel will automatically use the EFI console. + + (This works only in 2.6.6 or later; prior to that you had + to specify "console=ttyS0" to get a serial console.) + + - Without an HCDP, Linux defaults to a VGA console unless you + specify a "console=" argument. + + NOTE: Don't assume that a serial console device will be /dev/ttyS0. + It might be ttyS1, ttyS2, etc. Make sure you have the appropriate + entries in /etc/inittab (for getty) and /etc/securetty (to allow + root login). + +EARLY SERIAL CONSOLE + + The kernel can't start using a serial console until it knows where + the device lives. Normally this happens when the driver enumerates + all the serial devices, which can happen a minute or more after the + kernel starts booting. + + 2.6.10 and later kernels have an "early uart" driver that works + very early in the boot process. The kernel will automatically use + this if the user supplies an argument like "console=uart,io,0x3f8", + or if the EFI console path contains only a UART device and the + firmware supplies an HCDP. + +TROUBLESHOOTING SERIAL CONSOLE PROBLEMS + + No kernel output after elilo prints "Uncompressing Linux... done": + + - You specified "console=ttyS0" but Linux changed the device + to which ttyS0 refers. Configure exactly one EFI console + device[3] and remove the "console=" option. + + - The EFI console path contains both a VGA device and a UART. + EFI and elilo use both, but Linux defaults to VGA. Remove + the VGA device from the EFI console path[3]. + + - Multiple UARTs selected as EFI console devices. EFI and + elilo use all selected devices, but Linux uses only one. + Make sure only one UART is selected in the EFI console + path[3]. + + - You're connected to an HP MP port[2] but have a non-MP UART + selected as EFI console device. EFI uses the MP as a + console device even when it isn't explicitly selected. + Either move the console cable to the non-MP UART, or change + the EFI console path[3] to the MP UART. + + Long pause (60+ seconds) between "Uncompressing Linux... done" and + start of kernel output: + + - No early console because you used "console=ttyS<n>". Remove + the "console=" option if your firmware supplies an HCDP. + + - If you don't have an HCDP, the kernel doesn't know where + your console lives until the driver discovers serial + devices. Use "console=uart, io,0x3f8" (or appropriate + address for your machine). + + Kernel and init script output works fine, but no "login:" prompt: + + - Add getty entry to /etc/inittab for console tty. Look for + the "Adding console on ttyS<n>" message that tells you which + device is the console. + + "login:" prompt, but can't login as root: + + - Add entry to /etc/securetty for console tty. + + + +[1] http://www.dig64.org/specifications/DIG64_PCDPv20.pdf + The table was originally defined as the "HCDP" for "Headless + Console/Debug Port." The current version is the "PCDP" for + "Primary Console and Debug Port Devices." + +[2] The HP MP (management processor) is a PCI device that provides + several UARTs. One of the UARTs is often used as a console; the + EFI Boot Manager identifies it as "Acpi(HWP0002,700)/Pci(...)/Uart". + The external connection is usually a 25-pin connector, and a + special dongle converts that to three 9-pin connectors, one of + which is labelled "Console." + +[3] EFI console devices are configured using the EFI Boot Manager + "Boot option maintenance" menu. You may have to interrupt the + boot sequence to use this menu, and you will have to reset the + box after changing console configuration. diff --git a/Documentation/ioctl/cdrom.txt b/Documentation/ioctl/cdrom.txt index acc8f3a3bac0..4ccdcc6fe364 100644 --- a/Documentation/ioctl/cdrom.txt +++ b/Documentation/ioctl/cdrom.txt @@ -34,7 +34,7 @@ are as follows: (struct cdrom_multisession) CDROM_GET_MCN Obtain the "Universal Product Code" if available (struct cdrom_mcn) - CDROM_GET_UPC CDROM_GET_MCN (deprecated) + CDROM_GET_UPC Deprecated, use CDROM_GET_MCN instead. CDROMRESET hard-reset the drive CDROMVOLREAD Get the drive's volume setting (struct cdrom_volctrl) @@ -44,8 +44,8 @@ are as follows: CDROMSEEK seek msf address CDROMPLAYBLK scsi-cd only, (struct cdrom_blk) CDROMREADALL read all 2646 bytes - CDROMGETSPINDOWN - CDROMSETSPINDOWN + CDROMGETSPINDOWN return 4-bit spindown value + CDROMSETSPINDOWN set 4-bit spindown value CDROMCLOSETRAY pendant of CDROMEJECT CDROM_SET_OPTIONS Set behavior options CDROM_CLEAR_OPTIONS Clear behavior options @@ -79,10 +79,12 @@ code. It is likely that some corrections will be made over time. General: Unless otherwise specified, all ioctl calls return 0 on success - and -1 with errno set to an appropriate value on error. + and -1 with errno set to an appropriate value on error. (Some + ioctls return non-negative data values.) - Unless otherwise specified, all ioctl calls return EFAULT on a - failed attempt to copy data to or from user address space. + Unless otherwise specified, all ioctl calls return -1 and set + errno to EFAULT on a failed attempt to copy data to or from user + address space. Individual drivers may return error codes not listed here. @@ -136,6 +138,9 @@ CDROMPLAYMSF Play Audio MSF (struct cdrom_msf) ENOSYS cd drive not audio-capable. notes: + MSF stands for minutes-seconds-frames + LBA stands for logical block address + Segment is described as start and end times, where each time is described as minutes:seconds:frames. A frame is 1/75 of a second. @@ -196,8 +201,11 @@ CDROMREADTOCENTRY Read TOC entry (struct cdrom_tocentry) error return: ENOSYS cd drive not audio-capable. EINVAL entry.cdte_format not CDROM_MSF or CDROM_LBA + EINVAL requested track out of bounds + EIO I/O error reading TOC notes: + TOC stands for Table Of Contents MSF stands for minutes-seconds-frames LBA stands for logical block address @@ -216,6 +224,10 @@ CDROMSTOP Stop the cdrom drive error return: ENOSYS cd drive not audio-capable. + notes: + Exact interpretation of this ioctl depends on the device, + but most seem to spin the drive down. + CDROMSTART Start the cdrom drive @@ -230,6 +242,11 @@ CDROMSTART Start the cdrom drive error return: ENOSYS cd drive not audio-capable. + notes: + Exact interpretation of this ioctl depends on the device, + but most seem to spin the drive up and/or close the tray. + Other devices ignore the ioctl completely. + CDROMEJECT Ejects the cdrom media @@ -241,9 +258,12 @@ CDROMEJECT Ejects the cdrom media outputs: none - error return: + error returns: ENOSYS cd drive not capable of ejecting - EBUSY other processes have drive open or door is locked + EBUSY other processes are accessing drive, or door is locked + + notes: + See CDROM_LOCKDOOR, below. @@ -257,9 +277,12 @@ CDROMCLOSETRAY pendant of CDROMEJECT outputs: none - error return: + error returns: ENOSYS cd drive not capable of ejecting - EBUSY other processes have drive open or door is locked + EBUSY other processes are accessing drive, or door is locked + + notes: + See CDROM_LOCKDOOR, below. @@ -577,7 +600,7 @@ CDROM_SET_OPTIONS Set behavior options inputs: New values for drive options. The logical 'or' of: - CDO_AUTO_CLOSE close tray on first open + CDO_AUTO_CLOSE close tray on first open(2) CDO_AUTO_EJECT open tray on last release CDO_USE_FFLAGS use O_NONBLOCK information on open CDO_LOCK lock tray on open files @@ -918,6 +941,10 @@ CDROM_NEXT_WRITABLE get next writable block outputs: The next writable block. + notes: + If the device does not support this ioctl directly, the + ioctl will return CDROM_LAST_WRITTEN + 7. + CDROM_LAST_WRITTEN get last block written on disc @@ -925,11 +952,15 @@ CDROM_LAST_WRITTEN get last block written on disc usage: long last; - ioctl(fd, CDROM_NEXT_WRITABLE, &last); + ioctl(fd, CDROM_LAST_WRITTEN, &last); inputs: none outputs: The last block written on disc - + notes: + If the device does not support this ioctl directly, the + result is derived from the disc's table of contents. If the + table of contents can't be read, this ioctl returns an + error. diff --git a/Documentation/ioctl/hdio.txt b/Documentation/ioctl/hdio.txt index f5b8ef98e6e8..c42d3b68577e 100644 --- a/Documentation/ioctl/hdio.txt +++ b/Documentation/ioctl/hdio.txt @@ -28,7 +28,7 @@ are as follows: HDIO_GET_IDENTITY get IDE identification info HDIO_GET_WCACHE get write cache mode on|off HDIO_GET_ACOUSTIC get acoustic value - HDIO_GET_ADDRESS + HDIO_GET_ADDRESS get sector addressing mode HDIO_GET_BUSSTATE get the bus state of the hwif HDIO_TRISTATE_HWIF execute a channel tristate HDIO_DRIVE_RESET execute a device reset @@ -55,8 +55,8 @@ are as follows: HDIO_SET_QDMA change use-qdma flag HDIO_SET_ADDRESS change lba addressing modes - HDIO_SET_IDE_SCSI - HDIO_SET_SCSI_IDE + HDIO_SET_IDE_SCSI Set scsi emulation mode on/off + HDIO_SET_SCSI_IDE not implemented yet The information that follows was determined from reading kernel source @@ -73,8 +73,9 @@ General: Unless otherwise specified, all ioctl calls return 0 on success and -1 with errno set to an appropriate value on error. - Unless otherwise specified, all ioctl calls return EFAULT on a - failed attempt to copy data to or from user address space. + Unless otherwise specified, all ioctl calls return -1 and set + errno to EFAULT on a failed attempt to copy data to or from user + address space. Unless otherwise specified, all data structures and constants are defined in <linux/hdreg.h> @@ -145,7 +146,7 @@ HDIO_SET_UNMASKINTR permit other irqs during I/O usage: - long val; + unsigned long val; ioctl(fd, HDIO_SET_UNMASKINTR, val); inputs: @@ -204,7 +205,7 @@ HDIO_SET_MULTCOUNT change IDE blockmode This is tightly woven into the driver->do_special can not touch. DON'T do it again until a total personality rewrite - is committed." + is committed. If blockmode has already been set, this ioctl will fail with EBUSY @@ -371,14 +372,12 @@ HDIO_SET_NICE set nice flags usage: - int nice; + unsigned long nice; ... ioctl(fd, HDIO_SET_NICE, nice); inputs: - args[0] io address to probe - args[1] control address to probe - args[2] irq number + bitmask of nice flags. outputs: none @@ -392,6 +391,9 @@ HDIO_SET_NICE set nice flags This ioctl sets the DSC_OVERLAP and NICE_1 flags from values provided by the user. + Nice flags are listed in <linux/hdreg.h>, starting with + IDE_NICE_DSC_OVERLAP. These values represent shifts. + @@ -509,7 +511,7 @@ HDIO_DRIVE_RESET execute a device reset notes: - Aborts any current command, prevent anything else from being + Abort any current command, prevent anything else from being queued, execute a reset on the device, and issue BLKRRPART ioctl on the block device. @@ -523,6 +525,10 @@ HDIO_DRIVE_TASKFILE execute raw taskfile Note: If you don't have a copy of the ANSI ATA specification handy, you should probably ignore this ioctl. + Execute an ATA disk command directly by writing the "taskfile" + registers of the drive. Requires ADMIN and RAWIO access + privileges. + usage: struct { @@ -541,27 +547,27 @@ HDIO_DRIVE_TASKFILE execute raw taskfile (See below for details on memory area passed to ioctl.) - io_ports[] values to be written to taskfile registers - hob_ports[] values to be written to taskfile registers + io_ports[8] values to be written to taskfile registers + hob_ports[8] high-order bytes, for extended commands. out_flags flags indicating which registers are valid in_flags flags indicating which registers should be returned data_phase see below req_cmd command type to be executed out_size size of output buffer outbuf buffer of data to be transmitted to disk - inbuf buffer of data to be received from disk + inbuf buffer of data to be received from disk (see [1]) outputs: io_ports[] values returned in the taskfile registers - hob_ports[] values returned in the taskfile registers - out_flags flags indicating which registers are valid + hob_ports[] high-order bytes, for extended commands. + out_flags flags indicating which registers are valid (see [2]) in_flags flags indicating which registers should be returned - outbuf buffer of data to be transmitted to disk + outbuf buffer of data to be transmitted to disk (see [1]) inbuf buffer of data to be received from disk error returns: - EACCES CAP_SYS_ADMIN or CAP_SYS_RAWIO privelege not set. + EACCES CAP_SYS_ADMIN or CAP_SYS_RAWIO privilege not set. ENOMSG Device is not a disk drive. ENOMEM Unable to allocate memory for task EFAULT req_cmd == TASKFILE_IN_OUT (not implemented as of 2.6.8) @@ -571,9 +577,14 @@ HDIO_DRIVE_TASKFILE execute raw taskfile notes: - Execute an ATA disk command directly by writing the "taskfile" - registers of the drive. Requires ADMIN and RAWIO access - privileges. + [1] Currently (2.6.8), both the input and output buffers are + copied from the user and written back to the user, even when + not used. This may be a bug. + + [2] The out_flags and in_flags are returned to the user after + the ioctl completes. Currently (2.6.8) these are the same + as the input values, unchanged. In the future, they may have + more significance. Extreme caution should be used with using this ioctl. A mistake can easily corrupt data or hang the system. @@ -590,7 +601,7 @@ HDIO_DRIVE_TASKFILE execute raw taskfile hob_ports[8] high-order bytes, for extended commands out_flags flags indicating which entries in the io_ports[] and hob_ports[] arrays - contain valid values. + contain valid values. Type ide_reg_valid_t. in_flags flags indicating which entries in the io_ports[] and hob_ports[] arrays are expected to contain valid values @@ -600,8 +611,11 @@ HDIO_DRIVE_TASKFILE execute raw taskfile out_size output (user->drive) buffer size, bytes in_size input (drive->user) buffer size, bytes - Unused fields of io_ports[] and hob_ports[] should be set to - zero. + This ioctl does not necessarily respect all flags in the + out_flags and in_flags values -- some taskfile registers + may be written or read even if not requested in the flags. + Unused fields of io_ports[] and hob_ports[] should be set + to zero. The data_phase field describes the data transfer to be performed. Value is one of: @@ -631,10 +645,6 @@ HDIO_DRIVE_TASKFILE execute raw taskfile IDE_DRIVE_TASK_OUT IDE_DRIVE_TASK_RAW_WRITE - Currently (2.6.8), both the input and output buffers are - copied from the user and written back to the user, even when - not used. - @@ -666,11 +676,17 @@ HDIO_DRIVE_CMD execute a special drive command args[0] status args[1] error args[2] NSECTOR + args[3] undefined + args[4+] NSECTOR * 512 bytes of data returned by the command. error returns: EACCES Access denied: requires CAP_SYS_RAWIO ENOMEM Unable to allocate memory for task + notes: + + Taskfile registers IDE_LCYL, IDE_HCYL, and IDE_SELECT are + set to zero before executing the command. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7168ecf167da..ed2fbf2da775 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -701,6 +701,9 @@ running once the system is up. mem=nn[KMG] [KNL,BOOT] Force usage of a specific amount of memory Amount of memory to be used when the kernel is not able to see the whole system memory or for test. + [IA-32] Use together with memmap= to avoid physical + address space collisions. Without memmap= PCI devices + could be placed at addresses belonging to unused RAM. mem=nopentium [BUGS=IA-32] Disable usage of 4MB pages for kernel memory. @@ -1271,11 +1274,6 @@ running once the system is up. specialix= [HW,SERIAL] Specialix multi-serial port adapter See Documentation/specialix.txt. - speedstep_coppermine= - [HW,IA-32] Take CPU in your notebook as SpeedStep-capable - See comment before function speedstep_setup() in - arch/i386/kernel/cpu/cpufreq/speedstep.c. - spia_io_base= [HW,MTD] spia_fio_base= spia_pedr= diff --git a/Documentation/memory.txt b/Documentation/memory.txt index 7af1709e8fac..2b3dedd39538 100644 --- a/Documentation/memory.txt +++ b/Documentation/memory.txt @@ -21,6 +21,8 @@ systems. All of these problems can be addressed with the "mem=XXXM" boot option (where XXX is the size of RAM to use in megabytes). It can also tell Linux to use less memory than is actually installed. +If you use "mem=" on a machine with PCI, consider using "memmap=" to avoid +physical address space collisions. See the documentation of your boot loader (LILO, loadlin, etc.) about how to pass options to the kernel. @@ -44,7 +46,9 @@ Try: * Disabling the cache from the BIOS. * Try passing the "mem=4M" option to the kernel to limit - Linux to using a very small amount of memory. + Linux to using a very small amount of memory. Use "memmap="-option + together with "mem=" on systems with PCI to avoid physical address + space collisions. Other tricks: diff --git a/Documentation/rocket.txt b/Documentation/rocket.txt index f9345cd14e16..a10678004451 100644 --- a/Documentation/rocket.txt +++ b/Documentation/rocket.txt @@ -20,8 +20,27 @@ or installing it into kernels which do not have the driver configured into them. Installations instructions for the external module are in the included README and HW_INSTALL files. -RocketPort ISA and RocketModem II PCI boards are also supported by this -driver, but must use the external module driver for configuration reasons. +RocketPort ISA and RocketModem II PCI boards currently are only supported by +this driver in module form. + +The RocketPort ISA board requires I/O ports to be configured by the DIP +switches on the board. See the section "ISA Rocketport Boards" below for +information on how to set the DIP switches. + +You pass the I/O port to the driver using the following module parameters: + +board1 : I/O port for the first ISA board +board2 : I/O port for the second ISA board +board3 : I/O port for the third ISA board +board4 : I/O port for the fourth ISA board + +There is a set of utilities and scripts provided with the external driver +( downloadable from http://www.comtrol.com ) that ease the configuration and +setup of the ISA cards. + +The RocketModem II PCI boards require firmware to be loaded into the card +before it will function. The driver has only been tested as a module for this +board. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -55,12 +74,95 @@ create the RocketPort/RocketModem device names, use the command >mknod /dev/ttyR1 c 46 1 >mknod /dev/ttyR2 c 46 2 -The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes) for you: +The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes) +for you: >/dev/MAKEDEV ttyR =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +ISA Rocketport Boards +--------------------- + +You must assign and configure the I/O addresses used by the ISA Rocketport +card before installing and using it. This is done by setting a set of DIP +switches on the Rocketport board. + + +SETTING THE I/O ADDRESS +----------------------- + +Before installing RocketPort(R) or RocketPort RA boards, you must find +a range of I/O addresses for it to use. The first RocketPort card +requires a 68-byte contiguous block of I/O addresses, starting at one +of the following: 0x100h, 0x140h, 0x180h, 0x200h, 0x240h, 0x280h, +0x300h, 0x340h, 0x380h. This I/O address must be reflected in the DIP +switiches of *all* of the Rocketport cards. + +The second, third, and fourth RocketPort cards require a 64-byte +contiguous block of I/O addresses, starting at one of the following +I/O addresses: 0x100h, 0x140h, 0x180h, 0x1C0h, 0x200h, 0x240h, 0x280h, +0x2C0h, 0x300h, 0x340h, 0x380h, 0x3C0h. The I/O address used by the +second, third, and fourth Rocketport cards (if present) are set via +software control. The DIP switch settings for the I/O address must be +set to the value of the first Rocketport cards. + +In order to destinguish each of the card from the others, each card +must have a unique board ID set on the dip switches. The first +Rocketport board must be set with the DIP switches corresponding to +the first board, the second board must be set with the DIP switches +corresponding to the second board, etc. IMPORTANT: The board ID is +the only place where the DIP switch settings should differ between the +various Rocketport boards in a system. + +The I/O address range used by any of the RocketPort cards must not +conflict with any other cards in the system, including other +RocketPort cards. Below, you will find a list of commonly used I/O +address ranges which may be in use by other devices in your system. +On a Linux system, "cat /proc/ioports" will also be helpful in +identifying what I/O addresses are being used by devics on your +system. + +Remember, the FIRST RocketPort uses 68 I/O addresses. So, if you set it +for 0x100, it will occupy 0x100 to 0x143. This would mean that you +CAN NOT set the second, third or fourth board for address 0x140 since +the first 4 bytes of that range are used by the first board. You would +need to set the second, third, or fourth board to one of the next available +blocks such as 0x180. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +RocketPort and RocketPort RA SW1 Settings: + + +-------------------------------+ + | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + +-------+-------+---------------+ + | Unused| Card | I/O Port Block| + +-------------------------------+ + +DIP Switches DIP Switches +7 8 6 5 +=================== =================== +On On UNUSED, MUST BE ON. On On First Card <==== Default + On Off Second Card + Off On Third Card + Off Off Fourth Card + +DIP Switches I/O Address Range +4 3 2 1 Used by the First Card +===================================== +On Off On Off 100-143 +On Off Off On 140-183 +On Off Off Off 180-1C3 <==== Default +Off On On Off 200-243 +Off On Off On 240-283 +Off On Off Off 280-2C3 +Off Off On Off 300-343 +Off Off Off On 340-383 +Off Off Off Off 380-3C3 + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + REPORTING BUGS -------------- diff --git a/MAINTAINERS b/MAINTAINERS index 30993e54f19c..8217e83941ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1731,6 +1731,12 @@ M: tsbogend@alpha.franken.de L: linux-net@vger.kernel.org S: Maintained +PHRAM MTD DRIVER +P: Jörn Engel +M: joern@wh.fh-wedel.de +L: linux-mtd@lists.infradead.org +S: Maintained + POSIX CLOCKS and TIMERS P: George Anzinger M: george@mvista.com diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ff65ff8dce15..da2c65c9baf3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -317,31 +317,40 @@ config ISA_DMA config FIQ bool - depends on ARCH_ACORN || ARCH_L7200 || ARCH_LH7A400 + depends on ARCH_ACORN || ARCH_L7200 default y # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. -config ZBOOT_ROM - bool "Compressed boot loader in ROM/flash" - help - Say Y here if you intend to execute your compressed kernel image (zImage) - directly from ROM or flash. If unsure, say N. - config ZBOOT_ROM_TEXT hex "Compressed ROM boot loader base address" default "0" help - The base address for zImage. Unless you have special requirements, you - should not change this value. + The physical address at which the ROM-able zImage is to be + placed in the target. Platforms which normally make use of + ROM-able zImage formats normally set this to a suitable + value in their defconfig file. + + If ZBOOT_ROM is not enabled, this has no effect. config ZBOOT_ROM_BSS hex "Compressed ROM boot loader BSS address" default "0" help - The base address of 64KiB of read/write memory, which must be available - while the decompressor is running. Unless you have special requirements, - you should not change this value. + The base address of 64KiB of read/write memory in the target + for the ROM-able zImage, which must be available while the + decompressor is running. Platforms which normally make use of + ROM-able zImage formats normally set this to a suitable + value in their defconfig file. + + If ZBOOT_ROM is not enabled, this has no effect. + +config ZBOOT_ROM + bool "Compressed boot loader in ROM/flash" + depends on ZBOOT_ROM_TEXT != ZBOOT_ROM_BSS + help + Say Y here if you intend to execute your compressed kernel image + (zImage) directly from ROM or flash. If unsure, say N. config XIP_KERNEL bool "Kernel Execute-In-Place from ROM" diff --git a/arch/arm/configs/ep80219_defconfig b/arch/arm/configs/ep80219_defconfig index fc4dbc248561..e2c9926372ce 100644 --- a/arch/arm/configs/ep80219_defconfig +++ b/arch/arm/configs/ep80219_defconfig @@ -1,10 +1,13 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc3 +# Wed Dec 15 17:03:41 2004 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -16,6 +19,7 @@ CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -25,17 +29,20 @@ CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,6 +52,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y # @@ -59,6 +67,7 @@ CONFIG_KMOD=y # CONFIG_ARCH_INTEGRATOR is not set CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set @@ -67,7 +76,9 @@ CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set # # IOP3xx Implementation Options @@ -94,6 +105,7 @@ CONFIG_CPU_32=y CONFIG_CPU_XSCALE=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_TLB_V4WBI=y CONFIG_CPU_MINICACHE=y @@ -110,6 +122,7 @@ CONFIG_PCI=y # CONFIG_ZBOOT_ROM is not set CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_XIP_KERNEL is not set # CONFIG_PCI_LEGACY_PROC is not set CONFIG_PCI_NAMES=y @@ -119,7 +132,6 @@ CONFIG_PCI_NAMES=y CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set -# CONFIG_VFP is not set CONFIG_BINFMT_ELF=y CONFIG_BINFMT_AOUT=y # CONFIG_BINFMT_MISC is not set @@ -148,8 +160,8 @@ CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_REDBOOT_PARTS=y -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -191,7 +203,10 @@ CONFIG_MTD_CFI_UTIL=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0xf0000000 +CONFIG_MTD_PHYSMAP_LEN=0x00800000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_EDB7312 is not set @@ -232,8 +247,19 @@ CONFIG_MTD_CFI_UTIL=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # # Multi-device support (RAID and LVM) @@ -243,9 +269,11 @@ CONFIG_BLK_DEV_MD=y # CONFIG_MD_LINEAR is not set CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=y # CONFIG_MD_RAID6 is not set # CONFIG_MD_MULTIPATH is not set +# CONFIG_MD_FAULTY is not set CONFIG_BLK_DEV_DM=y # CONFIG_DM_CRYPT is not set # CONFIG_DM_SNAPSHOT is not set @@ -280,6 +308,9 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -299,7 +330,6 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing @@ -362,7 +392,6 @@ CONFIG_E100_NAPI=y # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set -# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -375,6 +404,7 @@ CONFIG_E100_NAPI=y # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set # CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set # CONFIG_TIGON3 is not set # @@ -449,7 +479,8 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DMX3191D is not set @@ -458,6 +489,7 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_SYM53C8XX_2 is not set # CONFIG_SCSI_IPR is not set @@ -519,7 +551,6 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set -# CONFIG_SERIO_I8042 is not set # # Input Device Drivers @@ -554,7 +585,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -567,7 +597,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -575,7 +604,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # # Ftape, the floppy tape device driver # -# CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set @@ -590,6 +618,7 @@ CONFIG_I2C_CHARDEV=y # # CONFIG_I2C_ALGOBIT is not set # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support @@ -612,9 +641,11 @@ CONFIG_I2C_IOP3XX=y # CONFIG_I2C_SIS5595 is not set # CONFIG_I2C_SIS630 is not set # CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # # Hardware Sensors Chip support @@ -622,20 +653,25 @@ CONFIG_I2C_IOP3XX=y # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set # CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set # CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set # CONFIG_SENSORS_LM75 is not set # CONFIG_SENSORS_LM77 is not set # CONFIG_SENSORS_LM78 is not set # CONFIG_SENSORS_LM80 is not set # CONFIG_SENSORS_LM83 is not set # CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set # CONFIG_SENSORS_LM90 is not set # CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set @@ -685,6 +721,7 @@ CONFIG_XFS_POSIX_ACL=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -709,6 +746,7 @@ CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y @@ -754,6 +792,7 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set @@ -799,7 +838,6 @@ CONFIG_MSDOS_PARTITION=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y # @@ -815,6 +853,12 @@ CONFIG_DUMMY_CONSOLE=y # USB support # # CONFIG_USB is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# # # USB Gadget Support @@ -822,16 +866,22 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_GADGET is not set # +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# # Kernel hacking # +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_INFO is not set CONFIG_FRAME_POINTER=y CONFIG_DEBUG_USER=y -# CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_KERNEL is not set # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # diff --git a/arch/arm/configs/integrator_defconfig b/arch/arm/configs/integrator_defconfig index 8e2413266d66..e8c1f0592287 100644 --- a/arch/arm/configs/integrator_defconfig +++ b/arch/arm/configs/integrator_defconfig @@ -124,8 +124,8 @@ CONFIG_NET=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y -# CONFIG_FPE_NWFPE is not set -CONFIG_FPE_FASTFPE=y +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set CONFIG_KCORE_ELF=y # CONFIG_KCORE_AOUT is not set CONFIG_BINFMT_AOUT=y diff --git a/arch/arm/configs/iq31244_defconfig b/arch/arm/configs/iq31244_defconfig index 64ebb89b63a0..89c0eb224198 100644 --- a/arch/arm/configs/iq31244_defconfig +++ b/arch/arm/configs/iq31244_defconfig @@ -1,10 +1,13 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc3 +# Wed Dec 15 16:58:36 2004 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -16,6 +19,7 @@ CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -25,17 +29,20 @@ CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,6 +52,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y # @@ -59,6 +67,7 @@ CONFIG_KMOD=y # CONFIG_ARCH_INTEGRATOR is not set CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set @@ -67,7 +76,9 @@ CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set # # IOP3xx Implementation Options @@ -94,6 +105,7 @@ CONFIG_CPU_32=y CONFIG_CPU_XSCALE=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_TLB_V4WBI=y CONFIG_CPU_MINICACHE=y @@ -110,6 +122,7 @@ CONFIG_PCI=y # CONFIG_ZBOOT_ROM is not set CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_XIP_KERNEL is not set # CONFIG_PCI_LEGACY_PROC is not set CONFIG_PCI_NAMES=y @@ -119,7 +132,6 @@ CONFIG_PCI_NAMES=y CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set -# CONFIG_VFP is not set CONFIG_BINFMT_ELF=y CONFIG_BINFMT_AOUT=y # CONFIG_BINFMT_MISC is not set @@ -148,8 +160,8 @@ CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_REDBOOT_PARTS=y -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -191,7 +203,10 @@ CONFIG_MTD_CFI_UTIL=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0xf0000000 +CONFIG_MTD_PHYSMAP_LEN=0x00800000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_EDB7312 is not set @@ -232,8 +247,19 @@ CONFIG_MTD_CFI_UTIL=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # # Multi-device support (RAID and LVM) @@ -243,9 +269,11 @@ CONFIG_BLK_DEV_MD=y # CONFIG_MD_LINEAR is not set CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=y # CONFIG_MD_RAID6 is not set # CONFIG_MD_MULTIPATH is not set +# CONFIG_MD_FAULTY is not set CONFIG_BLK_DEV_DM=y # CONFIG_DM_CRYPT is not set # CONFIG_DM_SNAPSHOT is not set @@ -280,6 +308,9 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -299,7 +330,6 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing @@ -418,7 +448,8 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DMX3191D is not set @@ -427,6 +458,7 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_SYM53C8XX_2 is not set # CONFIG_SCSI_IPR is not set @@ -488,7 +520,6 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set -# CONFIG_SERIO_I8042 is not set # # Input Device Drivers @@ -523,7 +554,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -536,7 +566,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -544,7 +573,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # # Ftape, the floppy tape device driver # -# CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set @@ -559,6 +587,7 @@ CONFIG_I2C_CHARDEV=y # # CONFIG_I2C_ALGOBIT is not set # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support @@ -581,9 +610,11 @@ CONFIG_I2C_IOP3XX=y # CONFIG_I2C_SIS5595 is not set # CONFIG_I2C_SIS630 is not set # CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # # Hardware Sensors Chip support @@ -591,20 +622,25 @@ CONFIG_I2C_IOP3XX=y # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set # CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set # CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set # CONFIG_SENSORS_LM75 is not set # CONFIG_SENSORS_LM77 is not set # CONFIG_SENSORS_LM78 is not set # CONFIG_SENSORS_LM80 is not set # CONFIG_SENSORS_LM83 is not set # CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set # CONFIG_SENSORS_LM90 is not set # CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set @@ -654,6 +690,7 @@ CONFIG_XFS_POSIX_ACL=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -678,6 +715,7 @@ CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y @@ -723,6 +761,7 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set @@ -768,7 +807,6 @@ CONFIG_MSDOS_PARTITION=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y # @@ -784,6 +822,12 @@ CONFIG_DUMMY_CONSOLE=y # USB support # # CONFIG_USB is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# # # USB Gadget Support @@ -791,16 +835,22 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_GADGET is not set # +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# # Kernel hacking # +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_INFO is not set CONFIG_FRAME_POINTER=y CONFIG_DEBUG_USER=y -# CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_KERNEL is not set # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # diff --git a/arch/arm/configs/iq80321_defconfig b/arch/arm/configs/iq80321_defconfig index f19320b3b787..f3e08188f418 100644 --- a/arch/arm/configs/iq80321_defconfig +++ b/arch/arm/configs/iq80321_defconfig @@ -1,10 +1,13 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc3 +# Wed Dec 15 16:48:43 2004 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -16,6 +19,7 @@ CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -25,17 +29,20 @@ CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,6 +52,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y # @@ -59,6 +67,7 @@ CONFIG_KMOD=y # CONFIG_ARCH_INTEGRATOR is not set CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set @@ -67,7 +76,9 @@ CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set # # IOP3xx Implementation Options @@ -94,6 +105,7 @@ CONFIG_CPU_32=y CONFIG_CPU_XSCALE=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_TLB_V4WBI=y CONFIG_CPU_MINICACHE=y @@ -110,6 +122,7 @@ CONFIG_PCI=y # CONFIG_ZBOOT_ROM is not set CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_XIP_KERNEL is not set # CONFIG_PCI_LEGACY_PROC is not set CONFIG_PCI_NAMES=y @@ -119,7 +132,6 @@ CONFIG_PCI_NAMES=y CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set -# CONFIG_VFP is not set CONFIG_BINFMT_ELF=y CONFIG_BINFMT_AOUT=y # CONFIG_BINFMT_MISC is not set @@ -148,8 +160,8 @@ CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_REDBOOT_PARTS=y -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -191,7 +203,10 @@ CONFIG_MTD_CFI_UTIL=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0xf0000000 +CONFIG_MTD_PHYSMAP_LEN=0x00800000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=1 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_EDB7312 is not set @@ -232,8 +247,19 @@ CONFIG_MTD_CFI_UTIL=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # # Multi-device support (RAID and LVM) @@ -268,6 +294,9 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -287,7 +316,6 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing @@ -413,7 +441,6 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set -# CONFIG_SERIO_I8042 is not set # # Input Device Drivers @@ -448,7 +475,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -461,7 +487,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -469,7 +494,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # # Ftape, the floppy tape device driver # -# CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set @@ -510,6 +534,7 @@ CONFIG_XFS_POSIX_ACL=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -534,6 +559,7 @@ CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y @@ -579,6 +605,7 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set @@ -624,7 +651,6 @@ CONFIG_MSDOS_PARTITION=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y # @@ -640,6 +666,12 @@ CONFIG_DUMMY_CONSOLE=y # USB support # # CONFIG_USB is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# # # USB Gadget Support @@ -647,16 +679,22 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_GADGET is not set # +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# # Kernel hacking # +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_INFO is not set CONFIG_FRAME_POINTER=y # CONFIG_DEBUG_USER is not set -# CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_KERNEL is not set # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # diff --git a/arch/arm/configs/iq80331_defconfig b/arch/arm/configs/iq80331_defconfig index 9adbf9b09c01..b744add800a2 100644 --- a/arch/arm/configs/iq80331_defconfig +++ b/arch/arm/configs/iq80331_defconfig @@ -1,10 +1,13 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc3 +# Wed Dec 15 16:43:39 2004 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -16,6 +19,7 @@ CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -25,6 +29,7 @@ CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y @@ -32,11 +37,13 @@ CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -46,6 +53,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y # @@ -60,6 +68,7 @@ CONFIG_KMOD=y # CONFIG_ARCH_INTEGRATOR is not set CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set @@ -68,7 +77,9 @@ CONFIG_ARCH_IOP3XX=y # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set # # IOP3xx Implementation Options @@ -94,6 +105,7 @@ CONFIG_CPU_32=y CONFIG_CPU_XSCALE=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y CONFIG_CPU_TLB_V4WBI=y CONFIG_CPU_MINICACHE=y @@ -110,6 +122,7 @@ CONFIG_PCI=y # CONFIG_ZBOOT_ROM is not set CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_XIP_KERNEL is not set # CONFIG_PCI_LEGACY_PROC is not set CONFIG_PCI_NAMES=y @@ -119,7 +132,6 @@ CONFIG_PCI_NAMES=y CONFIG_FPE_NWFPE=y # CONFIG_FPE_NWFPE_XP is not set # CONFIG_FPE_FASTFPE is not set -# CONFIG_VFP is not set CONFIG_BINFMT_ELF=y CONFIG_BINFMT_AOUT=y # CONFIG_BINFMT_MISC is not set @@ -149,8 +161,8 @@ CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_REDBOOT_PARTS=y -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -196,7 +208,10 @@ CONFIG_MTD_CFI_UTIL=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0xc0000000 +CONFIG_MTD_PHYSMAP_LEN=0x00800000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=1 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_EDB7312 is not set @@ -237,8 +252,19 @@ CONFIG_MTD_CFI_UTIL=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # # Multi-device support (RAID and LVM) @@ -248,9 +274,11 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=y # CONFIG_MD_RAID6 is not set # CONFIG_MD_MULTIPATH is not set +# CONFIG_MD_FAULTY is not set CONFIG_BLK_DEV_DM=y # CONFIG_DM_CRYPT is not set # CONFIG_DM_SNAPSHOT is not set @@ -285,6 +313,9 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -304,7 +335,6 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing @@ -423,7 +453,8 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DMX3191D is not set @@ -432,6 +463,7 @@ CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_SYM53C8XX_2 is not set # CONFIG_SCSI_IPR is not set @@ -493,7 +525,6 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set -# CONFIG_SERIO_I8042 is not set # # Input Device Drivers @@ -528,7 +559,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -541,7 +571,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -549,7 +578,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # # Ftape, the floppy tape device driver # -# CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set @@ -590,6 +618,7 @@ CONFIG_XFS_POSIX_ACL=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -614,6 +643,7 @@ CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y @@ -653,6 +683,7 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set @@ -698,7 +729,6 @@ CONFIG_MSDOS_PARTITION=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y # @@ -714,6 +744,12 @@ CONFIG_DUMMY_CONSOLE=y # USB support # # CONFIG_USB is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# # # USB Gadget Support @@ -721,23 +757,31 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_GADGET is not set # +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# # Kernel hacking # -CONFIG_FRAME_POINTER=y -CONFIG_DEBUG_USER=y -# CONFIG_DEBUG_INFO is not set CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SLAB is not set # CONFIG_MAGIC_SYSRQ is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_KOBJECT is not set CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set CONFIG_DEBUG_ERRORS=y # CONFIG_DEBUG_LL is not set # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # diff --git a/arch/arm/configs/versatile_defconfig b/arch/arm/configs/versatile_defconfig index b5e260236936..e98f061a89ec 100644 --- a/arch/arm/configs/versatile_defconfig +++ b/arch/arm/configs/versatile_defconfig @@ -65,7 +65,7 @@ CONFIG_KMOD=y # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_S3C2410 is not set # CONFIG_ARCH_OMAP is not set -CONFIG_ARCH_VERSATILE_PB=y +CONFIG_ARCH_VERSATILE=y # # CLPS711X/EP721X Implementations @@ -110,6 +110,12 @@ CONFIG_ARCH_VERSATILE_PB=y # # +# Versatile platform type +# +CONFIG_ARCH_VERSATILE_PB=y +# CONFIG_MACH_VERSATILE_AB is not set + +# # Processor Type # CONFIG_CPU_32=y @@ -144,8 +150,8 @@ CONFIG_ZBOOT_ROM_BSS=0x0 # # At least one math emulation must be selected # -# CONFIG_FPE_NWFPE is not set -CONFIG_FPE_FASTFPE=y +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set # CONFIG_BINFMT_MISC is not set diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 21d0a197a00a..a78ccd76e3ca 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -132,21 +132,21 @@ static struct resource io_res[] = { #define lp2 io_res[2] static const char *cache_types[16] = { - "VIVT write-through", - "VIVT write-back", - "VIVT write-back", + "write-through", + "write-back", + "write-back", "undefined 3", "undefined 4", "undefined 5", - "VIVT write-back", - "VIVT write-back", + "write-back", + "write-back", "undefined 8", "undefined 9", "undefined 10", "undefined 11", "undefined 12", "undefined 13", - "VIPT write-back", + "write-back", "undefined 15", }; @@ -236,7 +236,8 @@ static void __init dump_cpu_info(void) unsigned int info = read_cpuid(CPUID_CACHETYPE); if (info != processor_id) { - printk("CPU: D %s cache\n", cache_types[CACHE_TYPE(info)]); + printk("CPU: D %s %s cache\n", cache_is_vivt() ? "VIVT" : "VIPT", + cache_types[CACHE_TYPE(info)]); if (CACHE_S(info)) { dump_cache("CPU: I cache", CACHE_ISIZE(info)); dump_cache("CPU: D cache", CACHE_DSIZE(info)); @@ -255,7 +256,7 @@ int cpu_architecture(void) } else if ((processor_id & 0x0000f000) == 0x00007000) { cpu_arch = (processor_id & (1 << 23)) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3; } else { - cpu_arch = (processor_id >> 16) & 15; + cpu_arch = (processor_id >> 16) & 7; if (cpu_arch) cpu_arch += CPU_ARCH_ARMv3; } diff --git a/arch/arm/mach-iop3xx/iop331-mm.c b/arch/arm/mach-iop3xx/iop331-mm.c deleted file mode 100644 index 8a43d4dc3d7b..000000000000 --- a/arch/arm/mach-iop3xx/iop331-mm.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * linux/arch/arm/mach-iop3xx/mm.c - * - * Low level memory initialization for IOP331 based systems - * - * Author: Dave Jiang <dave.jiang@intel.com> - * Copyright (C) 2003 Intel Corp. - * - * 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. - * - */ - -#include <linux/mm.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/page.h> - -#include <asm/mach/map.h> -#include <asm/mach-types.h> - - -/* - * Standard IO mapping for all IOP331 based systems - */ -static struct map_desc iop331_std_desc[] __initdata = { - /* virtual physical length type */ - - /* mem mapped registers */ - { IOP331_VIRT_MEM_BASE, IOP331_PHYS_MEM_BASE, 0x00002000, MT_DEVICE }, - - /* PCI IO space */ - { 0xfe000000, 0x90000000, 0x00020000, MT_DEVICE } -}; - -void __init iop331_map_io(void) -{ - iotable_init(iop331_std_desc, ARRAY_SIZE(iop331_std_desc)); -} diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c index 2c7067f27e38..69486bde92de 100644 --- a/arch/arm/mach-ixp4xx/common-pci.c +++ b/arch/arm/mach-ixp4xx/common-pci.c @@ -238,7 +238,7 @@ static u32 byte_lane_enable_bits(u32 n, int size) return 0xffffffff; } -static int ixp4xx_pci_read_config(struct pci_bus *bus, u16 devfn, int where, int size, u32 *value) +static int ixp4xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value) { u32 n, byte_enables, addr, data; u8 bus_num = bus->number; @@ -261,7 +261,7 @@ static int ixp4xx_pci_read_config(struct pci_bus *bus, u16 devfn, int where, int return PCIBIOS_SUCCESSFUL; } -static int ixp4xx_pci_write_config(struct pci_bus *bus, u16 devfn, int where, int size, u32 value) +static int ixp4xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value) { u32 n, byte_enables, addr, data; u8 bus_num = bus->number; diff --git a/arch/arm/mach-lh7a40x/arch-lpd7a40x.c b/arch/arm/mach-lh7a40x/arch-lpd7a40x.c index ba15ec398c37..06373b295f83 100644 --- a/arch/arm/mach-lh7a40x/arch-lpd7a40x.c +++ b/arch/arm/mach-lh7a40x/arch-lpd7a40x.c @@ -267,7 +267,7 @@ MACHINE_START (LPD7A400, "Logic Product Development LPD7A400-10") BOOT_PARAMS (0xc0000100) MAPIO (lpd7a400_map_io) INITIRQ (lh7a400_init_irq) - .timer = &lpd7a40x_timer, + .timer = &lh7a40x_timer, INIT_MACHINE (lpd7a40x_init) MACHINE_END @@ -281,7 +281,7 @@ MACHINE_START (LPD7A404, "Logic Product Development LPD7A404-10") BOOT_PARAMS (0xc0000100) MAPIO (lpd7a400_map_io) INITIRQ (lh7a404_init_irq) - .timer = &lpd7a40x_timer, + .timer = &lh7a40x_timer, INIT_MACHINE (lpd7a40x_init) MACHINE_END diff --git a/arch/arm/mach-lh7a40x/common.h b/arch/arm/mach-lh7a40x/common.h index 46df8131c0a3..05564eca3546 100644 --- a/arch/arm/mach-lh7a40x/common.h +++ b/arch/arm/mach-lh7a40x/common.h @@ -1,9 +1,14 @@ -/* - * linux/arch/arm/mach-lh7a40x/common.h +/* arch/arm/mach-lh7a40x/common.h + * + * Copyright (C) 2004 Marc Singer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. * - * Header file for common stuff. */ -struct sys_timer; + extern struct sys_timer lh7a40x_timer; extern void lh7a400_init_irq (void); +extern void lh7a404_init_irq (void); diff --git a/arch/arm/mach-lh7a40x/time.c b/arch/arm/mach-lh7a40x/time.c index 4dc8f8cad821..51e1c814b400 100644 --- a/arch/arm/mach-lh7a40x/time.c +++ b/arch/arm/mach-lh7a40x/time.c @@ -71,5 +71,5 @@ static void __init lh7a40x_timer_init(void) } struct sys_timer lh7a40x_timer = { - .init = &lh7a40x_timer, + .init = &lh7a40x_timer_init, }; diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c index 5ae6d7ff18c9..ea7c25a50c73 100644 --- a/arch/arm/mach-s3c2410/pm.c +++ b/arch/arm/mach-s3c2410/pm.c @@ -187,6 +187,8 @@ static void s3c2410_pm_debug_init(void) #define DBG(fmt...) printk(KERN_DEBUG fmt) #define s3c2410_pm_debug_init() do { } while(0) + +static struct sleep_save uart_save[] = {}; #endif #if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0 @@ -378,8 +380,6 @@ static void s3c2410_pm_check_restore(void) #else -static struct sleep_save uart_save[] = {}; - #define s3c2410_pm_check_prepare() do { } while(0) #define s3c2410_pm_check_restore() do { } while(0) #define s3c2410_pm_check_store() do { } while(0) diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index df955410e5cc..a3003b867555 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -31,6 +31,7 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include <asm/mach/irda.h> #include <asm/mach/map.h> #include <asm/mach/serial_sa1100.h> #include <asm/arch/assabet.h> @@ -94,6 +95,11 @@ static void assabet_lcd_power(int on) ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); } + +/* + * Assabet flash support code. + */ + #ifdef ASSABET_REV_4 /* * Phase 4 Assabet has two 28F160B3 flash parts in bank 0: @@ -156,6 +162,42 @@ static struct resource assabet_flash_resources[] = { } }; + +/* + * Assabet IrDA support code. + */ + +static int assabet_irda_set_power(struct device *dev, unsigned int state) +{ + static unsigned int bcr_state[4] = { + ASSABET_BCR_IRDA_MD0, + ASSABET_BCR_IRDA_MD1|ASSABET_BCR_IRDA_MD0, + ASSABET_BCR_IRDA_MD1, + 0 + }; + + if (state < 4) { + state = bcr_state[state]; + ASSABET_BCR_clear(state ^ (ASSABET_BCR_IRDA_MD1| + ASSABET_BCR_IRDA_MD0)); + ASSABET_BCR_set(state); + } + return 0; +} + +static void assabet_irda_set_speed(struct device *dev, unsigned int speed) +{ + if (speed < 4000000) + ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL); + else + ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL); +} + +static struct irda_platform_data assabet_irda_data = { + .set_power = assabet_irda_set_power, + .set_speed = assabet_irda_set_speed, +}; + static void __init assabet_init(void) { /* @@ -203,6 +245,7 @@ static void __init assabet_init(void) sa11x0_set_flash_data(&assabet_flash_data, assabet_flash_resources, ARRAY_SIZE(assabet_flash_resources)); + sa11x0_set_irda_data(&assabet_irda_data); } /* diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 2d1aa9391d00..95ae217be1bc 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -283,6 +283,38 @@ void sa11x0_set_flash_data(struct flash_platform_data *flash, sa11x0mtd_device.num_resources = nr; } +static struct resource sa11x0ir_resources[] = { + { + .start = __PREG(Ser2UTCR0), + .end = __PREG(Ser2UTCR0) + 0x24 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = __PREG(Ser2HSCR0), + .end = __PREG(Ser2HSCR0) + 0x1c - 1, + .flags = IORESOURCE_MEM, + }, { + .start = __PREG(Ser2HSCR2), + .end = __PREG(Ser2HSCR2) + 0x04 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_Ser2ICP, + .end = IRQ_Ser2ICP, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sa11x0ir_device = { + .name = "sa11x0-ir", + .id = -1, + .num_resources = ARRAY_SIZE(sa11x0ir_resources), + .resource = sa11x0ir_resources, +}; + +void sa11x0_set_irda_data(struct irda_platform_data *irda) +{ + sa11x0ir_device.dev.platform_data = irda; +} + static struct platform_device *sa11x0_devices[] __initdata = { &sa11x0udc_device, &sa11x0uart1_device, @@ -298,6 +330,9 @@ static int __init sa1100_init(void) { pm_power_off = sa1100_power_off; + if (sa11x0ir_device.dev.platform_data) + platform_device_register(&sa11x0ir_device); + return platform_add_devices(sa11x0_devices, ARRAY_SIZE(sa11x0_devices)); } diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h index a72ad2de9aeb..bfe41da9923e 100644 --- a/arch/arm/mach-sa1100/generic.h +++ b/arch/arm/mach-sa1100/generic.h @@ -33,3 +33,6 @@ struct resource; extern void sa11x0_set_flash_data(struct flash_platform_data *flash, struct resource *res, int nr); + +struct irda_platform_data; +void sa11x0_set_irda_data(struct irda_platform_data *irda); diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c index 5a0933c6d2ac..9788d3aefa73 100644 --- a/arch/arm/mach-sa1100/h3600.c +++ b/arch/arm/mach-sa1100/h3600.c @@ -38,6 +38,7 @@ #include <asm/mach/irq.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include <asm/mach/irda.h> #include <asm/mach/map.h> #include <asm/mach/serial_sa1100.h> @@ -119,9 +120,34 @@ static struct resource h3xxx_flash_resource = { .flags = IORESOURCE_MEM, }; +/* + * This turns the IRDA power on or off on the Compaq H3600 + */ +static int h3600_irda_set_power(struct device *dev, unsigned int state) +{ + assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); + + return 0; +} + +static void h3600_irda_set_speed(struct device *dev, int speed) +{ + if (speed < 4000000) { + clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + } else { + set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + } +} + +static struct irda_platform_data h3600_irda_data = { + .set_power = h3600_irda_set_power, + .set_speed = h3600_irda_set_speed, +}; + static void h3xxx_mach_init(void) { sa11x0_set_flash_data(&h3xxx_flash_data, &h3xxx_flash_resource, 1); + sa11x0_set_irda_data(&h3600_irda_data); } /* diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c index 34cc31b38ca1..51c08ccfb8db 100644 --- a/arch/arm/mach-sa1100/lart.c +++ b/arch/arm/mach-sa1100/lart.c @@ -17,6 +17,8 @@ #include "generic.h" +#warning "include/asm/arch-sa1100/ide.h needs fixing for lart" + static struct map_desc lart_io_desc[] __initdata = { /* virtual physical length type */ { 0xe8000000, 0x00000000, 0x00400000, MT_DEVICE }, /* main flash memory */ diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig index 3969d775b5eb..8d787f4c78e6 100644 --- a/arch/arm/mach-versatile/Kconfig +++ b/arch/arm/mach-versatile/Kconfig @@ -7,7 +7,7 @@ config ARCH_VERSATILE_PB help Include support for the ARM(R) Versatile/PB platform. -config ARCH_VERSATILE_AB +config MACH_VERSATILE_AB bool "Support Versatile/AB platform" default n help diff --git a/arch/arm/mach-versatile/Makefile b/arch/arm/mach-versatile/Makefile index 931aa0d29bbd..5d608837757a 100644 --- a/arch/arm/mach-versatile/Makefile +++ b/arch/arm/mach-versatile/Makefile @@ -4,4 +4,4 @@ obj-y := core.o clock.o obj-$(CONFIG_ARCH_VERSATILE_PB) += versatile_pb.o -obj-$(CONFIG_ARCH_VERSATILE_AB) += versatile_ab.o +obj-$(CONFIG_MACH_VERSATILE_AB) += versatile_ab.o diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 52d824f507a2..2e71ad68362f 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -189,7 +189,7 @@ static struct map_desc versatile_io_desc[] __initdata = { { IO_ADDRESS(VERSATILE_SIC_BASE), VERSATILE_SIC_BASE, SZ_4K, MT_DEVICE }, { IO_ADDRESS(VERSATILE_VIC_BASE), VERSATILE_VIC_BASE, SZ_4K, MT_DEVICE }, { IO_ADDRESS(VERSATILE_SCTL_BASE), VERSATILE_SCTL_BASE, SZ_4K * 9, MT_DEVICE }, -#ifdef CONFIG_ARCH_VERSATILE_AB +#ifdef CONFIG_MACH_VERSATILE_AB { IO_ADDRESS(VERSATILE_GPIO0_BASE), VERSATILE_GPIO0_BASE, SZ_4K, MT_DEVICE }, { IO_ADDRESS(VERSATILE_IB2_BASE), VERSATILE_IB2_BASE, SZ_64M, MT_DEVICE }, #endif @@ -341,7 +341,7 @@ static void versatile_oscvco_set(struct clk *clk, struct icst307_vco vco) unsigned long sys_lock = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_LOCK_OFFSET; #if defined(CONFIG_ARCH_VERSATILE_PB) unsigned long sys_osc = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_OSC4_OFFSET; -#elif defined(CONFIG_ARCH_VERSATILE_AB) +#elif defined(CONFIG_MACH_VERSATILE_AB) unsigned long sys_osc = IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_OSC1_OFFSET; #endif u32 val; @@ -512,7 +512,7 @@ static void versatile_clcd_disable(struct clcd_fb *fb) val &= ~SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH; writel(val, sys_clcd); -#ifdef CONFIG_ARCH_VERSATILE_AB +#ifdef CONFIG_MACH_VERSATILE_AB /* * If the LCD is Sanyo 2x5 in on the IB2 board, turn the back-light off */ @@ -561,7 +561,7 @@ static void versatile_clcd_enable(struct clcd_fb *fb) val |= SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH; writel(val, sys_clcd); -#ifdef CONFIG_ARCH_VERSATILE_AB +#ifdef CONFIG_MACH_VERSATILE_AB /* * If the LCD is Sanyo 2x5 in on the IB2 board, turn the back-light on */ diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 77254fe3e4fb..787daaa3a90b 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -121,8 +121,8 @@ config CPU_ARM925T # ARM926T config CPU_ARM926T bool "Support ARM926T processor" if ARCH_INTEGRATOR - depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP1610 || ARCH_OMAP5912 - default y if ARCH_VERSATILE_PB || ARCH_VERSATILE_AB + depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP1610 || ARCH_OMAP5912 + default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB select CPU_32v5 select CPU_ABRT_EV5TJ select CPU_CACHE_VIVT diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 0a4ff26247b0..166a94e861c9 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -250,8 +250,8 @@ cpu_elf_name: */ .type __v6_proc_info, #object __v6_proc_info: - .long 0x00070000 - .long 0x00ff0000 + .long 0x0007b000 + .long 0x0007f000 .long 0x00000c0e b __v6_setup .long cpu_arch_name diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 2be02f7f5166..495167ca2329 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -679,6 +679,11 @@ cpu_ixp42x_name: .asciz "XScale-IXP42x Family" .size cpu_ixp42x_name, . - cpu_ixp42x_name + .type cpu_ixp46x_name, #object +cpu_ixp46x_name: + .asciz "XScale-IXP46x Family" + .size cpu_ixp46x_name, . - cpu_ixp46x_name + .type cpu_ixp2400_name, #object cpu_ixp2400_name: .asciz "XScale-IXP2400" @@ -831,6 +836,22 @@ __ixp42x_proc_info: .long xscale_cache_fns .size __ixp42x_proc_info, . - __ixp42x_proc_info + .type __ixp46x_proc_info, #object +__ixp46x_proc_info: + .long 0x69054200 + .long 0xffffff00 + .long 0x00000c0e + b __xscale_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP + .long cpu_ixp46x_name + .long xscale_processor_functions + .long v4wbi_tlb_fns + .long xscale_mc_user_fns + .long xscale_cache_fns + .size __ixp46x_proc_info, . - __ixp46x_proc_info + .type __pxa255_proc_info,#object __pxa255_proc_info: .long 0x69052d00 diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 344de2e5ba99..402a71fd162b 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -200,7 +200,7 @@ config M586 bool "586/K5/5x86/6x86/6x86MX" help Select this for an 586 or 686 series processor such as the AMD K5, - the Intel 5x86 or 6x86, or the Intel 6x86MX. This choice does not + the Cyrix 5x86, 6x86 and 6x86MX. This choice does not assume the RDTSC (Read Time Stamp Counter) instruction. config M586TSC diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 808286311b44..2935c9403982 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -235,6 +235,7 @@ sysenter_past_esp: /* if something modifies registers it must also disable sysexit */ movl EIP(%esp), %edx movl OLDESP(%esp), %ecx + xorl %ebp,%ebp sti sysexit diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index d3e8774b79c4..7efce8e4c376 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -99,6 +99,8 @@ void default_idle(void) safe_halt(); else local_irq_enable(); + } else { + cpu_relax(); } } diff --git a/arch/i386/kernel/quirks.c b/arch/i386/kernel/quirks.c index 65d5ee76731c..847ba44c2e06 100644 --- a/arch/i386/kernel/quirks.c +++ b/arch/i386/kernel/quirks.c @@ -1,10 +1,11 @@ /* * This file contains work-arounds for x86 and x86_64 platform bugs. */ +#include <linux/config.h> #include <linux/pci.h> #include <linux/irq.h> -#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) +#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) && defined(CONFIG_PCI) void __devinit quirk_intel_irqbalance(struct pci_dev *dev) { diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index 2c5312b24b1f..a4ec0f26b946 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -381,9 +381,9 @@ extern void (*late_time_init)(void); void __init hpet_time_init(void) { xtime.tv_sec = get_cmos_time(); - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); if (hpet_enable() >= 0) { printk("Using HPET for base-timer\n"); @@ -409,9 +409,9 @@ void __init time_init(void) } #endif xtime.tv_sec = get_cmos_time(); - wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); cur_timer = select_timer(); printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name); diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index 7344cddd4a29..80b302689697 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -723,7 +723,14 @@ static irqreturn_t irq_handler(int intno, void *dev_id, struct pt_regs * regs) irqbits |= irq_bit; if (vm86_irqs[intno].sig) send_sig(vm86_irqs[intno].sig, vm86_irqs[intno].tsk, 1); - /* else user will poll for IRQs */ + spin_unlock_irqrestore(&irqbits_lock, flags); + /* + * IRQ will be re-enabled when user asks for the irq (whether + * polling or as a result of the signal) + */ + disable_irq(intno); + return IRQ_HANDLED; + out: spin_unlock_irqrestore(&irqbits_lock, flags); return IRQ_NONE; diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index 21162f032ec3..2932ac71bd0b 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -82,7 +82,9 @@ unwcheck: vmlinux archclean: $(Q)$(MAKE) $(clean)=$(boot) -CLEAN_FILES += include/asm-ia64/.offsets.h.stamp include/asm-ia64/offsets.h vmlinux.gz bootloader +CLEAN_FILES += include/asm-ia64/.offsets.h.stamp vmlinux.gz bootloader + +MRPROPER_FILES += include/asm-ia64/offsets.h prepare: include/asm-ia64/offsets.h diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index e231d5ce16e4..a141ecbd40ed 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -1998,7 +1998,7 @@ pfm_close(struct inode *inode, struct file *filp) /* * XXX: check for signals : - * - ok of explicit close + * - ok for explicit close * - not ok when coming from exit_files() */ schedule(); @@ -4978,26 +4978,14 @@ pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_reg static void pfm_context_force_terminate(pfm_context_t *ctx, struct pt_regs *regs) { - if (ctx->ctx_fl_system) { - printk(KERN_ERR "perfmon: pfm_context_force_terminate [%d] is system-wide\n", current->pid); - return; - } - /* - * we stop the whole thing, we do no need to flush - * we know we WERE masked - */ - pfm_clear_psr_up(); - ia64_psr(regs)->up = 0; - ia64_psr(regs)->sp = 1; + int ret; - /* - * disconnect the task from the context and vice-versa - */ - current->thread.pfm_context = NULL; - current->thread.flags &= ~IA64_THREAD_PM_VALID; - ctx->ctx_task = NULL; + DPRINT(("entering for [%d]\n", current->pid)); - DPRINT(("context terminated\n")); + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "pfm_context_force_terminate: [%d] unloaded failed with %d\n", current->pid, ret); + } /* * and wakeup controlling task, indicating we are now disconnected @@ -5370,9 +5358,8 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str if (ovfl_notify == 0) reset_pmds = ovfl_pmds; } - DPRINT(("ovfl_pmds=0x%lx reset_pmds=0x%lx\n", - ovfl_pmds, - reset_pmds)); + DPRINT_ovfl(("ovfl_pmds=0x%lx reset_pmds=0x%lx\n", ovfl_pmds, reset_pmds)); + /* * reset the requested PMD registers using the short reset values */ @@ -6367,6 +6354,9 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx) * XXX: sampling situation is not taken into account here */ mask2 = ctx->ctx_used_pmds[0]; + + DPRINT(("is_self=%d ovfl_val=0x%lx mask2=0x%lx\n", is_self, ovfl_val, mask2)); + for (i = 0; mask2; i++, mask2>>=1) { /* skip non used pmds */ @@ -6405,7 +6395,7 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx) } } - DPRINT(("[%d] is_self=%d ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, is_self, i, val, pmd_val)); + DPRINT(("[%d] ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, i, val, pmd_val)); if (is_self) task->thread.pmds[i] = pmd_val; diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index d54628de4970..7b2033bf0954 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -289,6 +289,15 @@ early_console_setup (char *cmdline) return -1; } +static inline void +mark_bsp_online (void) +{ +#ifdef CONFIG_SMP + /* If we register an early console, allow CPU 0 to printk */ + cpu_set(smp_processor_id(), cpu_online_map); +#endif +} + void __init setup_arch (char **cmdline_p) { @@ -306,11 +315,8 @@ setup_arch (char **cmdline_p) machvec_init(acpi_get_sysname()); #endif -#ifdef CONFIG_SMP - /* If we register an early console, allow CPU 0 to printk */ - if (!early_console_setup(*cmdline_p)) - cpu_set(smp_processor_id(), cpu_online_map); -#endif + if (early_console_setup(*cmdline_p) == 0) + mark_bsp_online(); #ifdef CONFIG_ACPI_BOOT /* Initialize the ACPI boot-time table parser */ diff --git a/arch/m32r/kernel/time.c b/arch/m32r/kernel/time.c index 34a05cf85dcc..3c4707280a52 100644 --- a/arch/m32r/kernel/time.c +++ b/arch/m32r/kernel/time.c @@ -275,8 +275,8 @@ void __init time_init(void) xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - wall_to_monotonic.tv_sec = -xtime.tv_sec; - wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); #if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \ || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \ diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig index 5596b0feb708..a912df14485d 100644 --- a/arch/m68k/configs/amiga_defconfig +++ b/arch/m68k/configs/amiga_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:46:49 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:25 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -137,6 +137,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -722,6 +723,10 @@ CONFIG_DMASOUND=m # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -953,7 +958,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig index 459f20a1a30e..45ed61426c36 100644 --- a/arch/m68k/configs/apollo_defconfig +++ b/arch/m68k/configs/apollo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:47:18 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:29 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -122,6 +122,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -581,6 +582,10 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig index 318435c84ad6..2e3a1624ba3b 100644 --- a/arch/m68k/configs/atari_defconfig +++ b/arch/m68k/configs/atari_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:48:18 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:34 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -129,6 +129,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -634,6 +635,10 @@ CONFIG_DMASOUND=m # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -865,7 +870,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig index 5d751396528a..5416c544cad3 100644 --- a/arch/m68k/configs/bvme6000_defconfig +++ b/arch/m68k/configs/bvme6000_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:48:27 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:38 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -122,6 +122,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -580,6 +581,10 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig index 1263af387b52..3ff6b3b8c7df 100644 --- a/arch/m68k/configs/hp300_defconfig +++ b/arch/m68k/configs/hp300_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:48:53 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:44 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -123,6 +123,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -582,6 +583,10 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig index d381ddb935f7..7d7fd0582263 100644 --- a/arch/m68k/configs/mac_defconfig +++ b/arch/m68k/configs/mac_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:04 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:47 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -124,6 +124,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -643,6 +644,10 @@ CONFIG_LOGO_MAC_CLUT224=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig index 5686194c445c..84610d3411cc 100644 --- a/arch/m68k/configs/mvme147_defconfig +++ b/arch/m68k/configs/mvme147_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:10 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:49 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -122,6 +122,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -597,6 +598,10 @@ CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig index dbdeaea6da45..688c9e78f16c 100644 --- a/arch/m68k/configs/mvme16x_defconfig +++ b/arch/m68k/configs/mvme16x_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:35 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:52 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -122,6 +122,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -596,6 +597,10 @@ CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -827,7 +832,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig index ec289bc3542f..6ede4e73a82f 100644 --- a/arch/m68k/configs/q40_defconfig +++ b/arch/m68k/configs/q40_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:42 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:55 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -127,6 +127,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -673,6 +674,10 @@ CONFIG_DMASOUND=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -900,7 +905,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig index d132fe772229..e4e473e9b75f 100644 --- a/arch/m68k/configs/sun3_defconfig +++ b/arch/m68k/configs/sun3_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:50 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:58 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -110,6 +110,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -586,6 +587,10 @@ CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -815,7 +820,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig index 719b59f0265e..f9acd19a08ff 100644 --- a/arch/m68k/configs/sun3x_defconfig +++ b/arch/m68k/configs/sun3x_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:49:55 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:22:01 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -121,6 +121,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -596,6 +597,10 @@ CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -825,7 +830,7 @@ CONFIG_CRYPTO_TEST=m # Library routines # CONFIG_CRC_CCITT=m -CONFIG_CRC32=m +CONFIG_CRC32=y CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index d19fe830d2ff..89628d8d9c93 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2-m68k -# Mon Nov 15 12:48:44 2004 +# Linux kernel version: 2.6.10-rc3-m68k +# Sun Dec 5 14:21:41 2004 # CONFIG_M68K=y CONFIG_MMU=y @@ -115,6 +115,7 @@ CONFIG_AMIGA_FLOPPY=y # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" @@ -452,6 +453,10 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_USB_ARCH_HAS_OHCI is not set # +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set diff --git a/arch/ppc/boot/common/util.S b/arch/ppc/boot/common/util.S index 7c4fb8f57953..d0161ac4cc56 100644 --- a/arch/ppc/boot/common/util.S +++ b/arch/ppc/boot/common/util.S @@ -27,6 +27,7 @@ .text +#ifdef CONFIG_6xx .globl disable_6xx_mmu disable_6xx_mmu: /* Establish default MSR value, exception prefix 0xFFF. @@ -94,6 +95,7 @@ disable_6xx_l1cache: sync isync blr +#endif .globl _setup_L2CR _setup_L2CR: diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 1534f0229b9c..4db17100eb67 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -321,7 +321,7 @@ void show_regs(struct pt_regs * regs) trap = TRAP(regs); if (trap == 0x300 || trap == 0x600) printk("DAR: %08lX, DSISR: %08lX\n", regs->dar, regs->dsisr); - printk("TASK = %p[%d] '%s' THREAD: %p", + printk("TASK = %p[%d] '%s' THREAD: %p\n", current, current->pid, current->comm, current->thread_info); printk("Last syscall: %ld ", current->thread.last_syscall); @@ -370,6 +370,10 @@ void exit_thread(void) last_task_used_math = NULL; if (last_task_used_altivec == current) last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif } void flush_thread(void) @@ -378,6 +382,10 @@ void flush_thread(void) last_task_used_math = NULL; if (last_task_used_altivec == current) last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif } void @@ -480,6 +488,10 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) last_task_used_math = NULL; if (last_task_used_altivec == current) last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); current->thread.fpscr = 0; #ifdef CONFIG_ALTIVEC diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 0940f2c9d6bc..b2bc832d785d 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -319,7 +319,7 @@ restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr, int sig) if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_SPE) != 0) { /* restore spe registers from the stack */ if (__copy_from_user(current->thread.evr, &sr->mc_vregs, - sizeof(sr->mc_vregs))) + ELF_NEVRREG * sizeof(u32))) return 1; } else if (current->thread.used_spe) memset(¤t->thread.evr, 0, ELF_NEVRREG * sizeof(u32)); diff --git a/arch/ppc/platforms/prep_pci.c b/arch/ppc/platforms/prep_pci.c index d7abcbe8cc03..5dce24212aea 100644 --- a/arch/ppc/platforms/prep_pci.c +++ b/arch/ppc/platforms/prep_pci.c @@ -49,10 +49,10 @@ static char Utah_pci_IRQ_map[23] __prepdata = 0, /* Slot 1 - unused */ 5, /* Slot 2 - SCSI - NCR825A */ 0, /* Slot 3 - unused */ - 1, /* Slot 4 - Ethernet - DEC2114x */ + 3, /* Slot 4 - Ethernet - DEC2114x */ 0, /* Slot 5 - unused */ - 3, /* Slot 6 - PCI Card slot #1 */ - 4, /* Slot 7 - PCI Card slot #2 */ + 2, /* Slot 6 - PCI Card slot #1 */ + 3, /* Slot 7 - PCI Card slot #2 */ 5, /* Slot 8 - PCI Card slot #3 */ 5, /* Slot 9 - PCI Bridge */ /* added here in case we ever support PCI bridges */ diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c index b2f3d850503c..89f6825240ce 100644 --- a/arch/ppc/syslib/ppc4xx_dma.c +++ b/arch/ppc/syslib/ppc4xx_dma.c @@ -466,7 +466,7 @@ ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init) /* clear all polarity signals and then "or" in new signal levels */ polarity &= ~GET_DMA_POLARITY(dmanr); - polarity |= p_dma_ch->polarity; + polarity |= p_init->polarity; #if DCRN_POL > 0 mtdcr(DCRN_POL, polarity); #endif diff --git a/arch/ppc64/kernel/entry.S b/arch/ppc64/kernel/entry.S index cdeca63ff55d..74e7d48bc8f3 100644 --- a/arch/ppc64/kernel/entry.S +++ b/arch/ppc64/kernel/entry.S @@ -162,7 +162,7 @@ syscall_error_cont: /* check for syscall tracing or audit */ ld r9,TI_FLAGS(r12) - andi. r0,r9,_TIF_SYSCALL_T_OR_A + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) bne- syscall_exit_trace syscall_exit_trace_cont: @@ -265,16 +265,21 @@ _GLOBAL(save_nvgprs) _GLOBAL(ppc32_sigsuspend) bl .save_nvgprs bl .sys32_sigsuspend - b syscall_exit + b 70f _GLOBAL(ppc64_rt_sigsuspend) bl .save_nvgprs bl .sys_rt_sigsuspend - b syscall_exit + b 70f _GLOBAL(ppc32_rt_sigsuspend) bl .save_nvgprs bl .sys32_rt_sigsuspend + /* If sigsuspend() returns zero, we are going into a signal handler */ +70: cmpdi 0,r3,0 + beq .ret_from_except + /* If it returned -EINTR, we need to return via syscall_exit to set + the SO bit in cr0 and potentially stop for ptrace. */ b syscall_exit _GLOBAL(ppc_fork) @@ -317,7 +322,7 @@ _GLOBAL(ppc64_rt_sigreturn) blt syscall_exit clrrdi r4,r1,THREAD_SHIFT ld r4,TI_FLAGS(r4) - andi. r4,r4,_TIF_SYSCALL_T_OR_A + andi. r4,r4,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) beq+ 81f bl .do_syscall_trace_leave 81: b .ret_from_except diff --git a/arch/ppc64/kernel/pSeries_lpar.c b/arch/ppc64/kernel/pSeries_lpar.c index 678ef2f75b8b..21e8a27cec33 100644 --- a/arch/ppc64/kernel/pSeries_lpar.c +++ b/arch/ppc64/kernel/pSeries_lpar.c @@ -259,6 +259,22 @@ out: return found; } +void vpa_init(int cpu) +{ + int hwcpu = get_hard_smp_processor_id(cpu); + unsigned long vpa = (unsigned long)&(paca[cpu].lppaca); + long ret; + unsigned long flags; + + /* Register the Virtual Processor Area (VPA) */ + flags = 1UL << (63 - 18); + ret = register_vpa(flags, hwcpu, __pa(vpa)); + + if (ret) + printk(KERN_ERR "WARNING: vpa_init: VPA registration for " + "cpu %d (hw %d) of area %lx returns %ld\n", + cpu, hwcpu, __pa(vpa), ret); +} long pSeries_lpar_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long prpn, diff --git a/arch/ppc64/kernel/pSeries_setup.c b/arch/ppc64/kernel/pSeries_setup.c index 6aeeb81ead62..3787957abe17 100644 --- a/arch/ppc64/kernel/pSeries_setup.c +++ b/arch/ppc64/kernel/pSeries_setup.c @@ -234,6 +234,9 @@ static void __init pSeries_setup_arch(void) #endif pSeries_nvram_init(); + + if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) + vpa_init(boot_cpuid); } static int __init pSeries_init_panel(void) diff --git a/arch/ppc64/kernel/pSeries_smp.c b/arch/ppc64/kernel/pSeries_smp.c index ea2c7163f302..6eba926cda70 100644 --- a/arch/ppc64/kernel/pSeries_smp.c +++ b/arch/ppc64/kernel/pSeries_smp.c @@ -59,16 +59,6 @@ extern void pseries_secondary_smp_init(unsigned long); -static void vpa_init(int cpu) -{ - unsigned long flags, pcpu = get_hard_smp_processor_id(cpu); - - /* Register the Virtual Processor Area (VPA) */ - flags = 1UL << (63 - 18); - register_vpa(flags, pcpu, __pa((unsigned long)&(paca[cpu].lppaca))); -} - - /* Get state of physical CPU. * Return codes: * 0 - The processor is in the RTAS stopped state diff --git a/arch/ppc64/kernel/pmac_pci.c b/arch/ppc64/kernel/pmac_pci.c index f6729dd7a348..00d1db487cdf 100644 --- a/arch/ppc64/kernel/pmac_pci.c +++ b/arch/ppc64/kernel/pmac_pci.c @@ -739,8 +739,8 @@ void __init pmac_pci_init(void) pmac_check_ht_link(); - /* Tell pci.c to use the common resource allocation mecanism */ - pci_probe_only = 0; + /* Tell pci.c to not use the common resource allocation mecanism */ + pci_probe_only = 1; /* Allow all IO */ io_page_mask = -1; diff --git a/arch/ppc64/kernel/prom_init.c b/arch/ppc64/kernel/prom_init.c index 785adcc92ae7..e570799a84cc 100644 --- a/arch/ppc64/kernel/prom_init.c +++ b/arch/ppc64/kernel/prom_init.c @@ -1108,6 +1108,16 @@ static void __init prom_init_stdout(void) } } +static void __init prom_close_stdin(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + ihandle val; + + if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) + call_prom("close", 1, 0, val); +} + static int __init prom_find_machine_type(void) { unsigned long offset = reloc_offset(); @@ -1686,6 +1696,9 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long prom_printf("copying OF device tree ...\n"); flatten_device_tree(); + /* in case stdin is USB and still active on IBM machines... */ + prom_close_stdin(); + /* * Call OF "quiesce" method to shut down pending DMA's from * devices etc... diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c index 0a4011f58f2f..b3f560bd839e 100644 --- a/arch/ppc64/kernel/ptrace.c +++ b/arch/ppc64/kernel/ptrace.c @@ -318,7 +318,8 @@ void do_syscall_trace_leave(void) if (unlikely(current->audit_context)) audit_syscall_exit(current, 0); /* FIXME: pass pt_regs */ - if (test_thread_flag(TIF_SYSCALL_TRACE) + if ((test_thread_flag(TIF_SYSCALL_TRACE) + || test_thread_flag(TIF_SINGLESTEP)) && (current->ptrace & PT_PTRACED)) do_syscall_trace(); } diff --git a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c index 956424bebe5d..abb144118e01 100644 --- a/arch/ppc64/kernel/setup.c +++ b/arch/ppc64/kernel/setup.c @@ -1020,11 +1020,11 @@ void __init setup_arch(char **cmdline_p) /* set up the bootmem stuff with available memory */ do_init_bootmem(); + ppc_md.setup_arch(); + /* Select the correct idle loop for the platform. */ idle_setup(); - ppc_md.setup_arch(); - paging_init(); ppc64_boot_msg(0x15, "Setup Done"); } diff --git a/arch/ppc64/kernel/signal.c b/arch/ppc64/kernel/signal.c index 3d284a39c9e2..1e228bdc0e73 100644 --- a/arch/ppc64/kernel/signal.c +++ b/arch/ppc64/kernel/signal.c @@ -26,6 +26,7 @@ #include <linux/unistd.h> #include <linux/stddef.h> #include <linux/elf.h> +#include <linux/ptrace.h> #include <asm/sigcontext.h> #include <asm/ucontext.h> #include <asm/uaccess.h> @@ -98,7 +99,7 @@ long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(&saveset, regs)) - return regs->gpr[3]; + return 0; } } @@ -387,7 +388,7 @@ badframe: return 0; } -static void setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { /* Handler is *really* a pointer to the function descriptor for @@ -452,7 +453,10 @@ static void setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, if (err) goto badframe; - return; + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + + return 1; badframe: #if DEBUG_SIG @@ -460,25 +464,30 @@ badframe: regs, frame, newsp); #endif force_sigsegv(signr, current); + return 0; } /* * OK, we're invoking a handler */ -static void handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) +static int handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { + int ret; + /* Set up Signal Frame */ - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (ret && !(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } + + return ret; } static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) @@ -538,8 +547,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) /* Whee! Actually deliver the signal. */ if (TRAP(regs) == 0x0C00) syscall_restart(regs, &ka); - handle_signal(signr, &ka, &info, oldset, regs); - return 1; + return handle_signal(signr, &ka, &info, oldset, regs); } if (TRAP(regs) == 0x0C00) { /* System Call! */ diff --git a/arch/ppc64/kernel/signal32.c b/arch/ppc64/kernel/signal32.c index 642268815700..bdc0d624b86f 100644 --- a/arch/ppc64/kernel/signal32.c +++ b/arch/ppc64/kernel/signal32.c @@ -25,6 +25,7 @@ #include <linux/errno.h> #include <linux/elf.h> #include <linux/compat.h> +#include <linux/ptrace.h> #include <asm/ppc32.h> #include <asm/uaccess.h> #include <asm/ppcdebug.h> @@ -284,14 +285,12 @@ long sys32_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, schedule(); if (do_signal32(&saveset, regs)) /* - * If a signal handler needs to be called, - * do_signal32() has set R3 to the signal number (the - * first argument of the signal handler), so don't - * overwrite that with EINTR ! - * In the other cases, do_signal32() doesn't touch - * R3, so it's still set to -EINTR (see above). + * Returning 0 means we return to userspace via + * ret_from_except and thus restore all user + * registers from *regs. This is what we need + * to do when a signal has been delivered. */ - return regs->gpr[3]; + return 0; } } @@ -587,14 +586,12 @@ int sys32_rt_sigsuspend(compat_sigset_t __user * unewset, size_t sigsetsize, int schedule(); if (do_signal32(&saveset, regs)) /* - * If a signal handler needs to be called, - * do_signal32() has set R3 to the signal number (the - * first argument of the signal handler), so don't - * overwrite that with EINTR ! - * In the other cases, do_signal32() doesn't touch - * R3, so it's still set to -EINTR (see above). + * Returning 0 means we return to userspace via + * ret_from_except and thus restore all user + * registers from *regs. This is what we need + * to do when a signal has been delivered. */ - return regs->gpr[3]; + return 0; } } @@ -653,9 +650,9 @@ int sys32_sigaltstack(u32 __new, u32 __old, int r5, * Set up a signal frame for a "real-time" signal handler * (one which gets siginfo). */ -static void handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs, unsigned long newsp) +static int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs * regs, unsigned long newsp) { struct rt_sigframe32 __user *rt_sf; struct mcontext32 __user *frame; @@ -704,7 +701,10 @@ static void handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, regs->trap = 0; regs->result = 0; - return; + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + + return 1; badframe: #if DEBUG_SIG @@ -712,6 +712,7 @@ badframe: regs, frame, newsp); #endif force_sigsegv(sig, current); + return 0; } static long do_setcontext32(struct ucontext32 __user *ucp, struct pt_regs *regs, int sig) @@ -822,7 +823,7 @@ long sys32_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, /* * OK, we're invoking a handler */ -static void handle_signal32(unsigned long sig, struct k_sigaction *ka, +static int handle_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, unsigned long newsp) { @@ -867,7 +868,10 @@ static void handle_signal32(unsigned long sig, struct k_sigaction *ka, regs->trap = 0; regs->result = 0; - return; + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + + return 1; badframe: #if DEBUG_SIG @@ -875,6 +879,7 @@ badframe: regs, frame, *newspp); #endif force_sigsegv(sig, current); + return 0; } /* @@ -984,11 +989,11 @@ int do_signal32(sigset_t *oldset, struct pt_regs *regs) /* Whee! Actually deliver the signal. */ if (ka.sa.sa_flags & SA_SIGINFO) - handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp); + ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp); else - handle_signal32(signr, &ka, &info, oldset, regs, newsp); + ret = handle_signal32(signr, &ka, &info, oldset, regs, newsp); - if (!(ka.sa.sa_flags & SA_NODEFER)) { + if (ret && !(ka.sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked, ¤t->blocked, &ka.sa.sa_mask); @@ -997,5 +1002,5 @@ int do_signal32(sigset_t *oldset, struct pt_regs *regs) spin_unlock_irq(¤t->sighand->siglock); } - return 1; + return ret; } diff --git a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c index db57a8c17316..a46c63b500f7 100644 --- a/arch/ppc64/kernel/smp.c +++ b/arch/ppc64/kernel/smp.c @@ -76,8 +76,6 @@ extern unsigned char stab_array[]; extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); -extern long register_vpa(unsigned long flags, unsigned long proc, - unsigned long vpa); int smt_enabled_at_boot = 1; diff --git a/arch/ppc64/kernel/xics.c b/arch/ppc64/kernel/xics.c index 75253ea60ac2..674166e14265 100644 --- a/arch/ppc64/kernel/xics.c +++ b/arch/ppc64/kernel/xics.c @@ -504,7 +504,7 @@ nextnode: np; np = of_find_node_by_type(np, "cpu")) { ireg = (uint *)get_property(np, "reg", &ilen); - if (ireg && ireg[0] == hard_smp_processor_id()) { + if (ireg && ireg[0] == boot_cpuid_phys) { ireg = (uint *)get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); i = ilen / sizeof(int); diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index f2b5cef4e62e..b448166f5da9 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1508,7 +1508,7 @@ syscall_is_too_hard: .globl ret_sys_call ret_sys_call: ld [%curptr + TI_FLAGS], %l6 - cmp %o0, -ENOIOCTLCMD + cmp %o0, -ERESTART_RESTARTBLOCK ld [%sp + STACKFRAME_SZ + PT_PSR], %g3 set PSR_C, %g2 bgeu 1f @@ -1587,7 +1587,7 @@ solaris_syscall: st %o0, [%sp + STACKFRAME_SZ + PT_I0] set PSR_C, %g2 - cmp %o0, -ENOIOCTLCMD + cmp %o0, -ERESTART_RESTARTBLOCK bgeu 1f ld [%sp + STACKFRAME_SZ + PT_PSR], %g3 @@ -1678,7 +1678,7 @@ bsd_is_too_hard: st %o0, [%sp + STACKFRAME_SZ + PT_I0] set PSR_C, %g2 - cmp %o0, -ENOIOCTLCMD + cmp %o0, -ERESTART_RESTARTBLOCK bgeu 1f ld [%sp + STACKFRAME_SZ + PT_PSR], %g3 diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 3115cd933acb..a989a80f58c7 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.10-rc2 -# Tue Nov 16 11:09:23 2004 +# Linux kernel version: 2.6.10-rc3 +# Wed Dec 8 21:14:26 2004 # CONFIG_64BIT=y CONFIG_MMU=y @@ -233,6 +233,7 @@ CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_SX8=m CONFIG_BLK_DEV_UB=m # CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_INITRAMFS_SOURCE="" CONFIG_CDROM_PKTCDVD=m CONFIG_CDROM_PKTCDVD_BUFFERS=8 @@ -387,7 +388,6 @@ CONFIG_SCSI_QLOGIC_ISP=m CONFIG_SCSI_QLOGIC_FC=y CONFIG_SCSI_QLOGIC_FC_FIRMWARE=y # CONFIG_SCSI_QLOGIC_1280 is not set -# CONFIG_SCSI_QLOGIC_1280_1040 is not set CONFIG_SCSI_QLOGICPTI=m CONFIG_SCSI_QLA2XXX=y # CONFIG_SCSI_QLA21XX is not set @@ -1045,6 +1045,11 @@ CONFIG_ISDN_CAPI_CAPI20=m # Active AVM cards # CONFIG_CAPI_AVM=y +CONFIG_ISDN_DRV_AVMB1_B1PCI=m +CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y +CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m +CONFIG_ISDN_DRV_AVMB1_T1PCI=m +CONFIG_ISDN_DRV_AVMB1_C4=m # # Active Eicon DIVA Server cards @@ -1178,6 +1183,7 @@ CONFIG_I2C_PCA_ISA=m CONFIG_I2C_SENSOR=m CONFIG_SENSORS_ADM1021=m CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m CONFIG_SENSORS_ADM1031=m CONFIG_SENSORS_ASB100=m CONFIG_SENSORS_DS1621=m @@ -1439,39 +1445,6 @@ CONFIG_DVB=y CONFIG_DVB_CORE=m # -# DVB-S (satellite) frontends -# -CONFIG_DVB_STV0299=m -CONFIG_DVB_CX24110=m -CONFIG_DVB_GRUNDIG_29504_491=m -CONFIG_DVB_MT312=m -CONFIG_DVB_VES1X93=m - -# -# DVB-T (terrestrial) frontends -# -CONFIG_DVB_SP887X=m -CONFIG_DVB_ALPS_TDLB7=m -CONFIG_DVB_ALPS_TDMB7=m -CONFIG_DVB_CX22702=m -CONFIG_DVB_GRUNDIG_29504_401=m -CONFIG_DVB_TDA1004X=m -CONFIG_DVB_NXT6000=m -CONFIG_DVB_MT352=m -CONFIG_DVB_DIB3000MB=m - -# -# DVB-C (cable) frontends -# -CONFIG_DVB_ATMEL_AT76C651=m -CONFIG_DVB_VES1820=m - -# -# Misc. Frontend Modules -# -CONFIG_DVB_TWINHAN_DST=m - -# # Supported SAA7146 based PCI Adapters # CONFIG_DVB_AV7110=m @@ -1487,6 +1460,8 @@ CONFIG_DVB_BUDGET_PATCH=m # CONFIG_DVB_TTUSB_BUDGET is not set CONFIG_DVB_TTUSB_DEC=m CONFIG_DVB_DIBUSB=m +CONFIG_DVB_DIBUSB_MISDESIGNED_AN2235=y +CONFIG_DVB_DIBCOM_DEBUG=y CONFIG_DVB_CINERGYT2=m # CONFIG_DVB_CINERGYT2_TUNING is not set @@ -1499,6 +1474,45 @@ CONFIG_DVB_B2C2_SKYSTAR=m # Supported BT878 Adapters # CONFIG_DVB_BT8XX=m + +# +# Supported DVB Frontends +# + +# +# Customise DVB Frontends +# + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_STV0299=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA80XX=m +CONFIG_DVB_MT312=m +CONFIG_DVB_VES1X93=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_CX22702=m +CONFIG_DVB_L64781=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_DIB3000MB=m + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_ATMEL_AT76C651=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_STV0297=m CONFIG_VIDEO_SAA7146=m CONFIG_VIDEO_SAA7146_VV=m CONFIG_VIDEO_VIDEOBUF=m @@ -1634,6 +1648,10 @@ CONFIG_USB_UHCI_HCD=m # CONFIG_USB_MIDI is not set CONFIG_USB_ACM=m CONFIG_USB_PRINTER=m + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set CONFIG_USB_STORAGE_RW_DETECT=y diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index fb3e3b7806f1..c13eaf030758 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -79,7 +79,7 @@ typedef struct { #define elf_check_arch(x) (((x)->e_machine == EM_SPARC) || ((x)->e_machine == EM_SPARC32PLUS)) -#define ELF_ET_DYN_BASE 0x08000000 +#define ELF_ET_DYN_BASE 0x70000000 #include <asm/processor.h> diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 0accb458c859..c4b705d0e00c 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1778,7 +1778,7 @@ ret_sys_call: stx %l0, [%curptr + TI_FLAGS] 1: - cmp %o0, -ENOIOCTLCMD + cmp %o0, -ERESTART_RESTARTBLOCK bgeu,pn %xcc, 1f andcc %l0, _TIF_SYSCALL_TRACE, %l6 80: diff --git a/arch/sparc64/solaris/entry64.S b/arch/sparc64/solaris/entry64.S index ba045302373c..0cc9dad75c5e 100644 --- a/arch/sparc64/solaris/entry64.S +++ b/arch/sparc64/solaris/entry64.S @@ -109,7 +109,7 @@ ret_from_solaris: sra %o0, 0, %o0 mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %g3 - cmp %o0, -ENOIOCTLCMD + cmp %o0, -ERESTART_RESTARTBLOCK sllx %g2, 32, %g2 bgeu,pn %xcc, 1f andcc %l6, _TIF_SYSCALL_TRACE, %l6 diff --git a/arch/um/Kconfig_block b/arch/um/Kconfig_block index 9ca139520dce..1f0f9ccb4a01 100644 --- a/arch/um/Kconfig_block +++ b/arch/um/Kconfig_block @@ -43,6 +43,10 @@ config BLK_DEV_NBD config BLK_DEV_RAM tristate "RAM disk support" +config BLK_DEV_RAM_COUNT + int "Default number of RAM disks" if BLK_DEV_RAM + default "16" + config BLK_DEV_RAM_SIZE int "Default RAM disk size" depends on BLK_DEV_RAM diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 4486df15e55b..4ffa04271050 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -306,6 +306,7 @@ config NR_CPUS config GART_IOMMU bool "IOMMU support" + depends on PCI help Support the K8 IOMMU. Needed to run systems with more than 4GB of memory properly with 32-bit PCI devices that do not support DAC (Double Address diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 8a6901de1914..73dd5cc38116 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -615,12 +615,13 @@ static int ia_avail_descs(IADEV *iadev) { return tmp; } +static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb); + static int ia_que_tx (IADEV *iadev) { struct sk_buff *skb; int num_desc; struct atm_vcc *vcc; struct ia_vcc *iavcc; - static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb); num_desc = ia_avail_descs(iadev); while (num_desc && (skb = skb_dequeue(&iadev->tx_backlog))) { diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 9adbec0021e9..6ed9ddbbf94c 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1605,7 +1605,7 @@ static int __devinit zatm_init_one(struct pci_dev *pci_dev, goto out_disable; zatm_dev->pci_dev = pci_dev; - ZATM_DEV(dev) = zatm_dev; + dev = (struct atm_dev *)zatm_dev; zatm_dev->copper = (int)ent->driver_data; if ((ret = zatm_init(dev)) || (ret = zatm_start(dev))) goto out_release; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index f2c29756e6ed..97bf2d4b9207 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1100,7 +1100,7 @@ cleanup1: return(status); } default: - return -EBADRQC; + return -ENOTTY; } } diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 75d6c0387b74..893e26af0071 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -867,6 +867,9 @@ static inline void cfq_account_dispatch(struct cfq_rq *crq) struct cfq_data *cfqd = cfqq->cfqd; unsigned long now, elapsed; + if (!blk_fs_request(crq->request)) + return; + /* * accounted bit is necessary since some drivers will call * elv_next_request() many times for the same request (eg ide) @@ -912,6 +915,9 @@ cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq) { struct cfq_data *cfqd = cfqq->cfqd; + if (!crq->accounted) + return; + WARN_ON(!cfqd->rq_in_driver); cfqd->rq_in_driver--; diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 244f39b02ec8..88f9a89406d6 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -67,7 +67,7 @@ #endif #ifdef CONFIG_BT_HCIUSB_SCO -static int isoc = 1; +static int isoc = 2; #endif #define VERSION "2.7" @@ -898,7 +898,7 @@ static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_ISOC: if (ep->desc.wMaxPacketSize < size || - uif->desc.bAlternateSetting > 2) + uif->desc.bAlternateSetting != isoc) break; size = ep->desc.wMaxPacketSize; @@ -1037,7 +1037,7 @@ module_init(hci_usb_init); module_exit(hci_usb_exit); #ifdef CONFIG_BT_HCIUSB_SCO -module_param(isoc, bool, 0); +module_param(isoc, int, 0644); MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support"); #endif diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 88467495983a..8b4a2d4eb45a 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -1161,11 +1161,11 @@ static void EvaluateStatus(int st) return; } /*==========================================================================*/ +static int cmd_out_T(void); + static int get_state_T(void) { int i; - - static int cmd_out_T(void); clr_cmdbuf(); current_drive->n_bytes=1; @@ -1308,13 +1308,14 @@ static int cc_ReadError(void) return (i); } /*==========================================================================*/ +static int cc_DriveReset(void); + static int cmd_out_T(void) { #undef CMDT_TRIES #define CMDT_TRIES 1000 #define TEST_FALSE_FF 1 - - static int cc_DriveReset(void); + int i, j, l=0, m, ntries; unsigned long flags; diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 92a13643812c..a47386427eb2 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -55,9 +55,9 @@ static void pty_close(struct tty_struct * tty, struct file * filp) if (!tty->link) return; tty->link->packet = 0; + set_bit(TTY_OTHER_CLOSED, &tty->link->flags); wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); - set_bit(TTY_OTHER_CLOSED, &tty->link->flags); if (tty->driver->subtype == PTY_TYPE_MASTER) { set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS diff --git a/drivers/char/random.c b/drivers/char/random.c index 7261d99a8987..cfd25286c1d2 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -572,8 +572,8 @@ static void free_entropy_store(struct entropy_store *r) * it's cheap to do so and helps slightly in the expected case where * the entropy is concentrated in the low-order bits. */ -static void add_entropy_words(struct entropy_store *r, const __u32 *in, - int nwords) +static void __add_entropy_words(struct entropy_store *r, const __u32 *in, + int nwords, __u32 out[16]) { static __u32 const twist_table[8] = { 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, @@ -626,9 +626,23 @@ static void add_entropy_words(struct entropy_store *r, const __u32 *in, r->input_rotate = input_rotate; r->add_ptr = add_ptr; + if (out) { + for (i = 0; i < 16; i++) { + out[i] = r->pool[add_ptr]; + add_ptr = (add_ptr - 1) & wordmask; + } + } + spin_unlock_irqrestore(&r->lock, flags); } +static inline void add_entropy_words(struct entropy_store *r, const __u32 *in, + int nwords) +{ + __add_entropy_words(r, in, nwords, NULL); +} + + /* * Credit (or debit) the entropy store with n bits of entropy */ @@ -1342,7 +1356,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, size_t nbytes, int flags) { ssize_t ret, i; - __u32 tmp[TMP_BUF_SIZE]; + __u32 tmp[TMP_BUF_SIZE], data[16]; __u32 x; unsigned long cpuflags; @@ -1422,7 +1436,15 @@ static ssize_t extract_entropy(struct entropy_store *r, void * buf, HASH_TRANSFORM(tmp, r->pool+i); add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1); } - + + /* + * To avoid duplicates, we atomically extract a + * portion of the pool while mixing, and hash one + * final time. + */ + __add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1, data); + HASH_TRANSFORM(tmp, data); + /* * In case the hash function has some recognizable * output pattern, we fold it in half. diff --git a/drivers/char/vt.c b/drivers/char/vt.c index f501498b643d..8de27feaac5f 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -768,6 +768,8 @@ inline int resize_screen(int currcons, int width, int height) * [this is to be used together with some user program * like resize that changes the hardware videomode] */ +#define VC_RESIZE_MAXCOL (32767) +#define VC_RESIZE_MAXROW (32767) int vc_resize(int currcons, unsigned int cols, unsigned int lines) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; @@ -780,6 +782,9 @@ int vc_resize(int currcons, unsigned int cols, unsigned int lines) if (!vc_cons_allocated(currcons)) return -ENXIO; + if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW) + return -EINVAL; + new_cols = (cols ? cols : video_num_columns); new_rows = (lines ? lines : video_num_lines); new_row_size = new_cols << 1; diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c index eb13091b8b15..82396e06c8a8 100644 --- a/drivers/char/watchdog/ixp4xx_wdt.c +++ b/drivers/char/watchdog/ixp4xx_wdt.c @@ -216,7 +216,7 @@ static void __exit ixp4xx_wdt_exit(void) module_init(ixp4xx_wdt_init); module_exit(ixp4xx_wdt_exit); -MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net">); +MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); module_param(heartbeat, int, 0); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index cf4fc93c84f3..2dae7b293314 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -890,8 +890,14 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry); return ide_started; } else { + unsigned long flags; + /* packet command */ - HWIF(drive)->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG); + spin_lock_irqsave(&ide_lock, flags); + hwif->OUTBSYNC(drive, WIN_PACKETCMD, IDE_COMMAND_REG); + ndelay(400); + spin_unlock_irqrestore(&ide_lock, flags); + return (*handler) (drive); } } @@ -2353,25 +2359,31 @@ static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense) /* Read the multisession information. */ if (toc->hdr.first_track != CDROM_LEADOUT) { /* Read the multisession information. */ - stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, + stat = cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp, sizeof(ms_tmp), sense); if (stat) return stat; + + toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba); } else { - ms_tmp.ent.addr.msf.minute = 0; - ms_tmp.ent.addr.msf.second = 2; - ms_tmp.ent.addr.msf.frame = 0; ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT; + toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */ } #if ! STANDARD_ATAPI - if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) + if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) { + /* Re-read multisession information using MSF format */ + stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, + sizeof(ms_tmp), sense); + if (stat) + return stat; + msf_from_bcd (&ms_tmp.ent.addr.msf); + toc->last_session_lba = msf_to_lba(ms_tmp.ent.addr.msf.minute, + ms_tmp.ent.addr.msf.second, + ms_tmp.ent.addr.msf.frame); + } #endif /* not STANDARD_ATAPI */ - toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute, - ms_tmp.ent.addr.msf.second, - ms_tmp.ent.addr.msf.frame); - toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track); /* Now try to get the total cdrom capacity. */ diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index bf6a995d4275..aeaed74f949a 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -131,11 +131,9 @@ static const struct drive_list_entry drive_blacklist [] = { { "CD-ROM Drive/F5A", "ALL" }, { "WPI CDD-820", "ALL" }, { "SAMSUNG CD-ROM SC-148C", "ALL" }, - { "SAMSUNG CD-ROM SC-148F", "ALL" }, { "SAMSUNG CD-ROM SC", "ALL" }, { "SanDisk SDP3B-64" , "ALL" }, { "SAMSUNG CD-ROM SN-124", "ALL" }, - { "PLEXTOR CD-R PX-W8432T", "ALL" }, { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, { "_NEC DV5800A", "ALL" }, { NULL , NULL } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9403a8ee196c..d0ffc54b6625 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1327,9 +1327,6 @@ int ideprobe_init (void) for (index = 0; index < MAX_HWIFS; ++index) probe[index] = !ide_hwifs[index].present; - /* - * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports - */ for (index = 0; index < MAX_HWIFS; ++index) if (probe[index]) probe_hwif(&ide_hwifs[index]); diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index 537c87c5d5a3..fb88711812e6 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -226,6 +226,8 @@ static int __init ali14xx_probe(void) probe_hwif_init(hwif); probe_hwif_init(mate); + create_proc_ide_interfaces(); + return 0; } diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c index 65a1e7f4f3a1..20eb5b872ca9 100644 --- a/drivers/ide/legacy/dtc2278.c +++ b/drivers/ide/legacy/dtc2278.c @@ -141,6 +141,8 @@ static int __init probe_dtc2278(void) probe_hwif_init(hwif); probe_hwif_init(mate); + create_proc_ide_interfaces(); + return 0; } diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index eb1c448b4979..a77fb249d5cf 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -352,6 +352,8 @@ int __init ht6560b_init(void) probe_hwif_init(hwif); probe_hwif_init(mate); + create_proc_ide_interfaces(); + return 0; release_region: diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index 0614905e47cf..563fab0098be 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -429,6 +429,9 @@ static int __init qd_probe(int base) qd_setup(hwif, base, config, QD6500_DEF_DATA, QD6500_DEF_DATA, &qd6500_tune_drive); + + create_proc_ide_interfaces(); + return 1; } @@ -459,6 +462,8 @@ static int __init qd_probe(int base) &qd6580_tune_drive); qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + create_proc_ide_interfaces(); + return 1; } else { ide_hwif_t *mate; @@ -477,6 +482,8 @@ static int __init qd_probe(int base) &qd6580_tune_drive); qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + create_proc_ide_interfaces(); + return 0; /* no other qd65xx possible */ } } diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c index 68961c2cfe87..cdbdb2ff9f15 100644 --- a/drivers/ide/legacy/umc8672.c +++ b/drivers/ide/legacy/umc8672.c @@ -161,6 +161,8 @@ static int __init umc8672_probe(void) probe_hwif_init(hwif); probe_hwif_init(mate); + create_proc_ide_interfaces(); + return 0; } diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 51ea019da0cc..095ea1e3ac14 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -8,6 +8,7 @@ * Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org) * May be copied or modified under the terms of the GNU General Public License * Copyright (C) 2002 Alan Cox <alan@redhat.com> + * ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw> * * (U)DMA capable version of ali 1533/1543(C), 1535(D) * @@ -799,8 +800,9 @@ static void __init init_hwif_ali15x3 (ide_hwif_t *hwif) s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; int irq = -1; - - hwif->irq = hwif->channel ? 15 : 14; + + if (hwif->pci_dev->device == PCI_DEVICE_ID_AL_M5229) + hwif->irq = hwif->channel ? 15 : 14; if (isa_dev) { /* @@ -889,6 +891,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev static struct pci_device_id alim15x3_pci_tbl[] = { { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl); diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index ee4dae238360..f7deea91a06b 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -347,6 +347,7 @@ static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_devic static struct pci_device_id atiixp_pci_tbl[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 0, }, }; MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 898bb251d3e3..fe3ac755352f 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -230,7 +230,7 @@ static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif) { u16 CIS = 0, mask = (hwif->channel) ? (1<<11) : (1<<10); pci_read_config_word(hwif->pci_dev, 0x50, &CIS); - return ((u8)(CIS & mask)); + return (CIS & mask) ? 1 : 0; } /* diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 1a9dfaaa596a..7c3f6e7bcdf7 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -2933,8 +2933,8 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, d->ctrlClear = 0; d->cmdPtr = 0; - d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); - d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); + d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_ATOMIC); + d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); if (d->buf_cpu == NULL || d->buf_bus == NULL) { PRINT(KERN_ERR, "Failed to allocate dma buffer"); @@ -2945,8 +2945,8 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), - GFP_KERNEL); - d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); + GFP_ATOMIC); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, "Failed to allocate dma prg"); @@ -2956,7 +2956,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); - d->spb = kmalloc(d->split_buf_size, GFP_KERNEL); + d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); if (d->spb == NULL) { PRINT(KERN_ERR, "Failed to allocate split buffer"); diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 93849a6cc98d..ab0a151bd56b 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -98,8 +98,8 @@ static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device pci_enable_device(pci); gp->gameport.io = pci_resource_start(pci, 0); if ((gp->res_port = request_region(gp->gameport.io, 0x10, "FM801 GP")) == NULL) { - kfree(gp); printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f); + kfree(gp); return -1; } diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 3a6acdeada5c..6fcb2cf7b0b6 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -794,13 +794,13 @@ setup_avm_pcipnp(struct IsdnCard *card) #ifdef CONFIG_PCI if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, dev_avm))) { + if (pci_enable_device(dev_avm)) + return(0); cs->irq = dev_avm->irq; if (!cs->irq) { printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); return(0); } - if (pci_enable_device(dev_avm)) - return(0); cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); if (!cs->hw.avm.cfg_reg) { printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c index f491e392efb6..5c3a549a548e 100644 --- a/drivers/media/common/saa7146_core.c +++ b/drivers/media/common/saa7146_core.c @@ -25,11 +25,11 @@ struct list_head saa7146_devices; struct semaphore saa7146_devices_lock; static int initialized = 0; -int saa7146_num = 0; +static int saa7146_num = 0; unsigned int saa7146_debug = 0; -MODULE_PARM(saa7146_debug,"i"); +module_param(saa7146_debug, int, 0644); MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); #if 0 @@ -48,7 +48,7 @@ static void dump_registers(struct saa7146_dev* dev) * gpio and debi helper functions ****************************************************************************/ -/* write "data" to the gpio-pin "pin" */ +/* write "data" to the gpio-pin "pin" -- unused */ void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data) { u32 value = 0; @@ -67,7 +67,7 @@ void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data) } /* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ -int saa7146_wait_for_debi_done(struct saa7146_dev *dev) +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) { unsigned long start; @@ -80,6 +80,8 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev) DEB_S(("timed out while waiting for registers getting programmed\n")); return -ETIMEDOUT; } + if (nobusyloop) + msleep(1); } /* wait for transfer to complete */ @@ -92,6 +94,8 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev) DEB_S(("timed out while waiting for transfer completion\n")); return -ETIMEDOUT; } + if (nobusyloop) + msleep(1); } return 0; @@ -248,10 +252,9 @@ void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) /********************************************************************************/ /* interrupt handler */ - static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) { - struct saa7146_dev *dev = (struct saa7146_dev*)dev_id; + struct saa7146_dev *dev = dev_id; u32 isr = 0; /* read out the interrupt status register */ @@ -289,7 +292,7 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) if (0 != (isr & (MASK_16|MASK_17))) { u32 status = saa7146_read(dev, I2C_STATUS); if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) { - IER_DISABLE(dev, MASK_16|MASK_17); + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); /* only wake up if we expect something */ if( 0 != dev->i2c_op ) { u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2; @@ -308,7 +311,7 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) if( 0 != isr ) { ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr)); ERR(("disabling interrupt source(s)!\n")); - IER_DISABLE(dev,isr); + SAA7146_IER_DISABLE(dev,isr); } return IRQ_HANDLED; } @@ -318,16 +321,15 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) { - unsigned long adr = 0, len = 0; - struct saa7146_dev* dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL); - struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; struct saa7146_extension* ext = pci_ext->ext; - int err = 0; + struct saa7146_dev *dev; + int err = -ENOMEM; - if (!(dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL))) { + dev = kmalloc(sizeof(struct saa7146_dev), GFP_KERNEL); + if (!dev) { ERR(("out of memory.\n")); - return -ENOMEM; + goto out; } /* clear out mem for sure */ @@ -335,38 +337,37 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent DEB_EE(("pci:%p\n",pci)); - if (pci_enable_device(pci)) { + err = pci_enable_device(pci); + if (err < 0) { ERR(("pci_enable_device() failed.\n")); - err = -EIO; - goto pci_error; + goto err_free; } /* enable bus-mastering */ pci_set_master(pci); dev->pci = pci; + /* get chip-revision; this is needed to enable bug-fixes */ - if( 0 > pci_read_config_dword(dev->pci, PCI_CLASS_REVISION, &dev->revision)) { + err = pci_read_config_dword(pci, PCI_CLASS_REVISION, &dev->revision); + if (err < 0) { ERR(("pci_read_config_dword() failed.\n")); - err = -ENODEV; - goto pci_error; + goto err_disable; } dev->revision &= 0xf; /* remap the memory from virtual to physical adress */ - adr = pci_resource_start(pci,0); - len = pci_resource_len(pci,0); - if (!request_mem_region(pci_resource_start(pci,0), pci_resource_len(pci,0), "saa7146")) { - ERR(("request_mem_region() failed.\n")); - err = -ENODEV; - goto pci_error; - } + err = pci_request_region(pci, 0, "saa7146"); + if (err < 0) + goto err_disable; - if (!(dev->mem = ioremap(adr,len))) { + dev->mem = ioremap(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + if (!dev->mem) { ERR(("ioremap() failed.\n")); err = -ENODEV; - goto ioremap_error; + goto err_release; } /* we don't do a master reset here anymore, it screws up @@ -386,42 +387,40 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent saa7146_write(dev, MC2, 0xf8000000); /* request an interrupt for the saa7146 */ - if (request_irq(dev->pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT, - dev->name, dev)) - { + err = request_irq(pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT, + dev->name, dev); + if (err < 0) { ERR(("request_irq() failed.\n")); - err = -ENODEV; - goto irq_error; + goto err_unmap; } - /* get memory for various stuff */ - dev->d_rps0.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps0.dma_handle); - if( NULL == dev->d_rps0.cpu_addr ) { err = -ENOMEM; - goto kmalloc_error_1; - } + + /* get memory for various stuff */ + dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, + &dev->d_rps0.dma_handle); + if (!dev->d_rps0.cpu_addr) + goto err_free_irq; memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM); - dev->d_rps1.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps1.dma_handle); - if( NULL == dev->d_rps1.cpu_addr ) { - err = -ENOMEM; - goto kmalloc_error_2; - } + dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, + &dev->d_rps1.dma_handle); + if (!dev->d_rps1.cpu_addr) + goto err_free_rps0; memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM); - dev->d_i2c.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_i2c.dma_handle); - if( NULL == dev->d_i2c.cpu_addr ) { - err = -ENOMEM; - goto kmalloc_error_3; - } + dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, + &dev->d_i2c.dma_handle); + if (!dev->d_i2c.cpu_addr) + goto err_free_rps1; memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM); /* the rest + print status message */ /* create a nice device name */ - sprintf(&dev->name[0], "saa7146 (%d)",saa7146_num); + sprintf(dev->name, "saa7146 (%d)", saa7146_num); - INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision,dev->pci->irq,dev->pci->subsystem_vendor,dev->pci->subsystem_device)); + INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device)); dev->ext = ext; pci_set_drvdata(pci,dev); @@ -438,18 +437,18 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent /* set some sane pci arbitrition values */ saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - if( 0 != ext->probe) { - if( 0 != ext->probe(dev) ) { - DEB_D(("ext->probe() failed for %p. skipping device.\n",dev)); + /* TODO: use the status code of the callback */ + err = -ENODEV; - goto probe_error; - } + + if (ext->probe && ext->probe(dev)) { + DEB_D(("ext->probe() failed for %p. skipping device.\n",dev)); + goto err_free_i2c; } - if( 0 != ext->attach(dev,pci_ext) ) { + if (ext->attach(dev, pci_ext)) { DEB_D(("ext->attach() failed for %p. skipping device.\n",dev)); - err = -ENODEV; - goto attach_error; + goto err_unprobe; } INIT_LIST_HEAD(&dev->item); @@ -457,30 +456,46 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent saa7146_num++; err = 0; - goto out; -attach_error: -probe_error: +out: + return err; + +err_unprobe: pci_set_drvdata(pci,NULL); - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle); -kmalloc_error_3: - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle); -kmalloc_error_2: - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle); -kmalloc_error_1: - free_irq(dev->pci->irq, (void *)dev); -irq_error: +err_free_i2c: + pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, + dev->d_i2c.dma_handle); +err_free_rps1: + pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, + dev->d_rps1.dma_handle); +err_free_rps0: + pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, + dev->d_rps0.dma_handle); +err_free_irq: + free_irq(pci->irq, (void *)dev); +err_unmap: iounmap(dev->mem); -ioremap_error: - release_mem_region(adr,len); -pci_error: +err_release: + pci_release_region(pci, 0); +err_disable: + pci_disable_device(pci); +err_free: kfree(dev); -out: - return err; + goto out; } static void saa7146_remove_one(struct pci_dev *pdev) { - struct saa7146_dev* dev = (struct saa7146_dev*) pci_get_drvdata(pdev); + struct saa7146_dev* dev = pci_get_drvdata(pdev); + struct { + void *addr; + dma_addr_t dma; + } dev_map[] = { + { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, + { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, + { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, + { NULL, 0 } + }, *p; + DEB_EE(("dev:%p\n",dev)); dev->ext->detach(dev); @@ -491,17 +506,15 @@ static void saa7146_remove_one(struct pci_dev *pdev) /* disable all irqs, release irq-routine */ saa7146_write(dev, IER, 0); - free_irq(dev->pci->irq, (void *)dev); + free_irq(pdev->irq, dev); - /* free kernel memory */ - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle); - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle); - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle); + for (p = dev_map; p->addr; p++) + pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma); iounmap(dev->mem); - release_mem_region(pci_resource_start(dev->pci,0), pci_resource_len(dev->pci,0)); - + pci_release_region(pdev, 0); list_del(&dev->item); + pci_disable_device(pdev); kfree(dev); saa7146_num--; diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 0182416676c8..a6dd055b7612 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -1,4 +1,5 @@ #include <media/saa7146_vv.h> +#include <linux/version.h> #define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1) @@ -32,17 +33,6 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) return 1; } -int saa7146_res_check(struct saa7146_fh *fh, unsigned int bit) -{ - return (fh->resources & bit); -} - -int saa7146_res_locked(struct saa7146_dev *dev, unsigned int bit) -{ - struct saa7146_vv *vv = dev->vv_data; - return (vv->resources & bit); -} - void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) { struct saa7146_dev *dev = fh->dev; diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c index e9c96f68edec..7bf6316b3480 100644 --- a/drivers/media/common/saa7146_hlp.c +++ b/drivers/media/common/saa7146_hlp.c @@ -9,11 +9,6 @@ static void calculate_output_format_register(struct saa7146_dev* saa, u32 palett *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16)); } -static void calculate_bcs_ctrl_register(struct saa7146_dev *dev, int brightness, int contrast, int colour, u32 *bcs_ctrl) -{ - *bcs_ctrl = ((brightness << 24) | (contrast << 16) | (colour << 0)); -} - static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl) { *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28); @@ -62,7 +57,7 @@ static struct { }; /* table of attenuation values for horizontal scaling */ -u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; +static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; /* calculate horizontal scale registers */ static int calculate_h_scale_registers(struct saa7146_dev *dev, @@ -208,7 +203,7 @@ static struct { }; /* table of attenuation values for vertical scaling */ -u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; +static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; /* calculate vertical scale registers */ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field, @@ -620,18 +615,6 @@ static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long pal saa7146_write(dev, MC2, (MASK_05 | MASK_21)); } -void saa7146_set_picture_prop(struct saa7146_dev *dev, int brightness, int contrast, int colour) -{ - u32 bcs_ctrl = 0; - - calculate_bcs_ctrl_register(dev, brightness, contrast, colour, &bcs_ctrl); - saa7146_write(dev, BCS_CTRL, bcs_ctrl); - - /* update the bcs register */ - saa7146_write(dev, MC2, (MASK_06 | MASK_22)); -} - - /* select input-source */ void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync) { diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c index a671741140a6..875ebd46623c 100644 --- a/drivers/media/common/saa7146_i2c.c +++ b/drivers/media/common/saa7146_i2c.c @@ -1,7 +1,7 @@ #include <linux/version.h> #include <media/saa7146_vv.h> -u32 saa7146_i2c_func(struct i2c_adapter *adapter) +static u32 saa7146_i2c_func(struct i2c_adapter *adapter) { //fm DEB_I2C(("'%s'.\n", adapter->name)); @@ -190,7 +190,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d saa7146_write(dev, I2C_TRANSFER, *dword); dev->i2c_op = 1; - IER_ENABLE(dev, MASK_16|MASK_17); + SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0); diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index 31af5044164e..55884d935156 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -91,7 +91,7 @@ static int vbi_workaround(struct saa7146_dev *dev) saa7146_write(dev, MC2, MASK_04|MASK_20); /* enable rps1 irqs */ - IER_ENABLE(dev,MASK_28); + SAA7146_IER_ENABLE(dev,MASK_28); /* prepare to wait to be woken up by the irq-handler */ add_wait_queue(&vv->vbi_wq, &wait); @@ -109,7 +109,7 @@ static int vbi_workaround(struct saa7146_dev *dev) current->state = TASK_RUNNING; /* disable rps1 irqs */ - IER_DISABLE(dev,MASK_28); + SAA7146_IER_DISABLE(dev,MASK_28); /* stop video-dma3 */ saa7146_write(dev, MC1, MASK_20); @@ -130,7 +130,7 @@ static int vbi_workaround(struct saa7146_dev *dev) return 0; } -void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) +static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) { struct saa7146_vv *vv = dev->vv_data; @@ -190,7 +190,7 @@ void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, s WRITE_RPS1(CMD_STOP); /* enable rps1 irqs */ - IER_ENABLE(dev, MASK_28); + SAA7146_IER_ENABLE(dev, MASK_28); /* write the address of the rps-program */ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); @@ -325,7 +325,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) saa7146_write(dev, MC1, MASK_29); /* disable rps1 irqs */ - IER_DISABLE(dev, MASK_28); + SAA7146_IER_DISABLE(dev, MASK_28); /* shut down dma 3 transfers */ saa7146_write(dev, MC1, MASK_20); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 03511dc63ea1..87b0f6465812 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -2,7 +2,7 @@ static int max_memory = 32; -MODULE_PARM(max_memory,"i"); +module_param(max_memory, int, 0644); MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); #define IS_CAPTURE_ACTIVE(fh) \ @@ -760,7 +760,7 @@ static int video_begin(struct saa7146_fh *fh) saa7146_write(dev, MC2, MASK_27 ); /* enable rps0 irqs */ - IER_ENABLE(dev, MASK_27); + SAA7146_IER_ENABLE(dev, MASK_27); vv->video_fh = fh; vv->video_status = STATUS_CAPTURE; @@ -805,7 +805,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) saa7146_write(dev, MC1, MASK_28); /* disable rps0 irqs */ - IER_DISABLE(dev, MASK_27); + SAA7146_IER_DISABLE(dev, MASK_27); /* shut down all used video dma transfers */ saa7146_write(dev, MC1, dmas); diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 579d826c9f92..c9a5138e4215 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -21,8 +21,6 @@ config DVB source "drivers/media/dvb/dvb-core/Kconfig" -source "drivers/media/dvb/frontends/Kconfig" - comment "Supported SAA7146 based PCI Adapters" depends on DVB_CORE && PCI source "drivers/media/dvb/ttpci/Kconfig" @@ -42,5 +40,8 @@ comment "Supported BT878 Adapters" depends on DVB_CORE && PCI source "drivers/media/dvb/bt8xx/Kconfig" -endmenu +comment "Supported DVB Frontends" + depends on DVB_CORE +source "drivers/media/dvb/frontends/Kconfig" +endmenu diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index b3a45b4fc835..354f9dfc49f2 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -1,8 +1,24 @@ config DVB_B2C2_SKYSTAR - tristate "Technisat Skystar2 PCI" + tristate "B2C2/Technisat Air/Sky/CableStar 2 PCI" depends on DVB_CORE && PCI + select DVB_STV0299 + select DVB_MT352 + select DVB_MT312 help Support for the Skystar2 PCI DVB card by Technisat, which is equipped with the FlexCopII chipset by B2C2. 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 + this does nothing, but providing basic function for the used usb + protocol. + + Say Y if you own such a device and want to use it. + diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile index df86d99afa3a..9fb1247bfab8 100644 --- a/drivers/media/dvb/b2c2/Makefile +++ b/drivers/media/dvb/b2c2/Makefile @@ -1,3 +1,6 @@ +obj-b2c2-usb = b2c2-usb-core.o b2c2-common.o + obj-$(CONFIG_DVB_B2C2_SKYSTAR) += skystar2.o +obj-$(CONFIG_DVB_B2C2_USB) + = b2c2-usb.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/b2c2/b2c2-common.c b/drivers/media/dvb/b2c2/b2c2-common.c new file mode 100644 index 000000000000..cb42d44f4f98 --- /dev/null +++ b/drivers/media/dvb/b2c2/b2c2-common.c @@ -0,0 +1,214 @@ +/* + * 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; +} + +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, +}; diff --git a/drivers/media/dvb/b2c2/b2c2-usb-core.c b/drivers/media/dvb/b2c2/b2c2-usb-core.c new file mode 100644 index 000000000000..d46c8c0f8c15 --- /dev/null +++ b/drivers/media/dvb/b2c2/b2c2-usb-core.c @@ -0,0 +1,538 @@ +/* + * 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, 0x644); +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 { + 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), +} 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 * HZ); + + 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 * HZ); + + 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 * HZ); + 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 * HZ); + 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 * HZ); + 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 = 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++) { + deb_info("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset); + int frame_offset = 0; + struct urb *urb = b2c2->iso_urb[i]; + + 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"); diff --git a/drivers/media/dvb/b2c2/skystar2.c b/drivers/media/dvb/b2c2/skystar2.c index a29b8fe10aee..bfebb3d68561 100644 --- a/drivers/media/dvb/b2c2/skystar2.c +++ b/drivers/media/dvb/b2c2/skystar2.c @@ -50,6 +50,9 @@ #include "dvbdev.h" #include "demux.h" #include "dvb_net.h" +#include "stv0299.h" +#include "mt352.h" +#include "mt312.h" static int debug; @@ -77,6 +80,9 @@ struct dmaq { u8 *buffer; }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) +#define __iomem +#endif struct adapter { struct pci_dev *pdev; @@ -118,6 +124,9 @@ struct adapter { int pid_count; int whole_bandwidth_count; u32 mac_filter; + + struct dvb_frontend* fe; + int (*fe_sleep)(struct dvb_frontend* fe); }; #define write_reg_dw(adapter,reg,value) writel(value, adapter->io_mem + reg) @@ -297,13 +306,6 @@ static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg msgs[], int n for (i = 0; i < num; i++) { ddprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i, msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len); - - /* allow only the mt312, mt352 and stv0299 frontends to access the bus */ - if ((msgs[i].addr != 0x0e) && (msgs[i].addr != 0x68) && - (msgs[i].addr != 0x61) && (msgs[i].addr != 0x0f)) { - up(&tmp->i2c_sem); - return -EREMOTEIO; - } } // read command @@ -314,10 +316,10 @@ static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg msgs[], int n up(&tmp->i2c_sem); if (ret != msgs[1].len) { - printk("%s: read error !\n", __FUNCTION__); + dprintk("%s: read error !\n", __FUNCTION__); for (i = 0; i < 2; i++) { - printk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i, + dprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i, msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len); } @@ -337,9 +339,9 @@ static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg msgs[], int n up(&tmp->i2c_sem); if (ret != msgs[0].len - 1) { - printk("%s: write error %i !\n", __FUNCTION__, ret); + dprintk("%s: write error %i !\n", __FUNCTION__, ret); - printk("message %d: flags=0x%x, addr=0x%x, buf[0]=0x%x, len=%d \n", i, + dprintk("message %d: flags=0x%x, addr=0x%x, buf[0]=0x%x, len=%d \n", i, msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len); return -EREMOTEIO; @@ -785,7 +787,7 @@ static int eeprom_read(struct adapter *adapter, u16 addr, u8 *buf, u16 len) return flex_i2c_read(adapter, 0x20000000, 0x50, addr, buf, len); } -u8 calc_lrc(u8 *buf, int len) +static u8 calc_lrc(u8 *buf, int len) { int i; u8 sum; @@ -1773,6 +1775,7 @@ static void free_adapter_object(struct adapter *adapter) if (adapter->io_mem) iounmap(adapter->io_mem); + if (adapter != 0) kfree(adapter); } @@ -2134,12 +2137,11 @@ static int send_diseqc_msg(struct adapter *adapter, int len, u8 *msg, unsigned l return 0; } - -int soft_diseqc(struct adapter *adapter, unsigned int cmd, void *arg) +static int flexcop_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - switch (cmd) { - case FE_SET_TONE: - switch ((fe_sec_tone_mode_t) arg) { + struct adapter* adapter = (struct adapter*) fe->dvb->priv; + + switch(tone) { case SEC_TONE_ON: set_tuner_tone(adapter, 1); break; @@ -2149,129 +2151,312 @@ int soft_diseqc(struct adapter *adapter, unsigned int cmd, void *arg) default: return -EINVAL; }; - break; - case FE_DISEQC_SEND_MASTER_CMD: + return 0; +} + +static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) { - struct dvb_diseqc_master_cmd *cmd = arg; + struct adapter* adapter = (struct adapter*) fe->dvb->priv; send_diseqc_msg(adapter, cmd->msg_len, cmd->msg, 0); - break; + + return 0; } - case FE_DISEQC_SEND_BURST: - send_diseqc_msg(adapter, 0, NULL, (unsigned long) arg); - break; +static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct adapter* adapter = (struct adapter*) fe->dvb->priv; - default: - return -EOPNOTSUPP; - }; + send_diseqc_msg(adapter, 0, NULL, minicmd); return 0; } -static int flexcop_diseqc_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int flexcop_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { - struct adapter *adapter = fe->before_after_data; + struct adapter* adapter = (struct adapter*) fe->dvb->priv; - struct dvb_frontend_info info; + dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__); - fe->ioctl(fe, FE_GET_INFO, &info); - - // we must use different DiSEqC hw + switch (voltage) { + case SEC_VOLTAGE_13: + dprintk("%s: SEC_VOLTAGE_13, %x\n", __FUNCTION__, SEC_VOLTAGE_13); + set_tuner_polarity(adapter, 1); + return 0; - if (strcmp(info.name, "Zarlink MT312") == 0) { - //VP310 using mt312 driver for tuning only: diseqc not wired - //use FCII instead - if (!soft_diseqc(adapter, cmd, arg)) + case SEC_VOLTAGE_18: + dprintk("%s: SEC_VOLTAGE_18, %x\n", __FUNCTION__, SEC_VOLTAGE_18); + set_tuner_polarity(adapter, 2); return 0; + + default: + return -EINVAL; + } } - switch (cmd) { - case FE_SLEEP: +static int flexcop_sleep(struct dvb_frontend* fe) { - dprintk("%s: FE_SLEEP\n", __FUNCTION__); + struct adapter* adapter = (struct adapter*) fe->dvb->priv; + dprintk("%s: FE_SLEEP\n", __FUNCTION__); set_tuner_polarity(adapter, 0); - // return -EOPNOTSUPP, to make DVB core also send "FE_SLEEP" command to frontend. - return -EOPNOTSUPP; + if (adapter->fe_sleep) return adapter->fe_sleep(fe); + return 0; } - case FE_SET_VOLTAGE: +static u32 flexcop_i2c_func(struct i2c_adapter *adapter) { - dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__); + printk("flexcop_i2c_func\n"); - switch ((fe_sec_voltage_t) arg) { - case SEC_VOLTAGE_13: + return I2C_FUNC_I2C; +} - dprintk("%s: SEC_VOLTAGE_13, %x\n", __FUNCTION__, SEC_VOLTAGE_13); +static struct i2c_algorithm flexcop_algo = { + .name = "flexcop i2c algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = master_xfer, + .functionality = flexcop_i2c_func, +}; - set_tuner_polarity(adapter, 1); - return 0; - case SEC_VOLTAGE_18: - dprintk("%s: SEC_VOLTAGE_18, %x\n", __FUNCTION__, SEC_VOLTAGE_18); +static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; - set_tuner_polarity(adapter, 2); + 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; } - return 0; + 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); - default: + return 0; +} - return -EINVAL; - }; - } +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; - default: + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x84; // 0xC4 + buf[3] = 0x08; - return -EOPNOTSUPP; - }; + 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 client_register(struct i2c_client *client) + + +static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) { - struct adapter *adapter = (struct adapter*)i2c_get_adapdata(client->adapter); + 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)); - dprintk("client_register\n"); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - if (client->driver->command) - return client->driver->command(client, FE_REGISTER, adapter->dvb_adapter); return 0; } -static int client_unregister(struct i2c_client *client) +int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) { - struct adapter *adapter = (struct adapter*)i2c_get_adapdata(client->adapter); + u32 div; + unsigned char bs = 0; - dprintk("client_unregister\n"); + #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; - if (client->driver->command) - return client->driver->command(client, FE_UNREGISTER, adapter->dvb_adapter); return 0; } -u32 flexcop_i2c_func(struct i2c_adapter *adapter) +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) { - printk("flexcop_i2c_func\n"); + 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; - return I2C_FUNC_I2C; + 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 i2c_algorithm flexcop_algo = { - .name = "flexcop i2c algorithm", - .id = I2C_ALGO_BIT, - .master_xfer = master_xfer, - .functionality = flexcop_i2c_func, +static struct mt312_config skystar23_samsung_tbdu18132_config = { + + .demod_address = 0x0e, + .pll_set = skystar23_samsung_tbdu18132_pll_set, }; + + +static void frontend_init(struct adapter *skystar2) +{ + switch(skystar2->pdev->device) { + case 0x2103: // Technisat Skystar2 OR Technisat Airstar2 + + // try the skystar2 v2.6 first (stv0299/Samsung tbmu24112(sl1935)) + skystar2->fe = stv0299_attach(&samsung_tbmu24112_config, &skystar2->i2c_adap); + if (skystar2->fe != NULL) { + skystar2->fe->ops->set_voltage = flexcop_set_voltage; + skystar2->fe_sleep = skystar2->fe->ops->sleep; + skystar2->fe->ops->sleep = flexcop_sleep; + break; +} + + // try the airstar2 (mt352/Samsung tdtc9251dh0(??)) + skystar2->fe = mt352_attach(&samsung_tdtc9251dh0_config, &skystar2->i2c_adap); + if (skystar2->fe != NULL) { + skystar2->fe->ops->info.frequency_min = 474000000; + skystar2->fe->ops->info.frequency_max = 858000000; + break; + } + + // try the skystar2 v2.3 (vp310/Samsung tbdu18132(tsa5059)) + skystar2->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &skystar2->i2c_adap); + if (skystar2->fe != NULL) { + skystar2->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; + skystar2->fe->ops->diseqc_send_burst = flexcop_diseqc_send_burst; + skystar2->fe->ops->set_tone = flexcop_set_tone; + skystar2->fe->ops->set_voltage = flexcop_set_voltage; + skystar2->fe_sleep = skystar2->fe->ops->sleep; + skystar2->fe->ops->sleep = flexcop_sleep; + break; + } + break; + } + + if (skystar2->fe == NULL) { + printk("skystar2: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + skystar2->pdev->vendor, + skystar2->pdev->device, + skystar2->pdev->subsystem_vendor, + skystar2->pdev->subsystem_device); + } else { + 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); + skystar2->fe = NULL; + } + } +} + + static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct adapter *adapter; @@ -2298,8 +2483,10 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter = (struct adapter *) pci_get_drvdata(pdev); + dvb_adapter->priv = adapter; adapter->dvb_adapter = dvb_adapter; + init_MUTEX(&adapter->i2c_sem); @@ -2316,16 +2503,12 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->i2c_adap.algo = &flexcop_algo; adapter->i2c_adap.algo_data = NULL; adapter->i2c_adap.id = I2C_ALGO_BIT; - adapter->i2c_adap.client_register = client_register; - adapter->i2c_adap.client_unregister = client_unregister; if (i2c_add_adapter(&adapter->i2c_adap) < 0) { dvb_unregister_adapter (adapter->dvb_adapter); return -ENOMEM; } - dvb_add_frontend_ioctls(adapter->dvb_adapter, flexcop_diseqc_ioctl, NULL, adapter); - dvbdemux = &adapter->demux; dvbdemux->priv = (void *) adapter; @@ -2361,6 +2544,9 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return ret; dvb_net_init(adapter->dvb_adapter, &adapter->dvbnet, &dvbdemux->dmx); + + frontend_init(adapter); + return 0; } @@ -2385,9 +2571,9 @@ static void skystar2_remove(struct pci_dev *pdev) dvb_dmxdev_release(&adapter->dmxdev); dvb_dmx_release(&adapter->demux); - if (adapter->dvb_adapter != NULL) { - dvb_remove_frontend_ioctls(adapter->dvb_adapter, flexcop_diseqc_ioctl, NULL); + if (adapter->fe != NULL) dvb_unregister_frontend(adapter->fe); + if (adapter->dvb_adapter != NULL) { i2c_del_adapter(&adapter->i2c_adap); dvb_unregister_adapter(adapter->dvb_adapter); @@ -2398,7 +2584,7 @@ static void skystar2_remove(struct pci_dev *pdev) static struct pci_device_id skystar2_pci_tbl[] = { {0x000013d0, 0x00002103, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000}, - {0x000013d0, 0x00002200, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000}, //FCIII +/* {0x000013d0, 0x00002200, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000}, UNDEFINED HARDWARE - mail linuxtv.org list */ //FCIII {0,}, }; diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index 9e612199d905..114787f9284f 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -1,6 +1,8 @@ config DVB_BT8XX tristate "Nebula/Pinnacle PCTV/Twinhan PCI cards" depends on DVB_CORE && PCI && VIDEO_BT848 + select DVB_MT352 + select DVB_SP887X help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards and Twinhan DST cards. diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile index 6db28906a274..9da8604b9e18 100644 --- a/drivers/media/dvb/bt8xx/Makefile +++ b/drivers/media/dvb/bt8xx/Makefile @@ -1,5 +1,5 @@ -obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o +obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 2ade73a8b71e..7968bfef3045 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -44,7 +44,7 @@ #include "dmxdev.h" #include "dvbdev.h" #include "bt878.h" -#include "dst-bt878.h" +#include "dst_priv.h" /**************************************/ @@ -559,22 +559,11 @@ static struct pci_driver bt878_pci_driver = { static int bt878_pci_driver_registered = 0; -/* This will be used later by dvb-bt8xx to only use the audio - * dma of certain cards */ -int bt878_find_audio_dma(void) -{ - // pci_register_driver(&bt878_pci_driver); - bt878_pci_driver_registered = 1; - return 0; -} - -EXPORT_SYMBOL(bt878_find_audio_dma); - /*******************************/ /* Module management functions */ /*******************************/ -int bt878_init_module(void) +static int bt878_init_module(void) { bt878_num = 0; bt878_pci_driver_registered = 0; @@ -586,13 +575,13 @@ int bt878_init_module(void) /* bt878_check_chipset(); */ - /* later we register inside of bt878_find_audio_dma + /* later we register inside of bt878_find_audio_dma() * because we may want to ignore certain cards */ bt878_pci_driver_registered = 1; return pci_module_init(&bt878_pci_driver); } -void bt878_cleanup_module(void) +static void bt878_cleanup_module(void) { if (bt878_pci_driver_registered) { bt878_pci_driver_registered = 0; @@ -601,12 +590,10 @@ void bt878_cleanup_module(void) return; } -EXPORT_SYMBOL(bt878_init_module); -EXPORT_SYMBOL(bt878_cleanup_module); module_init(bt878_init_module); module_exit(bt878_cleanup_module); - +//MODULE_AUTHOR("XXX"); MODULE_LICENSE("GPL"); /* diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c new file mode 100644 index 000000000000..62830d177aae --- /dev/null +++ b/drivers/media/dvb/bt8xx/dst.c @@ -0,0 +1,1089 @@ +/* + 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. + +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "dst_priv.h" +#include "dst.h" + +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 demodulator 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; +}; + +static unsigned int dst_verbose = 0; +module_param(dst_verbose, int, 0644); +MODULE_PARM_DESC(dst_verbose, "verbose startup messages, default is 1 (yes)"); +static unsigned int dst_debug = 0; +module_param(dst_debug, int, 0644); +MODULE_PARM_DESC(dst_debug, "debug messages, default is 0 (no)"); + +#define dprintk if (dst_debug) printk + +#define DST_TYPE_IS_SAT 0 +#define DST_TYPE_IS_TERR 1 +#define DST_TYPE_IS_CABLE 2 + +#define DST_TYPE_HAS_NEWTUNE 1 +#define DST_TYPE_HAS_TS204 2 +#define DST_TYPE_HAS_SYMDIV 4 + +#define HAS_LOCK 1 +#define ATTEMPT_TUNE 2 +#define HAS_POWER 4 + +static void dst_packsize(struct dst_state* state, int psize) +{ + union dst_gpio_packet bits; + + bits.psize = psize; + bt878_device_control(state->bt, DST_IG_TS, &bits); +} + +static int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh) +{ + union dst_gpio_packet enb; + union dst_gpio_packet bits; + int err; + + enb.enb.mask = mask; + enb.enb.enable = enbb; + if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) { + dprintk("%s: dst_gpio_enb error (err == %i, mask == 0x%02x, enb == 0x%02x)\n", __FUNCTION__, err, mask, enbb); + return -EREMOTEIO; + } + + /* because complete disabling means no output, no need to do output packet */ + if (enbb == 0) + return 0; + + bits.outp.mask = enbb; + bits.outp.highvals = outhigh; + + if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) { + dprintk("%s: dst_gpio_outb error (err == %i, enbb == 0x%02x, outhigh == 0x%02x)\n", __FUNCTION__, err, enbb, outhigh); + return -EREMOTEIO; + } + return 0; +} + +static int dst_gpio_inb(struct dst_state *state, u8 * result) +{ + union dst_gpio_packet rd_packet; + int err; + + *result = 0; + + if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) { + dprintk("%s: dst_gpio_inb error (err == %i)\n", __FUNCTION__, err); + return -EREMOTEIO; + } + + *result = (u8) rd_packet.rd.value; + return 0; +} + +#define DST_I2C_ENABLE 1 +#define DST_8820 2 + +static int dst_reset8820(struct dst_state *state) +{ + int retval; + /* pull 8820 gpio pin low, wait, high, wait, then low */ + // dprintk ("%s: reset 8820\n", __FUNCTION__); + retval = dst_gpio_outb(state, DST_8820, DST_8820, 0); + if (retval < 0) + return retval; + msleep(10); + retval = dst_gpio_outb(state, DST_8820, DST_8820, DST_8820); + if (retval < 0) + return retval; + /* wait for more feedback on what works here * + msleep(10); + retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0); + if (retval < 0) + return retval; + */ + return 0; +} + +static int dst_i2c_enable(struct dst_state *state) +{ + int retval; + /* pull I2C enable gpio pin low, wait */ + // dprintk ("%s: i2c enable\n", __FUNCTION__); + retval = dst_gpio_outb(state, ~0, DST_I2C_ENABLE, 0); + if (retval < 0) + return retval; + // dprintk ("%s: i2c enable delay\n", __FUNCTION__); + msleep(33); + return 0; +} + +static int dst_i2c_disable(struct dst_state *state) +{ + int retval; + /* release I2C enable gpio pin, wait */ + // dprintk ("%s: i2c disable\n", __FUNCTION__); + retval = dst_gpio_outb(state, ~0, 0, 0); + if (retval < 0) + return retval; + // dprintk ("%s: i2c disable delay\n", __FUNCTION__); + msleep(33); + return 0; +} + +static int dst_wait_dst_ready(struct dst_state *state) +{ + u8 reply; + int retval; + int i; + for (i = 0; i < 200; i++) { + retval = dst_gpio_inb(state, &reply); + if (retval < 0) + return retval; + if ((reply & DST_I2C_ENABLE) == 0) { + dprintk("%s: dst wait ready after %d\n", __FUNCTION__, i); + return 1; + } + msleep(10); + } + dprintk("%s: dst wait NOT ready after %d\n", __FUNCTION__, i); + return 0; +} + +static int write_dst(struct dst_state *state, u8 * data, u8 len) +{ + struct i2c_msg msg = { + .addr = state->config->demod_address,.flags = 0,.buf = data,.len = len + }; + int err; + int cnt; + + if (dst_debug && dst_verbose) { + u8 i; + dprintk("%s writing", __FUNCTION__); + for (i = 0; i < len; i++) { + dprintk(" 0x%02x", data[i]); + } + dprintk("\n"); + } + msleep(30); + for (cnt = 0; cnt < 4; cnt++) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { + dprintk("%s: write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, data[0]); + dst_i2c_disable(state); + msleep(500); + dst_i2c_enable(state); + msleep(500); + continue; + } else + break; + } + if (cnt >= 4) + return -EREMOTEIO; + return 0; +} + +static int read_dst(struct dst_state *state, u8 * ret, u8 len) +{ + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = ret,.len = len }; + int err; + int cnt; + + for (cnt = 0; cnt < 4; cnt++) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { + dprintk("%s: read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, ret[0]); + dst_i2c_disable(state); + dst_i2c_enable(state); + continue; + } else + break; + } + if (cnt >= 4) + return -EREMOTEIO; + dprintk("%s reply is 0x%x\n", __FUNCTION__, ret[0]); + if (dst_debug && dst_verbose) { + for (err = 1; err < len; err++) + dprintk(" 0x%x", ret[err]); + if (err > 1) + dprintk("\n"); + } + return 0; +} + +static int dst_set_freq(struct dst_state *state, u32 freq) +{ + u8 *val; + + state->frequency = freq; + + // dprintk("%s: set frequency %u\n", __FUNCTION__, freq); + if (state->dst_type == DST_TYPE_IS_SAT) { + freq = freq / 1000; + if (freq < 950 || freq > 2150) + return -EINVAL; + val = &state->tx_tuna[0]; + val[2] = (freq >> 8) & 0x7f; + val[3] = (u8) freq; + val[4] = 1; + val[8] &= ~4; + if (freq < 1531) + val[8] |= 4; + } else if (state->dst_type == DST_TYPE_IS_TERR) { + freq = freq / 1000; + if (freq < 137000 || freq > 858000) + return -EINVAL; + val = &state->tx_tuna[0]; + val[2] = (freq >> 16) & 0xff; + val[3] = (freq >> 8) & 0xff; + val[4] = (u8) freq; + val[5] = 0; + switch (state->bandwidth) { + case BANDWIDTH_6_MHZ: + val[6] = 6; + break; + + case BANDWIDTH_7_MHZ: + case BANDWIDTH_AUTO: + val[6] = 7; + break; + + case BANDWIDTH_8_MHZ: + val[6] = 8; + break; + } + + val[7] = 0; + val[8] = 0; + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + /* guess till will get one */ + freq = freq / 1000; + val = &state->tx_tuna[0]; + val[2] = (freq >> 16) & 0xff; + val[3] = (freq >> 8) & 0xff; + val[4] = (u8) freq; + } else + return -EINVAL; + return 0; +} + +static int dst_set_bandwidth(struct dst_state* state, fe_bandwidth_t bandwidth) +{ + u8 *val; + + state->bandwidth = bandwidth; + + if (state->dst_type != DST_TYPE_IS_TERR) + return 0; + + val = &state->tx_tuna[0]; + switch (bandwidth) { + case BANDWIDTH_6_MHZ: + val[6] = 6; + break; + + case BANDWIDTH_7_MHZ: + val[6] = 7; + break; + + case BANDWIDTH_8_MHZ: + val[6] = 8; + break; + + default: + return -EINVAL; + } + return 0; +} + +static int dst_set_inversion(struct dst_state* state, fe_spectral_inversion_t inversion) +{ + u8 *val; + + state->inversion = inversion; + + val = &state->tx_tuna[0]; + + val[8] &= ~0x80; + + switch (inversion) { + case INVERSION_OFF: + break; + case INVERSION_ON: + val[8] |= 0x80; + break; + default: + return -EINVAL; + } + return 0; +} + +static int dst_set_fec(struct dst_state* state, fe_code_rate_t fec) +{ + state->fec = fec; + return 0; +} + +static fe_code_rate_t dst_get_fec(struct dst_state* state) +{ + return state->fec; +} + +static int dst_set_symbolrate(struct dst_state* state, u32 srate) +{ + u8 *val; + u32 symcalc; + u64 sval; + + state->symbol_rate = srate; + + if (state->dst_type == DST_TYPE_IS_TERR) { + return 0; + } + // dprintk("%s: set srate %u\n", __FUNCTION__, srate); + srate /= 1000; + val = &state->tx_tuna[0]; + + if (state->type_flags & DST_TYPE_HAS_SYMDIV) { + sval = srate; + sval <<= 20; + do_div(sval, 88000); + symcalc = (u32) sval; + // dprintk("%s: set symcalc %u\n", __FUNCTION__, symcalc); + val[5] = (u8) (symcalc >> 12); + val[6] = (u8) (symcalc >> 4); + val[7] = (u8) (symcalc << 4); + } else { + val[5] = (u8) (srate >> 16) & 0x7f; + val[6] = (u8) (srate >> 8); + val[7] = (u8) srate; + } + val[8] &= ~0x20; + if (srate > 8000) + val[8] |= 0x20; + return 0; +} + +static u8 dst_check_sum(u8 * buf, u32 len) +{ + u32 i; + u8 val = 0; + if (!len) + return 0; + for (i = 0; i < len; i++) { + val += buf[i]; + } + return ((~val) + 1); +} + +struct dst_types { + char *mstr; + int offs; + u8 dst_type; + u32 type_flags; +}; + +static struct dst_types dst_tlist[] = { + {"DST-020", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV}, + {"DST-030", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE}, + {"DST-03T", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204}, + {"DST-MOT", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV}, + {"DST-CI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE}, + {"DSTMCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE}, + {"DSTFCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE}, + {"DCTNEW", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE}, + {"DCT-CI", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE | DST_TYPE_HAS_TS204}, + {"DTTDIG", 1, DST_TYPE_IS_TERR, 0} +}; + +/* DCTNEW and DCT-CI are guesses */ + +static void dst_type_flags_print(u32 type_flags) +{ + printk("DST type flags :"); + if (type_flags & DST_TYPE_HAS_NEWTUNE) + printk(" 0x%x newtuner", DST_TYPE_HAS_NEWTUNE); + if (type_flags & DST_TYPE_HAS_TS204) + printk(" 0x%x ts204", DST_TYPE_HAS_TS204); + if (type_flags & DST_TYPE_HAS_SYMDIV) + printk(" 0x%x symdiv", DST_TYPE_HAS_SYMDIV); + printk("\n"); +} + +static int dst_type_print(u8 type) +{ + char *otype; + switch (type) { + case DST_TYPE_IS_SAT: + otype = "satellite"; + break; + case DST_TYPE_IS_TERR: + otype = "terrestrial"; + break; + case DST_TYPE_IS_CABLE: + otype = "cable"; + break; + default: + printk("%s: invalid dst type %d\n", __FUNCTION__, type); + return -EINVAL; + } + printk("DST type : %s\n", otype); + return 0; +} + +static int dst_check_ci(struct dst_state *state) +{ + u8 txbuf[8]; + u8 rxbuf[8]; + int retval; + int i; + struct dst_types *dsp; + u8 use_dst_type; + u32 use_type_flags; + + memset(txbuf, 0, sizeof(txbuf)); + txbuf[1] = 6; + txbuf[7] = dst_check_sum(txbuf, 7); + + dst_i2c_enable(state); + dst_reset8820(state); + retval = write_dst(state, txbuf, 8); + if (retval < 0) { + dst_i2c_disable(state); + dprintk("%s: write not successful, maybe no card?\n", __FUNCTION__); + return retval; + } + msleep(3); + retval = read_dst(state, rxbuf, 1); + dst_i2c_disable(state); + if (retval < 0) { + dprintk("%s: read not successful, maybe no card?\n", __FUNCTION__); + return retval; + } + if (rxbuf[0] != 0xff) { + dprintk("%s: write reply not 0xff, not ci (%02x)\n", __FUNCTION__, rxbuf[0]); + return retval; + } + if (!dst_wait_dst_ready(state)) + return 0; + // dst_i2c_enable(i2c); Dimitri + retval = read_dst(state, rxbuf, 8); + dst_i2c_disable(state); + if (retval < 0) { + dprintk("%s: read not successful\n", __FUNCTION__); + return retval; + } + if (rxbuf[7] != dst_check_sum(rxbuf, 7)) { + dprintk("%s: checksum failure\n", __FUNCTION__); + return retval; + } + rxbuf[7] = '\0'; + for (i = 0, dsp = &dst_tlist[0]; i < sizeof(dst_tlist) / sizeof(dst_tlist[0]); i++, dsp++) { + if (!strncmp(&rxbuf[dsp->offs], dsp->mstr, strlen(dsp->mstr))) { + use_type_flags = dsp->type_flags; + use_dst_type = dsp->dst_type; + printk("%s: recognize %s\n", __FUNCTION__, dsp->mstr); + break; + } + } + if (i >= sizeof(dst_tlist) / sizeof(dst_tlist[0])) { + printk("%s: unable to recognize %s or %s\n", __FUNCTION__, &rxbuf[0], &rxbuf[1]); + printk("%s please email linux-dvb@linuxtv.org with this type in\n", __FUNCTION__); + use_dst_type = DST_TYPE_IS_SAT; + use_type_flags = DST_TYPE_HAS_SYMDIV; + } + dst_type_print(use_dst_type); + + state->type_flags = use_type_flags; + state->dst_type = use_dst_type; + dst_type_flags_print(state->type_flags); + + if (state->type_flags & DST_TYPE_HAS_TS204) { + dst_packsize(state, 204); + } + return 0; +} + +static int dst_command(struct dst_state* state, u8 * data, u8 len) +{ + int retval; + u8 reply; + + dst_i2c_enable(state); + dst_reset8820(state); + retval = write_dst(state, data, len); + if (retval < 0) { + dst_i2c_disable(state); + dprintk("%s: write not successful\n", __FUNCTION__); + return retval; + } + msleep(33); + retval = read_dst(state, &reply, 1); + dst_i2c_disable(state); + if (retval < 0) { + dprintk("%s: read verify not successful\n", __FUNCTION__); + return retval; + } + if (reply != 0xff) { + dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply); + return 0; + } + if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) + return 0; + if (!dst_wait_dst_ready(state)) + return 0; + // dst_i2c_enable(i2c); Per dimitri + retval = read_dst(state, state->rxbuffer, 8); + dst_i2c_disable(state); + if (retval < 0) { + dprintk("%s: read not successful\n", __FUNCTION__); + return 0; + } + if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { + dprintk("%s: checksum failure\n", __FUNCTION__); + return 0; + } + return 0; +} + +static int dst_get_signal(struct dst_state* state) +{ + int retval; + u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; + + if ((state->diseq_flags & ATTEMPT_TUNE) == 0) { + state->decode_lock = state->decode_strength = state->decode_snr = 0; + return 0; + } + if (0 == (state->diseq_flags & HAS_LOCK)) { + state->decode_lock = state->decode_strength = state->decode_snr = 0; + return 0; + } + if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) { + retval = dst_command(state, get_signal, 8); + if (retval < 0) + return retval; + if (state->dst_type == DST_TYPE_IS_SAT) { + state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0; + state->decode_strength = state->rxbuffer[5] << 8; + state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; + } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) { + state->decode_lock = (state->rxbuffer[1]) ? 1 : 0; + state->decode_strength = state->rxbuffer[4] << 8; + state->decode_snr = state->rxbuffer[3] << 8; + } + state->cur_jiff = jiffies; + } + return 0; +} + +static int dst_tone_power_cmd(struct dst_state* state) +{ + u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; + + if (state->dst_type == DST_TYPE_IS_TERR) + return 0; + + if (state->voltage == SEC_VOLTAGE_OFF) + paket[4] = 0; + else + paket[4] = 1; + if (state->tone == SEC_TONE_ON) + paket[2] = state->k22; + else + paket[2] = 0; + paket[7] = dst_check_sum(&paket[0], 7); + dst_command(state, paket, 8); + return 0; +} + +static int dst_get_tuna(struct dst_state* state) +{ + int retval; + if ((state->diseq_flags & ATTEMPT_TUNE) == 0) + return 0; + state->diseq_flags &= ~(HAS_LOCK); + if (!dst_wait_dst_ready(state)) + return 0; + if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { + /* how to get variable length reply ???? */ + retval = read_dst(state, state->rx_tuna, 10); + } else { + retval = read_dst(state, &state->rx_tuna[2], 8); + } + if (retval < 0) { + dprintk("%s: read not successful\n", __FUNCTION__); + return 0; + } + if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { + if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { + dprintk("%s: checksum failure?\n", __FUNCTION__); + return 0; + } + } else { + if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) { + dprintk("%s: checksum failure?\n", __FUNCTION__); + return 0; + } + } + if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0) + return 0; + state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3]; + + state->decode_lock = 1; + /* + dst->decode_n1 = (dst->rx_tuna[4] << 8) + + (dst->rx_tuna[5]); + + dst->decode_n2 = (dst->rx_tuna[8] << 8) + + (dst->rx_tuna[7]); + */ + state->diseq_flags |= HAS_LOCK; + /* dst->cur_jiff = jiffies; */ + return 1; +} + +static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + +static int dst_write_tuna(struct dvb_frontend* fe) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + int retval; + u8 reply; + + dprintk("%s: type_flags 0x%x \n", __FUNCTION__, state->type_flags); + state->decode_freq = 0; + state->decode_lock = state->decode_strength = state->decode_snr = 0; + if (state->dst_type == DST_TYPE_IS_SAT) { + if (!(state->diseq_flags & HAS_POWER)) + dst_set_voltage(fe, SEC_VOLTAGE_13); + } + state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); + dst_i2c_enable(state); + if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { + dst_reset8820(state); + state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9); + retval = write_dst(state, &state->tx_tuna[0], 10); + } else { + state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7); + retval = write_dst(state, &state->tx_tuna[2], 8); + } + if (retval < 0) { + dst_i2c_disable(state); + dprintk("%s: write not successful\n", __FUNCTION__); + return retval; + } + msleep(3); + retval = read_dst(state, &reply, 1); + dst_i2c_disable(state); + if (retval < 0) { + dprintk("%s: read verify not successful\n", __FUNCTION__); + return retval; + } + if (reply != 0xff) { + dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply); + return 0; + } + state->diseq_flags |= ATTEMPT_TUNE; + return dst_get_tuna(state); +} + +/* + * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 + * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 + * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 + * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 + * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 + * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 + * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 + * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec + * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 + * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 + * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 + */ + +static int dst_set_diseqc(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; + + if (state->dst_type == DST_TYPE_IS_TERR) + return 0; + + if (cmd->msg_len == 0 || cmd->msg_len > 4) + return -EINVAL; + memcpy(&paket[3], cmd->msg, cmd->msg_len); + paket[7] = dst_check_sum(&paket[0], 7); + dst_command(state, paket, 8); + return 0; +} + +static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + u8 *val; + int need_cmd; + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + state->voltage = voltage; + + if (state->dst_type == DST_TYPE_IS_TERR) + return 0; + + need_cmd = 0; + val = &state->tx_tuna[0]; + val[8] &= ~0x40; + switch (voltage) { + case SEC_VOLTAGE_13: + if ((state->diseq_flags & HAS_POWER) == 0) + need_cmd = 1; + state->diseq_flags |= HAS_POWER; + break; + case SEC_VOLTAGE_18: + if ((state->diseq_flags & HAS_POWER) == 0) + need_cmd = 1; + state->diseq_flags |= HAS_POWER; + val[8] |= 0x40; + break; + case SEC_VOLTAGE_OFF: + need_cmd = 1; + state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); + break; + default: + return -EINVAL; + } + if (need_cmd) { + dst_tone_power_cmd(state); + } + return 0; +} + +static int dst_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + u8 *val; + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + state->tone = tone; + + if (state->dst_type == DST_TYPE_IS_TERR) + return 0; + + val = &state->tx_tuna[0]; + + val[8] &= ~0x1; + + switch (tone) { + case SEC_TONE_OFF: + break; + case SEC_TONE_ON: + val[8] |= 1; + break; + default: + return -EINVAL; + } + dst_tone_power_cmd(state); + return 0; +} + +static int dst_init(struct dvb_frontend* fe) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + static u8 ini_satci_tuna[] = { 9, 0, 3, 0xb6, 1, 0, 0x73, 0x21, 0, 0 }; + static u8 ini_satfta_tuna[] = { 0, 0, 3, 0xb6, 1, 0x55, 0xbd, 0x50, 0, 0 }; + static u8 ini_tvfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; + static u8 ini_tvci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; + static u8 ini_cabfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; + static u8 ini_cabci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; + state->inversion = INVERSION_ON; + state->voltage = SEC_VOLTAGE_13; + state->tone = SEC_TONE_OFF; + state->symbol_rate = 29473000; + state->fec = FEC_AUTO; + state->diseq_flags = 0; + state->k22 = 0x02; + state->bandwidth = BANDWIDTH_7_MHZ; + state->cur_jiff = jiffies; + if (state->dst_type == DST_TYPE_IS_SAT) { + state->frequency = 950000; + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_satci_tuna : ini_satfta_tuna), sizeof(ini_satfta_tuna)); + } else if (state->dst_type == DST_TYPE_IS_TERR) { + state->frequency = 137000000; + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_tvci_tuna : ini_tvfta_tuna), sizeof(ini_tvfta_tuna)); + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + state->frequency = 51000000; + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_cabci_tuna : ini_cabfta_tuna), sizeof(ini_cabfta_tuna)); + } + + return 0; +} + +static int dst_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + *status = 0; + if (state->diseq_flags & HAS_LOCK) { + dst_get_signal(state); + if (state->decode_lock) + *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; + } + + return 0; +} + +static int dst_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + dst_get_signal(state); + *strength = state->decode_strength; + + return 0; +} + +static int dst_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + dst_get_signal(state); + *snr = state->decode_snr; + + return 0; +} + +static int dst_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + dst_set_freq(state, p->frequency); + dst_set_inversion(state, p->inversion); + if (state->dst_type == DST_TYPE_IS_SAT) { + dst_set_fec(state, p->u.qpsk.fec_inner); + dst_set_symbolrate(state, p->u.qpsk.symbol_rate); + } else if (state->dst_type == DST_TYPE_IS_TERR) { + dst_set_bandwidth(state, p->u.ofdm.bandwidth); + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + dst_set_fec(state, p->u.qam.fec_inner); + dst_set_symbolrate(state, p->u.qam.symbol_rate); + } + dst_write_tuna(fe); + + return 0; +} + +static int dst_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + + p->frequency = state->decode_freq; + p->inversion = state->inversion; + if (state->dst_type == DST_TYPE_IS_SAT) { + p->u.qpsk.symbol_rate = state->symbol_rate; + p->u.qpsk.fec_inner = dst_get_fec(state); + } else if (state->dst_type == DST_TYPE_IS_TERR) { + p->u.ofdm.bandwidth = state->bandwidth; + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + p->u.qam.symbol_rate = state->symbol_rate; + p->u.qam.fec_inner = dst_get_fec(state); + p->u.qam.modulation = QAM_AUTO; + } + + return 0; +} + +static void dst_release(struct dvb_frontend* fe) +{ + struct dst_state* state = (struct dst_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops dst_dvbt_ops; +static struct dvb_frontend_ops dst_dvbs_ops; +static struct dvb_frontend_ops dst_dvbc_ops; + +struct dvb_frontend* dst_attach(const struct dst_config* config, + struct i2c_adapter* i2c, + struct bt878 *bt) +{ + struct dst_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct dst_state*) kmalloc(sizeof(struct dst_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->bt = bt; + + /* check if the demod is there */ + if (dst_check_ci(state) < 0) goto error; + + /* determine settings based on type */ + switch (state->dst_type) { + case DST_TYPE_IS_TERR: + memcpy(&state->ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops)); + break; + case DST_TYPE_IS_CABLE: + memcpy(&state->ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops)); + break; + case DST_TYPE_IS_SAT: + memcpy(&state->ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops)); + break; + default: + printk("dst: unknown frontend type. please report to the LinuxTV.org DVB mailinglist.\n"); + goto error; + } + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops dst_dvbt_ops = { + + .info = { + .name = "DST DVB-T", + .type = FE_OFDM, + .frequency_min = 137000000, + .frequency_max = 858000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = dst_release, + + .init = dst_init, + + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, +}; + +static struct dvb_frontend_ops dst_dvbs_ops = { + + .info = { + .name = "DST DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* . symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK + }, + + .release = dst_release, + + .init = dst_init, + + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, + + .diseqc_send_master_cmd = dst_set_diseqc, + .set_voltage = dst_set_voltage, + .set_tone = dst_set_tone, +}; + +static struct dvb_frontend_ops dst_dvbc_ops = { + + .info = { + .name = "DST DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* . symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO + }, + + .release = dst_release, + + .init = dst_init, + + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, +}; + +MODULE_DESCRIPTION("DST DVB-S/T/C Combo Frontend driver"); +MODULE_AUTHOR("Jamie Honan"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(dst_attach); diff --git a/drivers/media/dvb/bt8xx/dst.h b/drivers/media/dvb/bt8xx/dst.h new file mode 100644 index 000000000000..bcb418c5c121 --- /dev/null +++ b/drivers/media/dvb/bt8xx/dst.h @@ -0,0 +1,40 @@ +/* + 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 diff --git a/drivers/media/dvb/frontends/dst-bt878.h b/drivers/media/dvb/bt8xx/dst_priv.h index c266c9679373..b3d5e6fc6d13 100644 --- a/drivers/media/dvb/frontends/dst-bt878.h +++ b/drivers/media/dvb/bt8xx/dst_priv.h @@ -30,7 +30,7 @@ union dst_gpio_packet { #define DST_IG_READ 2 #define DST_IG_TS 3 -struct bt878 ; +struct bt878; int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp); diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index b35326e2dbb4..d199c596bea8 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -126,7 +126,286 @@ static struct bt878 __init *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci return NULL; } -static int __init dvb_bt8xx_load_card( struct dvb_bt8xx_card *card) + +static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) +{ + static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 }; + static u8 mt352_reset [] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 }; + static u8 mt352_gpp_ctl_cfg [] = { 0x75, 0x33 }; + 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_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + + return 0; +} + +static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +{ + u32 div; + unsigned char bs = 0; + unsigned char cp = 0; + + #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; + + if (params->frequency < 542000000) cp = 0xb4; + else if (params->frequency < 771000000) cp = 0xbc; + else cp = 0xf4; + + if (params->frequency == 0) bs = 0x03; + else if (params->frequency < 443250000) bs = 0x02; + else bs = 0x08; + + pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address + pllbuf[1] = div >> 8; + pllbuf[2] = div & 0xff; + pllbuf[3] = cp; + pllbuf[4] = bs; + + return 0; +} + +static struct mt352_config thomson_dtt7579_config = { + + .demod_address = 0x0f, + .demod_init = thomson_dtt7579_demod_init, + .pll_set = thomson_dtt7579_pll_set, +}; + + + +static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; + u8 cfg, cpump, band_select; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (36000000 + params->frequency + 83333) / 166666; + cfg = 0x88; + + if (params->frequency < 175000000) cpump = 2; + else if (params->frequency < 390000000) cpump = 1; + else if (params->frequency < 470000000) cpump = 2; + else if (params->frequency < 750000000) cpump = 2; + else cpump = 3; + + if (params->frequency < 175000000) band_select = 0x0e; + else if (params->frequency < 470000000) band_select = 0x05; + else band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = cpump | band_select; + + i2c_transfer(card->i2c_adapter, &msg, 1); + return (div * 166666 - 36000000); +} + +static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; + + return request_firmware(fw, name, &bt->bt->dev->dev); +} + +struct sp887x_config microtune_mt7202dtf_config = { + + .demod_address = 0x70, + .pll_set = microtune_mt7202dtf_pll_set, + .request_firmware = microtune_mt7202dtf_request_firmware, +}; + + + +static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) +{ + static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; + 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 }; + static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; + 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)); + udelay(2000); + mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + + return 0; +} + +static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +{ + u32 div; + unsigned char bs = 0; + unsigned char cp = 0; + + #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; + + if (params->frequency < 150000000) cp = 0xB4; + else if (params->frequency < 173000000) cp = 0xBC; + else if (params->frequency < 250000000) cp = 0xB4; + else if (params->frequency < 400000000) cp = 0xBC; + else if (params->frequency < 420000000) cp = 0xF4; + else if (params->frequency < 470000000) cp = 0xFC; + else if (params->frequency < 600000000) cp = 0xBC; + else if (params->frequency < 730000000) cp = 0xF4; + else cp = 0xFC; + + if (params->frequency < 150000000) bs = 0x01; + else if (params->frequency < 173000000) bs = 0x01; + else if (params->frequency < 250000000) bs = 0x02; + else if (params->frequency < 400000000) bs = 0x02; + else if (params->frequency < 420000000) bs = 0x02; + else if (params->frequency < 470000000) bs = 0x02; + else if (params->frequency < 600000000) bs = 0x08; + else if (params->frequency < 730000000) bs = 0x08; + else bs = 0x08; + + pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address + pllbuf[1] = div >> 8; + pllbuf[2] = div & 0xff; + pllbuf[3] = cp; + pllbuf[4] = bs; + + return 0; +} + +static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { + + .demod_address = 0x0f, + .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, + .pll_set = advbt771_samsung_tdtc9251dh0_pll_set, +}; + + +static struct dst_config dst_config = { + + .demod_address = 0x55, +}; + + +static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) }; + + div = (params->frequency + 36166667) / 166667; + + buf[0] = (div >> 8) & 0x7F; + buf[1] = div & 0xFF; + buf[2] = 0x85; + if ((params->frequency >= 47000000) && (params->frequency < 153000000)) + buf[3] = 0x01; + else if ((params->frequency >= 153000000) && (params->frequency < 430000000)) + buf[3] = 0x02; + else if ((params->frequency >= 430000000) && (params->frequency < 824000000)) + buf[3] = 0x0C; + else if ((params->frequency >= 824000000) && (params->frequency < 863000000)) + buf[3] = 0x8C; + else + return -EINVAL; + + i2c_transfer(card->i2c_adapter, &msg, 1); + return 0; +} + +static struct nxt6000_config vp3021_alps_tded4_config = { + + .demod_address = 0x0a, + .clock_inversion = 1, + .pll_set = vp3021_alps_tded4_pll_set, +}; + + +static void frontend_init(struct dvb_bt8xx_card *card, u32 type) +{ + switch(type) { +#ifdef BTTV_DVICO_DVBT_LITE + case BTTV_DVICO_DVBT_LITE: + card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter); + if (card->fe != NULL) { + card->fe->ops->info.frequency_min = 174000000; + card->fe->ops->info.frequency_max = 862000000; + break; + } + break; +#endif + +#ifdef BTTV_TWINHAN_VP3021 + case BTTV_TWINHAN_VP3021: +#else + case BTTV_NEBULA_DIGITV: +#endif + card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter); + if (card->fe != NULL) { + break; + } + break; + + case BTTV_AVDVBT_761: + card->fe = sp887x_attach(µtune_mt7202dtf_config, card->i2c_adapter); + if (card->fe != NULL) { + break; + } + break; + + case BTTV_AVDVBT_771: + card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); + if (card->fe != NULL) { + card->fe->ops->info.frequency_min = 174000000; + card->fe->ops->info.frequency_max = 862000000; + break; + } + break; + + case BTTV_TWINHAN_DST: + card->fe = dst_attach(&dst_config, card->i2c_adapter, card->bt); + if (card->fe != NULL) { + break; + } + break; + } + + if (card->fe == NULL) { + printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + card->bt->dev->vendor, + card->bt->dev->device, + card->bt->dev->subsystem_vendor, + card->bt->dev->subsystem_device); + } else { + 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); + card->fe = NULL; + } + } +} + +static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) { int result; @@ -136,6 +415,7 @@ static int __init dvb_bt8xx_load_card( struct dvb_bt8xx_card *card) return result; } + card->dvb_adapter->priv = card; card->bt->adapter = card->i2c_adapter; @@ -207,6 +487,8 @@ static int __init dvb_bt8xx_load_card( struct dvb_bt8xx_card *card) tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); + frontend_init(card, type); + return 0; } @@ -228,7 +510,7 @@ static int dvb_bt8xx_probe(struct device *dev) switch(sub->core->type) { - case BTTV_PINNACLESAT: +/* case BTTV_PINNACLESAT: UNDEFINED HARDWARE */ #ifdef BTTV_DVICO_DVBT_LITE case BTTV_DVICO_DVBT_LITE: #endif @@ -240,7 +522,11 @@ static int dvb_bt8xx_probe(struct device *dev) * DA_APP(parallel) */ break; +#ifdef BTTV_TWINHAN_VP3021 + case BTTV_TWINHAN_VP3021: +#else case BTTV_NEBULA_DIGITV: +#endif case BTTV_AVDVBT_761: card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5); card->op_sync_orin = 0; @@ -302,7 +588,7 @@ static int dvb_bt8xx_probe(struct device *dev) init_MUTEX(&card->bt->gpio_lock); card->bt->bttv_nr = sub->core->nr; - if ( (ret = dvb_bt8xx_load_card(card)) ) { + if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) { kfree(card); return ret; } @@ -324,6 +610,7 @@ static int dvb_bt8xx_remove(struct device *dev) card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); + if (card->fe) dvb_unregister_frontend(card->fe); dvb_unregister_adapter(card->dvb_adapter); kfree(card); @@ -331,24 +618,6 @@ static int dvb_bt8xx_remove(struct device *dev) return 0; } -static void dvb_bt8xx_i2c_info(struct bttv_sub_device *sub, - struct i2c_client *client, int attach) -{ - struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev); - - if (attach) { - printk("xxx attach\n"); - if (client->driver->command) - client->driver->command(client, FE_REGISTER, - card->dvb_adapter); - } else { - printk("xxx detach\n"); - if (client->driver->command) - client->driver->command(client, FE_UNREGISTER, - card->dvb_adapter); - } -} - static struct bttv_sub_driver driver = { .drv = { .name = "dvb-bt8xx", @@ -360,7 +629,6 @@ static struct bttv_sub_driver driver = { * .resume = dvb_bt8xx_resume, */ }, - .i2c_info = dvb_bt8xx_i2c_info, }; static int __init dvb_bt8xx_init(void) diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 5f9362a424e8..f25a2514455b 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -26,6 +26,10 @@ #include "dvbdev.h" #include "dvb_net.h" #include "bttv.h" +#include "mt352.h" +#include "sp887x.h" +#include "dst.h" +#include "nxt6000.h" struct dvb_bt8xx_card { struct semaphore lock; @@ -44,4 +48,5 @@ struct dvb_bt8xx_card { struct i2c_adapter *i2c_adapter; struct dvb_net dvbnet; + struct dvb_frontend* fe; }; diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig index d89e0025b942..226714085f58 100644 --- a/drivers/media/dvb/cinergyT2/Kconfig +++ b/drivers/media/dvb/cinergyT2/Kconfig @@ -23,8 +23,10 @@ config DVB_CINERGYT2_STREAM_URB_COUNT depends on DVB_CINERGYT2_TUNING default "32" help - USB Request Blocks for Highspeed Stream transfers are queued in a - for the Host Controller. Usually the default value is a safe choice. + USB Request Blocks for Highspeed Stream transfers are scheduled in + a queue for the Host Controller. + + Usually the default value is a safe choice. You may increase this number if you are using this device in a Server Environment with many high-traffic USB Highspeed devices @@ -44,6 +46,21 @@ config DVB_CINERGYT2_STREAM_BUF_SIZE sharing the same USB bus. +config DVB_CINERGYT2_QUERY_INTERVAL + int "Status update interval [milliseconds]" + depends on DVB_CINERGYT2_TUNING + default "250" + help + This is the interval for status readouts from the demodulator. + You may try lower values if you need more responsive signal quality + measurements. + + Please keep in mind that these updates cause traffic on the tuner + control bus and thus may or may not affect receiption sensitivity. + + The default value should be a safe choice for common applications. + + config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE bool "Register the onboard IR Remote Control Receiver as Input Device" depends on DVB_CINERGYT2_TUNING @@ -57,3 +74,12 @@ config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE source code to find out how to add support for other controls. +config DVB_CINERGYT2_RC_QUERY_INTERVAL + int "Infrared Remote Controller update interval [milliseconds]" + depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE + default "100" + help + If you have a very fast-repeating remote control you can try lower + values, for normal consumer receivers the default value should be + a safe choice. + diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index 4e235ad63d5e..fc17763efa0c 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -25,6 +25,7 @@ #include <linux/config.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/version.h> #include <linux/slab.h> #include <linux/usb.h> #include <linux/pci.h> @@ -35,16 +36,28 @@ #include "dvb_demux.h" #include "dvb_net.h" + + + + + + + + #ifdef CONFIG_DVB_CINERGYT2_TUNING #define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT) #define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE) + #define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL) #ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE + #define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL) #define ENABLE_RC (1) #endif #else #define STREAM_URB_COUNT (32) - #define STREAM_BUF_SIZE (512) + #define STREAM_BUF_SIZE (512) /* bytes */ #define ENABLE_RC (1) + #define RC_QUERY_INTERVAL (100) /* milliseconds */ + #define QUERY_INTERVAL (333) /* milliseconds */ #endif #define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver" @@ -54,7 +67,12 @@ module_param_named(debug, debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); #define dprintk(level, args...) \ - do { if ((debug & level)) { printk("%s: %s(): ",__stringify(KBUILD_MODNAME), __FUNCTION__); printk(args); } } while (0) +do { \ + if ((debug & level)) { \ + printk("%s: %s(): ", __stringify(KBUILD_MODNAME), \ + __FUNCTION__); \ + printk(args); } \ +} while (0) enum cinergyt2_ep1_cmd { CINERGYT2_EP1_PID_TABLE_RESET = 0x01, @@ -68,13 +86,34 @@ enum cinergyt2_ep1_cmd { CINERGYT2_EP1_SLEEP_MODE = 0x09 }; +struct dvbt_set_parameters_msg { + uint8_t cmd; + uint32_t freq; + uint8_t bandwidth; + uint16_t tps; + uint8_t flags; +} __attribute__((packed)); + +struct dvbt_get_status_msg { + uint32_t freq; + uint8_t bandwidth; + uint16_t tps; + uint8_t flags; + uint16_t gain; + uint8_t snr; + uint32_t viterbi_error_rate; + uint32_t rs_error_rate; + uint32_t uncorrected_block_count; + uint8_t lock_bits; + uint8_t prev_lock_bits; +} __attribute__((packed)); + static struct dvb_frontend_info cinergyt2_fe_info = { .name = DRIVER_NAME, .type = FE_OFDM, .frequency_min = 174000000, .frequency_max = 862000000, .frequency_stepsize = 166667, - .notifier_delay = 0, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | @@ -93,6 +132,14 @@ struct cinergyt2 { struct dvb_net dvbnet; int streaming; + int sleeping; + + struct dvbt_set_parameters_msg param; + struct dvbt_get_status_msg status; + struct work_struct query_work; + + wait_queue_head_t poll_wq; + int pending_fe_events; void *streambuf; dma_addr_t streambuf_dmahandle; @@ -156,6 +203,45 @@ static const uint32_t rc_keys [] = { CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT }; +static int cinergyt2_command (struct cinergyt2 *cinergyt2, + char *send_buf, int send_buf_len, + char *recv_buf, int recv_buf_len) +{ + int actual_len; + char dummy; + int ret; + + ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1), + send_buf, send_buf_len, &actual_len, HZ); + + if (ret) + dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret); + + if (!recv_buf) + recv_buf = &dummy; + + ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1), + recv_buf, recv_buf_len, &actual_len, HZ); + + if (ret) + dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret); + + return ret ? ret : actual_len; +} + +static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable) +{ + char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; + cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); +} + +static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep) +{ + char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 }; + cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); + cinergyt2->sleeping = sleep; +} + static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs); static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb) @@ -236,9 +322,11 @@ static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2) { int i; + cinergyt2_control_stream_transfer(cinergyt2, 0); + for (i=0; i<STREAM_URB_COUNT; i++) if (cinergyt2->stream_urb[i]) - usb_unlink_urb(cinergyt2->stream_urb[i]); + usb_kill_urb(cinergyt2->stream_urb[i]); } static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2) @@ -253,65 +341,23 @@ static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2) } } + cinergyt2_control_stream_transfer(cinergyt2, 1); return 0; } -static int cinergyt2_command (struct cinergyt2 *cinergyt2, - char *send_buf, int send_buf_len, - char *rec_buf, int rec_buf_len) -{ - int ret; - int actual_len; - char dummy; - - if (down_interruptible(&cinergyt2->sem)) - return -EBUSY; - - ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1), - send_buf, send_buf_len, &actual_len, HZ); - - if (ret) - dprintk(1, "usb_bulk_msg() (send) failed, err %i\n", ret); - - if (!rec_buf) - rec_buf = &dummy; - - ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1), - rec_buf, rec_buf_len, &actual_len, HZ); - - if (ret) - dprintk(1, "usb_bulk_msg() (read) failed, err %i\n", ret); - - up(&cinergyt2->sem); - - return ret ? ret : actual_len; -} - -static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable) -{ - char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); -} - -static void cinergyt2_control_sleep_mode (struct cinergyt2 *cinergyt2, int sleep) -{ - char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); -} - static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *demux = dvbdmxfeed->demux; struct cinergyt2 *cinergyt2 = demux->priv; + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; - if (cinergyt2->streaming == 0) { - if (cinergyt2_start_stream_xfer (cinergyt2) == 0) - cinergyt2_control_stream_transfer (cinergyt2, 1); - } + if (cinergyt2->streaming == 0) + cinergyt2_start_stream_xfer(cinergyt2); cinergyt2->streaming++; - + up(&cinergyt2->sem); return 0; } @@ -320,11 +366,13 @@ static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed) struct dvb_demux *demux = dvbdmxfeed->demux; struct cinergyt2 *cinergyt2 = demux->priv; - if (--cinergyt2->streaming == 0) { - cinergyt2_control_stream_transfer(cinergyt2, 0); + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + + if (--cinergyt2->streaming == 0) cinergyt2_stop_stream_xfer(cinergyt2); - } + up(&cinergyt2->sem); return 0; } @@ -337,11 +385,10 @@ static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed) * * We replace errornous fields by default TPS fields (the ones with value 0). */ - -static uint16_t compute_tps (struct dvb_frontend_parameters *param) +static uint16_t compute_tps (struct dvb_frontend_parameters *p) { + struct dvb_ofdm_parameters *op = &p->u.ofdm; uint16_t tps = 0; - struct dvb_ofdm_parameters *op = ¶m->u.ofdm; switch (op->code_rate_HP) { case FEC_2_3: @@ -435,147 +482,135 @@ static uint16_t compute_tps (struct dvb_frontend_parameters *param) return tps; } -struct dvbt_set_parameters_msg { - uint8_t cmd; - uint32_t freq; - uint8_t bandwidth; - uint16_t tps; - uint8_t flags; -} __attribute__((packed)); - -struct dvbt_get_parameters_msg { - uint32_t freq; - uint8_t bandwidth; - uint16_t tps; - uint8_t flags; - uint16_t gain; - uint8_t snr; - uint32_t viterbi_error_rate; - uint32_t rs_error_rate; - uint32_t uncorrected_block_count; - uint8_t lock_bits; - uint8_t prev_lock_bits; -} __attribute__((packed)); - -static int cinergyt2_fe_open (struct inode *inode, struct file *file) +static int cinergyt2_open (struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; - cinergyt2_control_sleep_mode((struct cinergyt2 *) dvbdev->priv, 0); - return dvb_generic_open(inode, file); + struct cinergyt2 *cinergyt2 = dvbdev->priv; + int err; + + if ((err = dvb_generic_open(inode, file))) + return err; + + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + cinergyt2_sleep(cinergyt2, 0); + schedule_delayed_work(&cinergyt2->query_work, HZ/2); } -static int cinergyt2_fe_release (struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - cinergyt2_control_sleep_mode((struct cinergyt2 *) dvbdev->priv, 1); - return dvb_generic_release (inode, file); + up(&cinergyt2->sem); + return 0; } -static int cinergyt2_fe_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int cinergyt2_release (struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; struct cinergyt2 *cinergyt2 = dvbdev->priv; - int ret = 0; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &cinergyt2_fe_info, sizeof(struct dvb_frontend_info)); - break; - case FE_READ_STATUS: - { - struct dvbt_get_parameters_msg msg; - char cmd = CINERGYT2_EP1_GET_TUNER_STATUS; - fe_status_t *status = arg; - - *status = 0; - - cinergyt2_command(cinergyt2, &cmd, 1, (char *) &msg, sizeof(msg)); - - if (msg.lock_bits & (1 << 6)) - *status |= FE_HAS_LOCK; - if (msg.lock_bits & (1 << 5)) - *status |= FE_HAS_SYNC; - if (msg.lock_bits & (1 << 4)) - *status |= FE_HAS_CARRIER; - if (msg.lock_bits & (1 << 1)) - *status |= FE_HAS_VITERBI; + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; - break; + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + cancel_delayed_work(&cinergyt2->query_work); + flush_scheduled_work(); + cinergyt2_sleep(cinergyt2, 1); } - case FE_READ_BER: - { - struct dvbt_get_parameters_msg msg; - char cmd = CINERGYT2_EP1_GET_TUNER_STATUS; - u32 *ber = (u32 *) arg; - - cinergyt2_command(cinergyt2, &cmd, 1, (char *) &msg, sizeof(msg)); - - *ber = le32_to_cpu(msg.viterbi_error_rate); + up(&cinergyt2->sem); - break; + return dvb_generic_release(inode, file); } - case FE_READ_SIGNAL_STRENGTH: +static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait) { - struct dvbt_get_parameters_msg msg; - char cmd = CINERGYT2_EP1_GET_TUNER_STATUS; - u16 *signal = (u16 *) arg; + struct dvb_device *dvbdev = file->private_data; + struct cinergyt2 *cinergyt2 = dvbdev->priv; + poll_wait(file, &cinergyt2->poll_wq, wait); + return (POLLIN | POLLRDNORM | POLLPRI); + } - cinergyt2_command(cinergyt2, &cmd, 1, (char *) &msg, sizeof(msg)); - *signal = ~(le16_to_cpu(msg.gain)); +static int cinergyt2_ioctl (struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) + { + struct dvb_device *dvbdev = file->private_data; + struct cinergyt2 *cinergyt2 = dvbdev->priv; + struct dvbt_get_status_msg *stat = &cinergyt2->status; + fe_status_t status = 0; - break; - } + switch (cmd) { + case FE_GET_INFO: + return copy_to_user((void*) arg, &cinergyt2_fe_info, + sizeof(struct dvb_frontend_info)); - case FE_READ_SNR: - { - struct dvbt_get_parameters_msg msg; - char cmd = CINERGYT2_EP1_GET_TUNER_STATUS; - u16 *snr = (u16 *) arg; + case FE_READ_STATUS: + if (0xffff - le16_to_cpu(stat->gain) > 30) + status |= FE_HAS_SIGNAL; + if (stat->lock_bits & (1 << 6)) + status |= FE_HAS_LOCK; + if (stat->lock_bits & (1 << 5)) + status |= FE_HAS_SYNC; + if (stat->lock_bits & (1 << 4)) + status |= FE_HAS_CARRIER; + if (stat->lock_bits & (1 << 1)) + status |= FE_HAS_VITERBI; + + return copy_to_user((void *) arg, &status, sizeof(status)); - cinergyt2_command(cinergyt2, &cmd, 1, (char *) &msg, sizeof(msg)); + case FE_READ_BER: + return put_user(le32_to_cpu(stat->viterbi_error_rate), + (__u32 __user *) arg); - *snr = (msg.snr << 8) | msg.snr; + case FE_READ_SIGNAL_STRENGTH: + return put_user(0xffff - le16_to_cpu(stat->gain), + (__u16 __user *) arg); - break; - } + case FE_READ_SNR: + return put_user((stat->snr << 8) | stat->snr, + (__u16 __user *) arg); case FE_READ_UNCORRECTED_BLOCKS: + /* UNC are already converted to host byte order... */ + return put_user(stat->uncorrected_block_count, + (__u32 __user *) arg); + + case FE_SET_FRONTEND: { - struct dvbt_get_parameters_msg msg; - char cmd = CINERGYT2_EP1_GET_TUNER_STATUS; - u32 *ubc = (u32 *) arg; + struct dvbt_set_parameters_msg *param = &cinergyt2->param; + struct dvb_frontend_parameters p; + int err; - cinergyt2_command(cinergyt2, &cmd, 1, (char *) &msg, sizeof(msg)); + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; - *ubc = le32_to_cpu(msg.uncorrected_block_count); + if (copy_from_user(&p, (void *) arg, sizeof(p))) + return -EFAULT; - break; - } - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = (void*) arg; - struct dvb_ofdm_parameters *op = &p->u.ofdm; - struct dvbt_set_parameters_msg msg; + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; - msg.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; - msg.tps = cpu_to_le16(compute_tps(p)); - msg.freq = cpu_to_le32(p->frequency / 1000); - msg.bandwidth = 8 - op->bandwidth - BANDWIDTH_8_MHZ; + param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; + param->tps = cpu_to_le16(compute_tps(&p)); + param->freq = cpu_to_le32(p.frequency / 1000); + param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ; - cinergyt2_command(cinergyt2, (char *) &msg, sizeof(msg), NULL, 0); + stat->lock_bits = 0; + cinergyt2->pending_fe_events++; + wake_up_interruptible(&cinergyt2->poll_wq); - break; + err = cinergyt2_command(cinergyt2, + (char *) param, sizeof(*param), + NULL, 0); + + up(&cinergyt2->sem); + + return (err < 0) ? err : 0; } case FE_GET_FRONTEND: /** - * trivial to implement (see struct dvbt_get_parameters_msg). + * trivial to implement (see struct dvbt_get_status_msg). * equivalent to FE_READ ioctls, but needs * TPS -> linux-dvb parameter set conversion. Feel free * to implement this and send us a patch if you need this @@ -583,33 +618,75 @@ static int cinergyt2_fe_ioctl (struct inode *inode, struct file *file, */ break; + case FE_GET_EVENT: + { + /** + * for now we only fill the status field. the parameters + * are trivial to fill as soon FE_GET_FRONTEND is done. + */ + struct dvb_frontend_event *e = (void *) arg; + if (cinergyt2->pending_fe_events == 0) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + wait_event_interruptible(cinergyt2->poll_wq, + cinergyt2->pending_fe_events > 0); + } + cinergyt2->pending_fe_events = 0; + return cinergyt2_ioctl(inode, file, FE_READ_STATUS, + (unsigned long) &e->status); + } + default: + ; + } + + return -EINVAL; +} + +static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct dvb_device *dvbdev = file->private_data; + struct cinergyt2 *cinergyt2 = dvbdev->priv; + int ret = 0; + + lock_kernel(); + + if (vma->vm_flags & (VM_WRITE | VM_EXEC)) { + ret = -EPERM; + goto bailout; + } + + if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) { ret = -EINVAL; - break; + goto bailout; } + vma->vm_flags |= (VM_IO | VM_DONTCOPY); + vma->vm_file = file; + + ret = remap_pfn_range(vma, vma->vm_start, + virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot) ? -EAGAIN : 0; +bailout: + unlock_kernel(); return ret; } -static -struct file_operations cinergyt2_fe_fops = { +static struct file_operations cinergyt2_fops = { .owner = THIS_MODULE, - .ioctl = dvb_generic_ioctl, - /** - * do we really need this? If so, let's implement it via - * schedule_delayed_work() similiar to the IR code. - */ - /*.poll = cinergyt2_fe_poll, */ - .open = cinergyt2_fe_open, - .release = cinergyt2_fe_release + .ioctl = cinergyt2_ioctl, + .poll = cinergyt2_poll, + .open = cinergyt2_open, + .release = cinergyt2_release, + .mmap = cinergyt2_mmap }; static struct dvb_device cinergyt2_fe_template = { .users = ~0, .writers = 1, .readers = (~0)-1, - .fops = &cinergyt2_fe_fops, - .kernel_ioctl = cinergyt2_fe_ioctl + .fops = &cinergyt2_fops }; #ifdef ENABLE_RC @@ -620,6 +697,9 @@ static void cinergyt2_query_rc (void *data) struct cinergyt2_rc_event rc_events[12]; int n, len; + if (down_interruptible(&cinergyt2->sem)) + return; + len = cinergyt2_command(cinergyt2, buf, sizeof(buf), (char *) rc_events, sizeof(rc_events)); @@ -656,11 +736,45 @@ static void cinergyt2_query_rc (void *data) } } - schedule_delayed_work(&cinergyt2->rc_query_work, (HZ/5)); + schedule_delayed_work(&cinergyt2->rc_query_work, + msecs_to_jiffies(RC_QUERY_INTERVAL)); + + up(&cinergyt2->sem); } #endif -static int cinergyt2_probe (struct usb_interface *intf, const struct usb_device_id *id) +static void cinergyt2_query (void *data) +{ + struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data; + char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + struct dvbt_get_status_msg *s = &cinergyt2->status; + uint8_t lock_bits; + uint32_t unc; + + if (down_interruptible(&cinergyt2->sem)) + return; + + unc = s->uncorrected_block_count; + lock_bits = s->lock_bits; + + cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s)); + + unc += le32_to_cpu(s->uncorrected_block_count); + s->uncorrected_block_count = unc; + + if (lock_bits != s->lock_bits) { + wake_up_interruptible(&cinergyt2->poll_wq); + cinergyt2->pending_fe_events++; + } + + schedule_delayed_work(&cinergyt2->query_work, + msecs_to_jiffies(QUERY_INTERVAL)); + + up(&cinergyt2->sem); +} + +static int cinergyt2_probe (struct usb_interface *intf, + const struct usb_device_id *id) { struct cinergyt2 *cinergyt2; int i, err; @@ -674,8 +788,11 @@ static int cinergyt2_probe (struct usb_interface *intf, const struct usb_device_ usb_set_intfdata (intf, (void *) cinergyt2); init_MUTEX(&cinergyt2->sem); + init_waitqueue_head (&cinergyt2->poll_wq); + INIT_WORK(&cinergyt2->query_work, cinergyt2_query, cinergyt2); cinergyt2->udev = interface_to_usbdev(intf); + cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) { dprintk(1, "unable to allocate stream urbs\n"); @@ -722,10 +839,6 @@ static int cinergyt2_probe (struct usb_interface *intf, const struct usb_device_ cinergyt2->rc_input_dev.keycodesize = sizeof(unsigned char); cinergyt2->rc_input_dev.keycodemax = KEY_MAX; cinergyt2->rc_input_dev.name = DRIVER_NAME " remote control"; - cinergyt2->rc_input_dev.id.bustype = BUS_USB; - cinergyt2->rc_input_dev.id.vendor = 0x0001; - cinergyt2->rc_input_dev.id.product = 0x0001; - cinergyt2->rc_input_dev.id.version = 0x0100; for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3) set_bit(rc_keys[i+2], cinergyt2->rc_input_dev.keybit); @@ -735,9 +848,8 @@ static int cinergyt2_probe (struct usb_interface *intf, const struct usb_device_ cinergyt2->rc_input_event = KEY_MAX; INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2); - schedule_delayed_work(&cinergyt2->rc_query_work, HZ); + schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); #endif - return 0; bailout: @@ -753,6 +865,9 @@ static void cinergyt2_disconnect (struct usb_interface *intf) { struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); + if (down_interruptible(&cinergyt2->sem)) + return; + #ifdef ENABLE_RC cancel_delayed_work(&cinergyt2->rc_query_work); flush_scheduled_work(); @@ -768,9 +883,56 @@ static void cinergyt2_disconnect (struct usb_interface *intf) dvb_unregister_adapter(cinergyt2->adapter); cinergyt2_free_stream_urbs(cinergyt2); + up(&cinergyt2->sem); kfree(cinergyt2); } +static int cinergyt2_suspend (struct usb_interface *intf, u32 state) +{ + struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); + + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + + if (state > 0) { /* state 0 seems to mean DEVICE_PM_ON */ + struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); +#ifdef ENABLE_RC + cancel_delayed_work(&cinergyt2->rc_query_work); +#endif + cancel_delayed_work(&cinergyt2->query_work); + if (cinergyt2->streaming) + cinergyt2_stop_stream_xfer(cinergyt2); + flush_scheduled_work(); + cinergyt2_sleep(cinergyt2, 1); + } + + up(&cinergyt2->sem); + return 0; +} + +static int cinergyt2_resume (struct usb_interface *intf) +{ + struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); + struct dvbt_set_parameters_msg *param = &cinergyt2->param; + + if (down_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + + if (!cinergyt2->sleeping) { + cinergyt2_sleep(cinergyt2, 0); + cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0); + if (cinergyt2->streaming) + cinergyt2_start_stream_xfer(cinergyt2); + schedule_delayed_work(&cinergyt2->query_work, HZ/2); + } + +#ifdef ENABLE_RC + schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); +#endif + up(&cinergyt2->sem); + return 0; +} + static const struct usb_device_id cinergyt2_table [] __devinitdata = { { USB_DEVICE(0x0ccd, 0x0038) }, { 0 } @@ -780,9 +942,11 @@ MODULE_DEVICE_TABLE(usb, cinergyt2_table); static struct usb_driver cinergyt2_driver = { .owner = THIS_MODULE, - .name = "cinergyt2", + .name = "cinergyT2", .probe = cinergyt2_probe, .disconnect = cinergyt2_disconnect, + .suspend = cinergyt2_suspend, + .resume = cinergyt2_resume, .id_table = cinergyt2_table }; @@ -790,12 +954,10 @@ static int __init cinergyt2_init (void) { int err; - if ((err = usb_register(&cinergyt2_driver)) < 0) { + if ((err = usb_register(&cinergyt2_driver)) < 0) dprintk(1, "usb_register() failed! (err %i)\n", err); - return err; - } - return 0; + return err; } static void __exit cinergyt2_exit (void) diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig index 9db05f063f34..7f73bbb9ad49 100644 --- a/drivers/media/dvb/dibusb/Kconfig +++ b/drivers/media/dvb/dibusb/Kconfig @@ -1,9 +1,11 @@ config DVB_DIBUSB - tristate "DiBcom/Twinhan/KWorld/Hama/Artec/Compro USB DVB-T devices" + tristate "DiBcom USB DVB-T devices (see help for device list)" depends on DVB_CORE && USB select FW_LOADER + select DVB_DIB3000MB + select DVB_DIB3000MC help - Support for USB 1.1 DVB-T devices based on a reference design made by + Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by DiBcom (http://www.dibcom.fr). Devices supported by this driver: @@ -15,19 +17,41 @@ config DVB_DIBUSB DiBcom reference device (non-public) Ultima Electronic/Artec T1 USB TVBOX Compro Videomate DVB-U2000 - DVB-T USB + Grandtec DVB-T USB + Avermedia AverTV DVBT USB + Yakumo DVB-T mobile USB2.0 The VP7041 seems to be identical to "CTS Portable" (Chinese Television System). These devices can be understood as budget ones, they "only" deliver - the MPEG data. + (a part of) the MPEG2 transport stream. - Currently all known copies of the DiBcom reference design have the DiBcom 3000-MB - frontend onboard. Please enable and load this one manually in order to use this - device. - - A firmware is needed to use the device. See Documentation/dvb/README.dibusb + A firmware is needed to get the device working. See Documentation/dvb/README.dibusb details. Say Y if you own such a device and want to use it. You should build it as a module. + +config DVB_DIBUSB_MISDESIGNED_DEVICES + bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs" + depends on DVB_DIBUSB + help + Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their + USB1.1/USB2.0 devices. + So comes that they identify with the default Vendor and Product ID of the Cypress + CY7C64613 (AN2235) or Cypress FX2. + + Affected device IDs: + 0x0574:0x2235 (Artec T1 USB1.1, cold) + 0x04b4:0x8613 (Artec T1 USB2.0, cold) + 0x0574:0x1002 (Artec T1 USB2.0, warm) + + Say Y if your device has one of the mentioned IDs. + +config DVB_DIBCOM_DEBUG + bool "Enable extended debug support for DiBcom USB device" + depends on DVB_DIBUSB + help + Say Y if you want to enable debuging. See modinfo dvb-dibusb for + debug levels. diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile index 72cd6a241ca7..ab990cd3bfbc 100644 --- a/drivers/media/dvb/dibusb/Makefile +++ b/drivers/media/dvb/dibusb/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.c b/drivers/media/dvb/dibusb/dvb-dibusb.c index 6b9438467dd2..e4237a94bbd2 100644 --- a/drivers/media/dvb/dibusb/dvb-dibusb.c +++ b/drivers/media/dvb/dibusb/dvb-dibusb.c @@ -7,9 +7,9 @@ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) * * based on GPL code from DiBcom, which has - * * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) * + * Remote control code added by David Matthews (dm@prolingua.co.uk) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,9 +18,7 @@ * Acknowledgements * * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver - * sources, on which this driver (and the dib3000mb frontend) are based. - * - * + * sources, on which this driver (and the dib3000mb/mc/p frontends) are based. * * see Documentation/dvb/README.dibusb for more information */ @@ -32,19 +30,21 @@ #include <linux/version.h> #include <linux/moduleparam.h> #include <linux/pci.h> +#include <linux/input.h> #include "dmxdev.h" #include "dvb_demux.h" #include "dvb_filter.h" #include "dvb_net.h" #include "dvb_frontend.h" +#include "dib3000.h" #include "dvb-dibusb.h" -/* debug */ +/* debug */ #ifdef CONFIG_DVB_DIBCOM_DEBUG -#define dprintk_new(level,args...) \ +#define dprintk(level,args...) \ do { if ((debug & level)) { printk(args); } } while (0) #define debug_dump(b,l) if (debug) {\ @@ -55,20 +55,25 @@ static int debug; module_param(debug, int, 0x644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err (|-able))."); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))."); #else -#define dprintk_new(args...) +#define dprintk(args...) #define debug_dump(b,l) #endif -#define deb_info(args...) dprintk_new(0x01,args) -#define deb_xfer(args...) dprintk_new(0x02,args) -#define deb_alot(args...) dprintk_new(0x04,args) -#define deb_ts(args...) dprintk_new(0x08,args) -#define deb_err(args...) dprintk_new(0x10,args) +#define deb_info(args...) dprintk(0x01,args) +#define deb_xfer(args...) dprintk(0x02,args) +#define deb_alot(args...) dprintk(0x04,args) +#define deb_ts(args...) dprintk(0x08,args) +#define deb_err(args...) dprintk(0x10,args) +#define deb_rc(args...) dprintk(0x20,args) + +static int pid_parse; +module_param(pid_parse, int, 0x644); +MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0"); /* Version information */ -#define DRIVER_VERSION "0.0" +#define DRIVER_VERSION "0.1" #define DRIVER_DESC "Driver for DiBcom based USB Budget DVB-T device" #define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" @@ -83,25 +88,28 @@ static int dibusb_readwrite_usb(struct usb_dibusb *dib, if ((ret = down_interruptible(&dib->usb_sem))) return ret; - if (dib->streaming && wbuf[0] == DIBUSB_REQ_I2C_WRITE) - deb_err("BUG: writing to i2c, while TS-streaming destroys the stream. What" - " did you do ? Please enable debugging and send the syslog to the author. (%x reg: %x %x)", - wbuf[0],wbuf[2],wbuf[3]); + if (dib->feedcount && + wbuf[0] == DIBUSB_REQ_I2C_WRITE && + dib->dibdev->parm->type == DIBUSB1_1) + deb_err("BUG: writing to i2c, while TS-streaming destroys the stream." + "(%x reg: %x %x)\n", wbuf[0],wbuf[2],wbuf[3]); debug_dump(wbuf,wlen); - ret = usb_bulk_msg(dib->udev,COMMAND_PIPE, - wbuf,wlen,&actlen,DIBUSB_I2C_TIMEOUT); + ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev, + dib->dibdev->parm->cmd_pipe), wbuf,wlen,&actlen, + DIBUSB_I2C_TIMEOUT); if (ret) err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); else ret = actlen != wlen ? -1 : 0; - /* an answer is expected */ + /* an answer is expected, and no error before */ if (!ret && rbuf && rlen) { - ret = usb_bulk_msg(dib->udev,RESULT_PIPE,rbuf,rlen, - &actlen,DIBUSB_I2C_TIMEOUT); + ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev, + dib->dibdev->parm->result_pipe),rbuf,rlen,&actlen, + DIBUSB_I2C_TIMEOUT); if (ret) err("recv bulk message failed: %d",ret); @@ -115,11 +123,6 @@ static int dibusb_readwrite_usb(struct usb_dibusb *dib, return ret; } -static int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) -{ - return dibusb_readwrite_usb(dib,buf,len,NULL,0); -} - static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { @@ -146,97 +149,21 @@ static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, /* * DVB stuff */ - -static struct dibusb_pid * dibusb_get_free_pid(struct usb_dibusb *dib) -{ - int i; - unsigned long flags; - struct dibusb_pid *dpid = NULL; - - spin_lock_irqsave(&dib->pid_list_lock,flags); - for (i=0; i < DIBUSB_MAX_PIDS; i++) - if (!dib->pid_list[i].active) { - dpid = dib->pid_list + i; - dpid->active = 1; - break; - } - spin_unlock_irqrestore(&dib->pid_list_lock,flags); - return dpid; -} - -static int dibusb_start_xfer(struct usb_dibusb *dib) -{ - u8 b[4] = { - (DIB3000MB_REG_FIFO >> 8) & 0xff, - (DIB3000MB_REG_FIFO) & 0xff, - (DIB3000MB_FIFO_ACTIVATE >> 8) & 0xff, - (DIB3000MB_FIFO_ACTIVATE) & 0xff - }; - dib->streaming = 1; - deb_ts("start streaming\n"); - return dibusb_i2c_msg(dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); -} - -static int dibusb_stop_xfer(struct usb_dibusb *dib) -{ - u8 b[4] = { - (DIB3000MB_REG_FIFO >> 8) & 0xff, - (DIB3000MB_REG_FIFO) & 0xff, - (DIB3000MB_FIFO_INHIBIT >> 8) & 0xff, - (DIB3000MB_FIFO_INHIBIT) & 0xff - }; - dib->streaming = 0; - deb_ts("stop streaming\n"); - return dibusb_i2c_msg(dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); -} - -static int dibusb_set_pid(struct dibusb_pid *dpid) -{ - struct usb_dibusb *dib = dpid->dib; - u16 pid = dpid->pid | (dpid->active ? DIB3000MB_ACTIVATE_FILTERING : 0); - u8 b[4] = { - (dpid->reg >> 8) & 0xff, - (dpid->reg) & 0xff, - (pid >> 8) & 0xff, - (pid) & 0xff - }; - int ret; - - /* firmware bug, i2c write during mpeg transfer */ - if (dib->feedcount) { - deb_info("stop streaming\n"); - ret = dibusb_stop_xfer(dib); - } - - if (dpid->active) - dib->feedcount++; - else - dib->feedcount--; - - ret = dibusb_i2c_msg(dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); - - if (ret == 0 && dib->feedcount) { - deb_info("start streaming\n"); - ret = dibusb_start_xfer(dib); - } - return ret; -} - static void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs) { struct usb_dibusb *dib = urb->context; - deb_xfer("urb complete feedcount: %d, status: %d\n",dib->feedcount,urb->status); + deb_ts("urb complete feedcount: %d, status: %d\n",dib->feedcount,urb->status); if (dib->feedcount > 0 && urb->status == 0) { - deb_xfer("URB return len: %d\n",urb->actual_length); + deb_ts("URB return len: %d\n",urb->actual_length); if (urb->actual_length % 188) - deb_xfer("TS Packets: %d, %d\n", urb->actual_length/188,urb->actual_length % 188); + deb_ts("TS Packets: %d, %d\n", urb->actual_length/188,urb->actual_length % 188); /* Francois recommends to drop not full-filled packets, even if they may * contain valid TS packets */ - if (urb->actual_length == DIBUSB_TS_DEFAULT_SIZE && dib->dvb_is_ready) + if (urb->actual_length == dib->dibdev->parm->default_size && dib->dvb_is_ready) dvb_dmx_swfilter_packets(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length/188); else deb_ts("URB dropped because of the " @@ -247,78 +174,198 @@ static void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs) usb_submit_urb(urb,GFP_KERNEL); } - -static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +static int dibusb_ctrl_feed(struct usb_dibusb *dib, int pid, int onoff) { -// struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct usb_dibusb *dib = dvbdmxfeed->demux->priv; - struct dibusb_pid *dpid; + if (dib->dibdev->parm->firmware_bug && dib->feedcount) { + deb_ts("stop feeding\n"); + if (dib->xfer_ops.fifo_ctrl != NULL) { + if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) { + err("error while inhibiting fifo."); + return -ENODEV; + } + } else { + err("fifo_ctrl is not set."); + return -ENODEV; + } + } - deb_ts("pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + dib->feedcount += onoff ? 1 : -1; - if ((dpid = dibusb_get_free_pid(dib)) == NULL) { + if (dib->pid_parse) { + if (dib->xfer_ops.pid_ctrl != NULL) { + if (dib->xfer_ops.pid_ctrl(dib->fe,pid,onoff) < 0) { err("no free pid in list."); return -ENODEV; } - dvbdmxfeed->priv = dpid; - dpid->pid = dvbdmxfeed->pid; + } else { + err("no pid ctrl callback."); + return -ENODEV; + } + } + /* + * start the feed, either if there is the firmware bug or + * if this was the first pid to set. + */ + if (dib->dibdev->parm->firmware_bug || dib->feedcount == onoff) { - dibusb_set_pid(dpid); + deb_ts("controlling pid parser\n"); + if (dib->xfer_ops.pid_parse != NULL) { + if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) { + err("could not handle pid_parser"); + } + } - return 0; + deb_ts("start feeding\n"); + if (dib->xfer_ops.fifo_ctrl != NULL) { + if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) { + err("error while enabling fifo."); + return -ENODEV; + } + } else { + err("fifo_ctrl is not set."); + return -ENODEV; } - -static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dibusb_pid *dpid = (struct dibusb_pid *) dvbdmxfeed->priv; - - deb_ts("stopfeed pid: 0x%04x, feedtype: %d\n",dvbdmxfeed->pid, dvbdmxfeed->type); - - if (dpid == NULL) - err("channel in dmxfeed->priv was NULL"); - else { - dpid->active = 0; - dpid->pid = 0; - dibusb_set_pid(dpid); } - return 0; } -/* - * firmware transfers - */ +static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct usb_dibusb *dib = dvbdmxfeed->demux->priv; + deb_ts("pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + dvbdmxfeed->priv = dib; + return dibusb_ctrl_feed(dib,dvbdmxfeed->pid,1); +} -/* - * do not use this, just a workaround for a bug, - * which will hopefully never occur :). - */ -static int dibusb_interrupt_read_loop(struct usb_dibusb *dib) +static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { - u8 b[1] = { DIBUSB_REQ_INTR_READ }; - return dibusb_write_usb(dib,b,1); + struct usb_dibusb *dib = (struct usb_dibusb *) dvbdmxfeed->priv; + if (dib == NULL) { + err("dib in dmxfeed->priv was NULL"); + return -EINVAL; } + deb_ts("dvbdmxfeed pid: 0x%04x, feedtype: %d\n", + dvbdmxfeed->pid, dvbdmxfeed->type); + return dibusb_ctrl_feed(dib,dvbdmxfeed->pid,0); +} + +/* Table to map raw key codes to key events. This should not be hard-wired + into the kernel. */ +static const struct { u8 c0, c1, c2; uint32_t key; } rc_keys [] = +{ + /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ + { 0x00, 0xff, 0x16, KEY_POWER }, + { 0x00, 0xff, 0x10, KEY_MUTE }, + { 0x00, 0xff, 0x03, KEY_1 }, + { 0x00, 0xff, 0x01, KEY_2 }, + { 0x00, 0xff, 0x06, KEY_3 }, + { 0x00, 0xff, 0x09, KEY_4 }, + { 0x00, 0xff, 0x1d, KEY_5 }, + { 0x00, 0xff, 0x1f, KEY_6 }, + { 0x00, 0xff, 0x0d, KEY_7 }, + { 0x00, 0xff, 0x19, KEY_8 }, + { 0x00, 0xff, 0x1b, KEY_9 }, + { 0x00, 0xff, 0x15, KEY_0 }, + { 0x00, 0xff, 0x05, KEY_CHANNELUP }, + { 0x00, 0xff, 0x02, KEY_CHANNELDOWN }, + { 0x00, 0xff, 0x1e, KEY_VOLUMEUP }, + { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN }, + { 0x00, 0xff, 0x11, KEY_RECORD }, + { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x00, 0xff, 0x14, KEY_PLAY }, + { 0x00, 0xff, 0x1a, KEY_STOP }, + { 0x00, 0xff, 0x40, KEY_REWIND }, + { 0x00, 0xff, 0x12, KEY_FASTFORWARD }, + { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x00, 0xff, 0x4c, KEY_PAUSE }, + { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */ + { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ + { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */ + { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */ + { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */ + { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */ + { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */ + { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */ + /* Key codes for the KWorld/ADSTech/JetWay remote. */ + { 0x86, 0x6b, 0x12, KEY_POWER }, + { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */ + { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */ + { 0x86, 0x6b, 0x0b, KEY_EPG }, + { 0x86, 0x6b, 0x10, KEY_MUTE }, + { 0x86, 0x6b, 0x01, KEY_1 }, + { 0x86, 0x6b, 0x02, KEY_2 }, + { 0x86, 0x6b, 0x03, KEY_3 }, + { 0x86, 0x6b, 0x04, KEY_4 }, + { 0x86, 0x6b, 0x05, KEY_5 }, + { 0x86, 0x6b, 0x06, KEY_6 }, + { 0x86, 0x6b, 0x07, KEY_7 }, + { 0x86, 0x6b, 0x08, KEY_8 }, + { 0x86, 0x6b, 0x09, KEY_9 }, + { 0x86, 0x6b, 0x0a, KEY_0 }, + { 0x86, 0x6b, 0x18, KEY_ZOOM }, + { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */ + { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */ + { 0x86, 0x6b, 0x00, KEY_UNDO }, + { 0x86, 0x6b, 0x1d, KEY_RECORD }, + { 0x86, 0x6b, 0x0d, KEY_STOP }, + { 0x86, 0x6b, 0x0e, KEY_PAUSE }, + { 0x86, 0x6b, 0x16, KEY_PLAY }, + { 0x86, 0x6b, 0x11, KEY_BACK }, + { 0x86, 0x6b, 0x19, KEY_FORWARD }, + { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */ + { 0x86, 0x6b, 0x15, KEY_ESC }, + { 0x86, 0x6b, 0x1a, KEY_UP }, + { 0x86, 0x6b, 0x1e, KEY_DOWN }, + { 0x86, 0x6b, 0x1f, KEY_LEFT }, + { 0x86, 0x6b, 0x1b, KEY_RIGHT }, +}; /* - * TODO: a tasklet should run with a delay of 1/10 second - * and feed an appropriate event device ? - * NEC protocol is used for remote controlls + * Read the remote control and feed the appropriate event. + * NEC protocol is used for remote controls */ static int dibusb_read_remote_control(struct usb_dibusb *dib) { u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5]; int ret; + int i; if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5))) return ret; - - switch (rb[0]) { case DIBUSB_RC_NEC_KEY_PRESSED: + /* rb[1-3] is the actual key, rb[4] is a checksum */ + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + rb[1], rb[2], rb[3], rb[4]); + + if ((0xff - rb[3]) != rb[4]) { + deb_rc("remote control checksum failed.\n"); + break; + } + /* See if we can match the raw key code. */ + for (i = 0; i < sizeof(rc_keys)/sizeof(rc_keys[0]); i++) { + if (rc_keys[i].c0 == rb[1] && + rc_keys[i].c1 == rb[2] && + rc_keys[i].c2 == rb[3]) { + dib->rc_input_event = rc_keys[i].key; + deb_rc("Translated key 0x%04x\n", dib->rc_input_event); + /* Signal down and up events for this key. */ + input_report_key(&dib->rc_input_dev, dib->rc_input_event, 1); + input_report_key(&dib->rc_input_dev, dib->rc_input_event, 0); + input_sync(&dib->rc_input_dev); + break; + } + } + break; + case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */ break; - case DIBUSB_RC_NEC_EMPTY: case DIBUSB_RC_NEC_KEY_REPEATED: + /* rb[1]..rb[4] are always zero.*/ + /* Repeats often seem to occur so for the moment just ignore this. */ + deb_rc("Key repeat\n"); + break; default: break; } @@ -326,18 +373,38 @@ static int dibusb_read_remote_control(struct usb_dibusb *dib) return 0; } +#define RC_QUERY_INTERVAL (100) /* milliseconds */ + +/* Remote-control poll function - called every RC_QUERY_INTERVAL ms to see + whether the remote control has received anything. */ +static void dibusb_query_rc (void *data) +{ + struct usb_dibusb *dib = (struct usb_dibusb *) data; + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + dibusb_read_remote_control(dib); + schedule_delayed_work(&dib->rc_query_work, + msecs_to_jiffies(RC_QUERY_INTERVAL)); +} + /* - * ioctl for the firmware + * Cypress controls */ -static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) -{ - u8 b[34]; - int size = plen > 32 ? 32 : plen; - b[0] = DIBUSB_REQ_SET_IOCTL; - b[1] = cmd; - memcpy(&b[2],param,size); - return dibusb_write_usb(dib,b,2+size); +#if 0 +/* + * #if 0'ing the following functions as they are not in use _now_, + * but probably will be sometime. + */ + +/* + * do not use this, just a workaround for a bug, + * which will hopefully never occur :). + */ +static int dibusb_interrupt_read_loop(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_INTR_READ }; + return dibusb_write_usb(dib,b,1); } /* @@ -349,6 +416,26 @@ static int dibusb_hw_sleep(struct usb_dibusb *dib) return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); } +#endif +static int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) +{ + return dibusb_readwrite_usb(dib,buf,len,NULL,0); +} + +/* + * ioctl for the firmware + */ +static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) +{ + u8 b[34]; + int size = plen > 32 ? 32 : plen; + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = cmd; + memcpy(&b[2],param,size); + + return dibusb_write_usb(dib,b,2+size); +} + static int dibusb_hw_wakeup(struct usb_dibusb *dib) { u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP }; @@ -387,19 +474,110 @@ static u32 dibusb_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C; } -static int dibusb_i2c_client_register (struct i2c_client *i2c) +static int thomson_cable_eu_pll_set(struct dvb_frontend* fe, struct + dvb_frontend_parameters* params); + +static struct dib3000_config thomson_cable_eu_config = { + .demod_address = 0x10, + .pll_addr = 194, + .pll_set = thomson_cable_eu_pll_set, +}; + +static int thomson_cable_eu_pll_set(struct dvb_frontend* fe, struct + dvb_frontend_parameters* params) { - struct usb_dibusb *dib = i2c_get_adapdata(i2c->adapter); - if (i2c->driver->command) - return i2c->driver->command(i2c,FE_REGISTER,dib->adapter); + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + u8 buf[4]; + struct i2c_msg msg = { + .addr = thomson_cable_eu_config.pll_addr, + .flags = 0, + .buf = buf, + .len = sizeof(buf) + }; + u32 tfreq = (params->frequency + 36125000) / 62500; + int vu,p0,p1,p2; + + if (params->frequency > 403250000) + vu = 1, p2 = 1, p1 = 0, p0 = 1; + else if (params->frequency > 115750000) + vu = 0, p2 = 1, p1 = 1, p0 = 0; + else if (params->frequency > 44250000) + vu = 0, p2 = 0, p1 = 1, p0 = 1; + else + return -EINVAL; + + buf[0] = (tfreq >> 8) & 0x7f; + buf[1] = tfreq & 0xff; + buf[2] = 0x8e; + buf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0; + + if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1) + return -EIO; + + msleep(1); return 0; } -static int dibusb_i2c_client_unregister (struct i2c_client *i2c) +static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend *fe, struct + dvb_frontend_parameters *params); + +static struct dib3000_config panasonic_cofdm_env57h1xd5 = { + .demod_address = 0x18, + .pll_addr = 192, + .pll_set = panasonic_cofdm_env57h1xd5_pll_set, +}; + +static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend *fe, struct + dvb_frontend_parameters *params) { - struct usb_dibusb *dib = i2c_get_adapdata(i2c->adapter); - if (i2c->driver->command) - return i2c->driver->command(i2c,FE_UNREGISTER,dib->adapter); + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + u8 buf[4]; + u32 freq = params->frequency; + u32 tfreq = (freq + 36125000) / 1000000 * 6 + 1; + u8 TA, T210, R210, ctrl1, cp210, p4321; + struct i2c_msg msg = { + .addr = panasonic_cofdm_env57h1xd5.pll_addr, + .flags = 0, + .buf = buf, + .len = sizeof(buf) + }; + + if (freq > 858000000) { + err("frequency cannot be larger than 858 MHz."); + return -EINVAL; + } + + // contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0 + TA = 1; + T210 = 0; + R210 = 0x2; + ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210; + +// ******** CHARGE PUMP CONFIG vs RF FREQUENCIES ***************** + if (freq < 470000000) + cp210 = 2; // VHF Low and High band ch E12 to E4 to E12 + else if (freq < 526000000) + cp210 = 4; // UHF band Ch E21 to E27 + else // if (freq < 862000000) + cp210 = 5; // UHF band ch E28 to E69 + +//********************* BW select ******************************* + if (freq < 153000000) + p4321 = 1; // BW selected for VHF low + else if (freq < 470000000) + p4321 = 2; // BW selected for VHF high E5 to E12 + else // if (freq < 862000000) + p4321 = 4; // BW selection for UHF E21 to E69 + + buf[0] = (tfreq >> 8) & 0xff; + buf[1] = (tfreq >> 0) & 0xff; + buf[2] = 0xff & ctrl1; + buf[3] = (cp210 << 5) | (p4321); + + if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1) + return -EIO; + + msleep(1); return 0; } @@ -410,6 +588,32 @@ static struct i2c_algorithm dibusb_algo = { .functionality = dibusb_i2c_func, }; +static void frontend_init(struct usb_dibusb* dib) +{ + switch (dib->dibdev->parm->type) { + case DIBUSB1_1: + case DIBUSB1_1_AN2235: + dib->fe = dib3000mb_attach(&thomson_cable_eu_config, &dib->i2c_adap,&dib->xfer_ops); + break; + case DIBUSB2_0: + dib->fe = dib3000mc_attach(&panasonic_cofdm_env57h1xd5,&dib->i2c_adap, &dib->xfer_ops); + break; + } + + if (dib->fe == NULL) { + printk("dvb-dibusb: A frontend driver was not found for device %04x/%04x\n", + dib->udev->descriptor.idVendor, + dib->udev->descriptor.idProduct); + } else { + if (dvb_register_frontend(dib->adapter, dib->fe)) { + printk("dvb-dibusb: Frontend registration failed!\n"); + if (dib->fe->ops->release) + dib->fe->ops->release(dib->fe); + dib->fe = NULL; + } + } +} + static int dibusb_dvb_init(struct usb_dibusb *dib) { int ret; @@ -423,6 +627,7 @@ static int dibusb_dvb_init(struct usb_dibusb *dib) deb_info("dvb_register_adapter failed: error %d", ret); goto err; } + dib->adapter->priv = dib; strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE); #ifdef I2C_ADAP_CLASS_TV_DIGITAL @@ -433,8 +638,6 @@ static int dibusb_dvb_init(struct usb_dibusb *dib) dib->i2c_adap.algo = &dibusb_algo; dib->i2c_adap.algo_data = NULL; dib->i2c_adap.id = I2C_ALGO_BIT; - dib->i2c_adap.client_register = dibusb_i2c_client_register, - dib->i2c_adap.client_unregister = dibusb_i2c_client_unregister, i2c_set_adapdata(&dib->i2c_adap, dib); @@ -446,8 +649,8 @@ static int dibusb_dvb_init(struct usb_dibusb *dib) dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; dib->demux.priv = (void *)dib; - dib->demux.filternum = DIBUSB_MAX_PIDS; - dib->demux.feednum = DIBUSB_MAX_PIDS; + /* get pidcount from demod */ + dib->demux.feednum = dib->demux.filternum = 16; dib->demux.start_feed = dibusb_start_feed; dib->demux.stop_feed = dibusb_stop_feed; dib->demux.write_to_decoder = NULL; @@ -466,6 +669,11 @@ static int dibusb_dvb_init(struct usb_dibusb *dib) dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx); + frontend_init(dib); + + /* Start the remote-control polling. */ + schedule_delayed_work(&dib->rc_query_work, msecs_to_jiffies(RC_QUERY_INTERVAL)); + goto success; err_dmx_dev: dvb_dmx_release(&dib->demux); @@ -482,12 +690,17 @@ success: static int dibusb_dvb_exit(struct usb_dibusb *dib) { + cancel_delayed_work(&dib->rc_query_work); + flush_scheduled_work(); + input_unregister_device(&dib->rc_input_dev); + dib->dvb_is_ready = 0; deb_info("unregistering DVB part\n"); dvb_net_release(&dib->dvb_net); dib->demux.dmx.close(&dib->demux.dmx); dvb_dmxdev_release(&dib->dmxdev); dvb_dmx_release(&dib->demux); + if (dib->fe != NULL) dvb_unregister_frontend(dib->fe); i2c_del_adapter(&dib->i2c_adap); dvb_unregister_adapter(dib->adapter); @@ -497,21 +710,36 @@ static int dibusb_dvb_exit(struct usb_dibusb *dib) static int dibusb_exit(struct usb_dibusb *dib) { int i; - for (i = 0; i < DIBUSB_TS_NUM_URBS; i++) - if (dib->buf_urb[i] != NULL) { + if (dib->urb_list != NULL) { + for (i = 0; i < dib->dibdev->parm->num_urbs; i++) { + if (dib->urb_list[i] != NULL) { deb_info("killing URB no. %d.\n",i); - usb_kill_urb(dib->buf_urb[i]); // TODO kernel version ifdef for unlink_urb + + /* stop the URBs */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7) + usb_unlink_urb(dib->urb_list[i]); +#else + usb_kill_urb(dib->urb_list[i]); +#endif deb_info("freeing URB no. %d.\n",i); - usb_free_urb(dib->buf_urb[i]); + /* free the URBs */ + usb_free_urb(dib->urb_list[i]); + } + } + /* free the urb array */ + kfree(dib->urb_list); } - pci_free_consistent(NULL,DIBUSB_TS_BUFFER_SIZE,dib->buffer,dib->dma_handle); + + pci_free_consistent(NULL, + dib->dibdev->parm->urb_buf_size*dib->dibdev->parm->num_urbs,dib->buffer, + dib->dma_handle); return 0; } static int dibusb_init(struct usb_dibusb *dib) { - int ret,i; + int ret,i,bufsize; sema_init(&dib->usb_sem, 1); sema_init(&dib->i2c_sem, 1); @@ -519,46 +747,74 @@ static int dibusb_init(struct usb_dibusb *dib) * when reloading the driver w/o replugging the device * a timeout occures, this helps */ - usb_clear_halt(dib->udev,COMMAND_PIPE); - usb_clear_halt(dib->udev,RESULT_PIPE); - usb_clear_halt(dib->udev,DATA_PIPE); + usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->parm->cmd_pipe)); + usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->parm->result_pipe)); + usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->parm->data_pipe)); - /* dibusb_reset_cpu(dib); */ + /* allocate the array for the data transfer URBs */ + dib->urb_list = kmalloc(dib->dibdev->parm->num_urbs*sizeof(struct urb *),GFP_KERNEL); + if (dib->urb_list == NULL) + return -ENOMEM; + memset(dib->urb_list,0,dib->dibdev->parm->num_urbs*sizeof(struct urb *)); - if ((dib->buffer = pci_alloc_consistent(NULL,DIBUSB_TS_BUFFER_SIZE, &dib->dma_handle)) == NULL) { + bufsize = dib->dibdev->parm->num_urbs*dib->dibdev->parm->urb_buf_size; + deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); + /* allocate the actual buffer for the URBs */ + if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) { + deb_info("not enough memory.\n"); + dibusb_exit(dib); return -ENOMEM; } - memset(dib->buffer,0,DIBUSB_TS_BUFFER_SIZE); - for (i = 0; i < DIBUSB_TS_NUM_URBS; i++) { - if (!(dib->buf_urb[i] = usb_alloc_urb(0,GFP_KERNEL))) { + deb_info("allocation complete\n"); + memset(dib->buffer,0,bufsize); + + /* allocate and submit the URBs */ + for (i = 0; i < dib->dibdev->parm->num_urbs; i++) { + if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_KERNEL))) { dibusb_exit(dib); return -ENOMEM; } deb_info("submitting URB no. %d\n",i); - usb_fill_bulk_urb( dib->buf_urb[i], dib->udev, DATA_PIPE, - &dib->buffer[i*DIBUSB_TS_URB_BUFFER_SIZE], DIBUSB_TS_URB_BUFFER_SIZE, + usb_fill_bulk_urb( dib->urb_list[i], dib->udev, + usb_rcvbulkpipe(dib->udev,dib->dibdev->parm->data_pipe), + &dib->buffer[i*dib->dibdev->parm->urb_buf_size], + dib->dibdev->parm->urb_buf_size, dibusb_urb_complete, dib); - dib->buf_urb[i]->transfer_flags = 0; - if ((ret = usb_submit_urb(dib->buf_urb[i],GFP_KERNEL))) { + dib->urb_list[i]->transfer_flags = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7) + dib->urb_list[i]->timeout = 0; +#endif + + if ((ret = usb_submit_urb(dib->urb_list[i],GFP_KERNEL))) { err("could not submit buffer urb no. %d\n",i); dibusb_exit(dib); return ret; } } - for (i=0; i < DIBUSB_MAX_PIDS; i++) { - dib->pid_list[i].reg = i+DIB3000MB_REG_FIRST_PID; - dib->pid_list[i].pid = 0; - dib->pid_list[i].active = 0; - dib->pid_list[i].dib = dib; - } - - dib->feedcount = 0; - dib->streaming = 0; dib->dvb_is_ready = 0; + /* Initialise the remote-control structures.*/ + init_input_dev(&dib->rc_input_dev); + + dib->rc_input_dev.evbit[0] = BIT(EV_KEY); + dib->rc_input_dev.keycodesize = sizeof(unsigned char); + dib->rc_input_dev.keycodemax = KEY_MAX; + dib->rc_input_dev.name = DRIVER_DESC " remote control"; + + for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i++) + set_bit(rc_keys[i].key, dib->rc_input_dev.keybit); + + input_register_device(&dib->rc_input_dev); + + dib->rc_input_event = KEY_MAX; + + INIT_WORK(&dib->rc_query_work, dibusb_query_rc, dib); + + dibusb_hw_wakeup(dib); + if ((ret = dibusb_dvb_init(dib))) { dibusb_exit(dib); return ret; @@ -579,17 +835,20 @@ static int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_device *dibdev) { const struct firmware *fw = NULL; + const char **fws; u16 addr; u8 *b,*p; int ret = 0,i; - for (i = 0; i < sizeof(valid_firmware_filenames)/sizeof(const char*); i++) { - if ((ret = request_firmware(&fw, valid_firmware_filenames[i], &udev->dev)) == 0) { - info("using firmware file (%s).",valid_firmware_filenames[i]); + fws = dibdev->parm->fw_filenames; + + for (i = 0; i < sizeof(fws)/sizeof(const char*); i++) { + if ((ret = request_firmware(&fw, fws[i], &udev->dev)) == 0) { + info("using firmware file (%s).",fws[i]); break; } deb_info("tried to find '%s' firmware - unsuccessful. (%d)\n", - valid_firmware_filenames[i],ret); + fws[i],ret); } if (fw == NULL) { @@ -609,7 +868,7 @@ static int dibusb_loadfirmware(struct usb_device *udev, /* stop the CPU */ reset = 1; - if ((ret = dibusb_writemem(udev,DIBUSB_CPU_CSREG,&reset,1)) != 1) + if ((ret = dibusb_writemem(udev,dibdev->parm->usb_cpu_csreg,&reset,1)) != 1) err("could not stop the USB controller CPU."); for(i = 0; p[i+3] == 0 && i < fw->size; ) { b = (u8 *) &p[i]; @@ -631,7 +890,7 @@ static int dibusb_loadfirmware(struct usb_device *udev, ret = 0; /* restart the CPU */ reset = 0; - if (ret || dibusb_writemem(udev,DIBUSB_CPU_CSREG,&reset,1) != 1) { + if (ret || dibusb_writemem(udev,dibdev->parm->usb_cpu_csreg,&reset,1) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } @@ -686,6 +945,27 @@ static int dibusb_probe(struct usb_interface *intf, } memset(dib,0,sizeof(struct usb_dibusb)); + dib->pid_parse = 1; + 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, will use pid parsing."); + break; + case USB_SPEED_HIGH: + if (!pid_parse) { + dib->pid_parse = 0; + info("running at HIGH speed, will deliver the complete TS."); + } else + info("running at HIGH speed, will use pid_parsing anyway."); + break; + case USB_SPEED_UNKNOWN: /* fall through */ + default: + err("cannot handle USB speed because it is unkown."); + break; + } + dib->udev = udev; dib->dibdev = dibdev; diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h index 27bcc71ef7a9..8a17fc0d6169 100644 --- a/drivers/media/dvb/dibusb/dvb-dibusb.h +++ b/drivers/media/dvb/dibusb/dvb-dibusb.h @@ -7,136 +7,278 @@ * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2. * - * - * * for more information see dvb-dibusb.c . */ #ifndef __DVB_DIBUSB_H__ #define __DVB_DIBUSB_H__ -#define DIBUSB_DEMOD_I2C_ADDR_DEFAULT 0x10 +#include "dib3000.h" -/* Vendor IDs */ -#define USB_VID_TWINHAN_ID 0x1822 -#define USB_VID_IMC_NETWORKS_ID 0x13d3 -#define USB_VID_EMPIA_ID 0xeb1a -#define USB_VID_DIBCOM_ID 0x10b8 -#define USB_VID_ULTIMA_ELECTRONIC_ID 0x05d8 -#define USB_VID_COMPRO_ID 0x185b -#define USB_VID_HYPER_PALTEK 0x1025 +typedef enum { + DIBUSB1_1 = 0, + DIBUSB2_0, + DIBUSB1_1_AN2235, +} dibusb_type; + +static const char * dibusb_fw_filenames1_1[] = { + "dvb-dibusb-5.0.0.11.fw" +}; + +static const char * dibusb_fw_filenames1_1_an2235[] = { + "dvb-dibusb-an2235-1.fw" +}; -/* Product IDs before loading the firmware */ -#define USB_PID_TWINHAN_VP7041_COLD_ID 0x3201 -#define USB_PID_KWORLD_VSTREAM_COLD_ID 0x17de -#define USB_PID_DIBCOM_MOD3000_COLD_ID 0x0bb8 -#define USB_PID_ULTIMA_TVBOX_COLD_ID 0x8105 -#define USB_PID_COMPRO_DVBU2000_COLD_ID 0xd000 -#define USB_PID_UNK_HYPER_PALTEK_COLD_ID 0x005e - -/* product ID afterwards */ -#define USB_PID_TWINHAN_VP7041_WARM_ID 0x3202 -#define USB_PID_KWORLD_VSTREAM_WARM_ID 0x17df -#define USB_PID_DIBCOM_MOD3000_WARM_ID 0x0bb9 -#define USB_PID_ULTIMA_TVBOX_WARM_ID 0x8106 -#define USB_PID_COMPRO_DVBU2000_WARM_ID 0xd001 -#define USB_PID_UNK_HYPER_PALTEK_WARM_ID 0x005f - -/* static array of valid firmware names, the best one first */ -static const char * valid_firmware_filenames[] = { - "dvb-dibusb-5.0.0.11.fw", +static const char * dibusb_fw_filenames2_0[] = { + "dvb-dibusb-6.0.0.5.fw" +}; + +struct dibusb_device_parameter { + dibusb_type type; + u8 demod_addr; + const char **fw_filenames; + const char *usb_controller; + u16 usb_cpu_csreg; + + int num_urbs; + int urb_buf_size; + int default_size; + int firmware_bug; + + int cmd_pipe; + int result_pipe; + int data_pipe; +}; + +static struct dibusb_device_parameter dibusb_dev_parm[3] = { + { .type = DIBUSB1_1, + .demod_addr = 0x10, + .fw_filenames = dibusb_fw_filenames1_1, + .usb_controller = "Cypress AN2135", + .usb_cpu_csreg = 0x7f92, + + .num_urbs = 3, + .urb_buf_size = 4096, + .default_size = 188*21, + .firmware_bug = 1, + + .cmd_pipe = 0x01, + .result_pipe = 0x81, + .data_pipe = 0x82, + }, + { .type = DIBUSB2_0, + .demod_addr = 0x18, + .fw_filenames = dibusb_fw_filenames2_0, + .usb_controller = "Cypress FX2", + .usb_cpu_csreg = 0xe600, + + .num_urbs = 3, + .urb_buf_size = 40960, + .default_size = 188*210, + .firmware_bug = 0, + + .cmd_pipe = 0x01, + .result_pipe = 0x81, + .data_pipe = 0x86, + }, + { .type = DIBUSB1_1_AN2235, + .demod_addr = 0x10, + .fw_filenames = dibusb_fw_filenames1_1_an2235, + .usb_controller = "Cypress CY7C64613 (AN2235)", + .usb_cpu_csreg = 0x7f92, + + .num_urbs = 3, + .urb_buf_size = 4096, + .default_size = 188*21, + .firmware_bug = 1, + + .cmd_pipe = 0x01, + .result_pipe = 0x81, + .data_pipe = 0x82, + } }; struct dibusb_device { + const char *name; u16 cold_product_id; u16 warm_product_id; - u8 demod_addr; - const char *name; + struct dibusb_device_parameter *parm; }; -#define DIBUSB_SUPPORTED_DEVICES 6 +/* Vendor IDs */ +#define USB_VID_ANCHOR 0x0547 +#define USB_VID_AVERMEDIA 0x14aa +#define USB_VID_COMPRO 0x185b +#define USB_VID_COMPRO_UNK 0x145f +#define USB_VID_CYPRESS 0x04b4 +#define USB_VID_DIBCOM 0x10b8 +#define USB_VID_EMPIA 0xeb1a +#define USB_VID_GRANDTEC 0x5032 +#define USB_VID_HYPER_PALTEK 0x1025 +#define USB_VID_IMC_NETWORKS 0x13d3 +#define USB_VID_TWINHAN 0x1822 +#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 + +/* Product IDs */ +#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 +#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 +#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 +#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 +#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c +#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 +#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 +#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 +#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 +#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 +#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_KWORLD_VSTREAM_COLD 0x17de +#define USB_PID_KWORLD_VSTREAM_WARM 0x17df +#define USB_PID_TWINHAN_VP7041_COLD 0x3201 +#define USB_PID_TWINHAN_VP7041_WARM 0x3202 +#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 +#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 +#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 +#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 +#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 +#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 +#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e +#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f +#define USB_PID_YAKUMO_DTT200U_COLD 0x0201 +#define USB_PID_YAKUMO_DTT200U_WARM 0x0301 + +#define DIBUSB_SUPPORTED_DEVICES 15 /* USB Driver stuff */ static struct dibusb_device dibusb_devices[DIBUSB_SUPPORTED_DEVICES] = { - { .cold_product_id = USB_PID_TWINHAN_VP7041_COLD_ID, - .warm_product_id = USB_PID_TWINHAN_VP7041_WARM_ID, - .name = "TwinhanDTV USB-Ter/Magic Box / HAMA USB DVB-T device", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, - }, - { .cold_product_id = USB_PID_KWORLD_VSTREAM_COLD_ID, - .warm_product_id = USB_PID_KWORLD_VSTREAM_WARM_ID, - .name = "KWorld V-Stream XPERT DTV - DVB-T USB", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, - }, - { .cold_product_id = USB_PID_DIBCOM_MOD3000_COLD_ID, - .warm_product_id = USB_PID_DIBCOM_MOD3000_WARM_ID, - .name = "DiBcom USB DVB-T reference design (MOD300)", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, - }, - { .cold_product_id = USB_PID_ULTIMA_TVBOX_COLD_ID, - .warm_product_id = USB_PID_ULTIMA_TVBOX_WARM_ID, - .name = "Ultima Electronic/Artec T1 USB TVBOX", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, - }, - { .cold_product_id = USB_PID_COMPRO_DVBU2000_COLD_ID, - .warm_product_id = USB_PID_COMPRO_DVBU2000_WARM_ID, - .name = "Compro Videomate DVB-U2000 - DVB-T USB", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, - }, - { .cold_product_id = USB_PID_UNK_HYPER_PALTEK_COLD_ID, - .warm_product_id = USB_PID_UNK_HYPER_PALTEK_WARM_ID, - .name = "Unkown USB DVB-T device ???? please report the name to linux-dvb or to the author", - .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, + { .name = "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device", + .cold_product_id = USB_PID_TWINHAN_VP7041_COLD, + .warm_product_id = USB_PID_TWINHAN_VP7041_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "KWorld V-Stream XPERT DTV - DVB-T USB1.1", + .cold_product_id = USB_PID_KWORLD_VSTREAM_COLD, + .warm_product_id = USB_PID_KWORLD_VSTREAM_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Grandtec USB1.1 DVB-T/DiBcom USB1.1 DVB-T reference design (MOD3000)", + .cold_product_id = USB_PID_DIBCOM_MOD3000_COLD, + .warm_product_id = USB_PID_DIBCOM_MOD3000_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Artec T1 USB1.1 TVBOX with AN2135", + .cold_product_id = USB_PID_ULTIMA_TVBOX_COLD, + .warm_product_id = USB_PID_ULTIMA_TVBOX_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Artec T1 USB1.1 TVBOX with AN2235", + .cold_product_id = USB_PID_ULTIMA_TVBOX_AN2235_COLD, + .warm_product_id = USB_PID_ULTIMA_TVBOX_AN2235_WARM, + .parm = &dibusb_dev_parm[2], + }, + { .name = "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)", + .cold_product_id = USB_PID_ULTIMA_TVBOX_ANCHOR_COLD, + .warm_product_id = 0, /* undefined, this design becomes USB_PID_DIBCOM_MOD3000_WARM in warm state */ + .parm = &dibusb_dev_parm[2], + }, + { .name = "Artec T1 USB2.0 TVBOX (please report the warm ID)", + .cold_product_id = USB_PID_ULTIMA_TVBOX_USB2_COLD, + .warm_product_id = 0, /* don't know, it is most likely that the device will get another USB ID in warm state */ + .parm = &dibusb_dev_parm[1], + }, + { .name = "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)", + .cold_product_id = USB_PID_ULTIMA_TVBOX_USB2_FX_COLD, + .warm_product_id = USB_PID_ULTIMA_TVBOX_USB2_FX_WARM, /* undefined, it could be that the device will get another USB ID in warm state */ + .parm = &dibusb_dev_parm[1], + }, + { .name = "Compro Videomate DVB-U2000 - DVB-T USB1.1", + .cold_product_id = USB_PID_COMPRO_DVBU2000_COLD, + .warm_product_id = USB_PID_COMPRO_DVBU2000_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Compro Videomate DVB-U2000 - DVB-T USB1.1 (really ?? please report the name!)", + .cold_product_id = USB_PID_COMPRO_DVBU2000_UNK_COLD, + .warm_product_id = USB_PID_COMPRO_DVBU2000_UNK_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Unkown USB1.1 DVB-T device ???? please report the name to the author", + .cold_product_id = USB_PID_UNK_HYPER_PALTEK_COLD, + .warm_product_id = USB_PID_UNK_HYPER_PALTEK_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "DiBcom USB2.0 DVB-T reference design (MOD3000P)", + .cold_product_id = USB_PID_DIBCOM_MOD3001_COLD, + .warm_product_id = USB_PID_DIBCOM_MOD3001_WARM, + .parm = &dibusb_dev_parm[1], + }, + { .name = "Grandtec DVB-T USB1.1", + .cold_product_id = USB_PID_GRANDTEC_DVBT_USB_COLD, + .warm_product_id = USB_PID_GRANDTEC_DVBT_USB_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Avermedia AverTV DVBT USB1.1", + .cold_product_id = USB_PID_AVERMEDIA_DVBT_USB_COLD, + .warm_product_id = USB_PID_AVERMEDIA_DVBT_USB_WARM, + .parm = &dibusb_dev_parm[0], + }, + { .name = "Yakumo DVB-T mobile USB2.0", + .cold_product_id = USB_PID_YAKUMO_DTT200U_COLD, + .warm_product_id = USB_PID_YAKUMO_DTT200U_WARM, + .parm = &dibusb_dev_parm[1], } }; /* USB Driver stuff */ /* table of devices that work with this driver */ static struct usb_device_id dibusb_table [] = { - { USB_DEVICE(USB_VID_TWINHAN_ID, USB_PID_TWINHAN_VP7041_COLD_ID) }, - { USB_DEVICE(USB_VID_TWINHAN_ID, USB_PID_TWINHAN_VP7041_WARM_ID) }, - { USB_DEVICE(USB_VID_IMC_NETWORKS_ID,USB_PID_TWINHAN_VP7041_COLD_ID) }, - { USB_DEVICE(USB_VID_IMC_NETWORKS_ID,USB_PID_TWINHAN_VP7041_WARM_ID) }, - { USB_DEVICE(USB_VID_EMPIA_ID, USB_PID_KWORLD_VSTREAM_COLD_ID) }, - { USB_DEVICE(USB_VID_EMPIA_ID, USB_PID_KWORLD_VSTREAM_WARM_ID) }, - { USB_DEVICE(USB_VID_DIBCOM_ID, USB_PID_DIBCOM_MOD3000_COLD_ID) }, - { USB_DEVICE(USB_VID_DIBCOM_ID, USB_PID_DIBCOM_MOD3000_WARM_ID) }, - { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC_ID, USB_PID_ULTIMA_TVBOX_COLD_ID) }, - { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC_ID, USB_PID_ULTIMA_TVBOX_WARM_ID) }, - { USB_DEVICE(USB_VID_COMPRO_ID, USB_PID_COMPRO_DVBU2000_COLD_ID) }, - { USB_DEVICE(USB_VID_COMPRO_ID, USB_PID_COMPRO_DVBU2000_WARM_ID) }, - { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD_ID) }, - { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM_ID) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_COLD)}, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_WARM)}, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, + { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, + { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, + { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, + { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, + { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, + { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, + { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, + { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, + { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_COLD) }, + { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_WARM) }, + { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, + { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_COLD) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_WARM) }, + { USB_DEVICE(USB_PID_COMPRO_DVBU2000_UNK_COLD, USB_VID_COMPRO_UNK) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, + +/* + * activate the following define when you have one of the devices and want to + * build it from build-2.6 in dvb-kernel + */ +// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES +#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES + { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) }, + { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) }, + { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) }, +#endif { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, dibusb_table); -/* CS register start/stop the usb controller cpu */ -#define DIBUSB_CPU_CSREG 0x7F92 - -// 0x10 is the I2C address of the first demodulator on the board -#define DIBUSB_DEMOD_I2C_ADDR_DEFAULT 0x10 #define DIBUSB_I2C_TIMEOUT HZ*5 -#define DIBUSB_MAX_PIDS 16 - -#define DIB3000MB_REG_FIRST_PID ( 153) - -struct usb_dibusb; - -struct dibusb_pid { - u16 reg; - u16 pid; - int active; - struct usb_dibusb *dib; -}; - -#define DIBUSB_TS_NUM_URBS 3 -#define DIBUSB_TS_URB_BUFFER_SIZE 4096 -#define DIBUSB_TS_BUFFER_SIZE (DIBUSB_TS_NUM_URBS * DIBUSB_TS_URB_BUFFER_SIZE) -#define DIBUSB_TS_DEFAULT_SIZE (188*21) - struct usb_dibusb { /* usb */ struct usb_device * udev; @@ -144,14 +286,13 @@ struct usb_dibusb { struct dibusb_device * dibdev; int feedcount; - int streaming; - struct urb * buf_urb[DIBUSB_TS_NUM_URBS]; + int pid_parse; + struct dib3000_xfer_ops xfer_ops; + + struct urb **urb_list; u8 *buffer; dma_addr_t dma_handle; - spinlock_t pid_list_lock; - struct dibusb_pid pid_list[DIBUSB_MAX_PIDS]; - /* I2C */ struct i2c_adapter i2c_adap; struct i2c_client i2c_client; @@ -166,16 +307,14 @@ struct usb_dibusb { struct dmxdev dmxdev; struct dvb_demux demux; struct dvb_net dvb_net; + struct dvb_frontend* fe; + + /* remote control */ + struct input_dev rc_input_dev; + struct work_struct rc_query_work; + int rc_input_event; }; -#define COMMAND_PIPE usb_sndbulkpipe(dib->udev, 0x01) -#define RESULT_PIPE usb_rcvbulkpipe(dib->udev, 0x81) -#define DATA_PIPE usb_rcvbulkpipe(dib->udev, 0x82) -/* - * last endpoint 0x83 only used for chaining the buffers - * of the endpoints in the cypress - */ -#define CHAIN_PIPE_DO_NOT_USE usb_rcvbulkpipe(dib->udev, 0x83) /* types of first byte of each buffer */ @@ -209,16 +348,4 @@ struct usb_dibusb { #define DIBUSB_IOCTL_POWER_SLEEP 0x00 #define DIBUSB_IOCTL_POWER_WAKEUP 0x01 - -/* - * values from the demodulator which are needed in - * the usb driver as well - */ - -#define DIB3000MB_REG_FIFO ( 145) -#define DIB3000MB_FIFO_INHIBIT ( 1) -#define DIB3000MB_FIFO_ACTIVATE ( 0) - -#define DIB3000MB_ACTIVATE_FILTERING (0x2000) - #endif diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile index 9d534602a864..c6baac20f529 100644 --- a/drivers/media/dvb/dvb-core/Makefile +++ b/drivers/media/dvb/dvb-core/Makefile @@ -4,6 +4,6 @@ dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ dvb_ca_en50221.o dvb_frontend.o \ - dvb_net.o dvb_ksyms.o dvb_ringbuffer.o + dvb_net.o dvb_ringbuffer.o obj-$(CONFIG_DVB_CORE) += dvb-core.o diff --git a/drivers/media/dvb/dvb-core/Makefile.lib b/drivers/media/dvb/dvb-core/Makefile.lib deleted file mode 100644 index 463372889b61..000000000000 --- a/drivers/media/dvb/dvb-core/Makefile.lib +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_DVB_CORE) += crc32.o diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 09ad79e08371..cdbfb15b78f3 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -42,18 +42,12 @@ MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); #define dprintk if (debug) printk -inline struct dmxdev_filter * +static inline struct dmxdev_filter * dvb_dmxdev_file_to_filter(struct file *file) { return (struct dmxdev_filter *) file->private_data; } -inline struct dmxdev_dvr * -dvb_dmxdev_file_to_dvr(struct dmxdev *dmxdev, struct file *file) -{ - return (struct dmxdev_dvr *) file->private_data; -} - static inline void dvb_dmxdev_buffer_init(struct dmxdev_buffer *buffer) { buffer->data=NULL; @@ -846,7 +840,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, } -ssize_t +static ssize_t dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file); @@ -1122,6 +1116,7 @@ dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) return 0; } +EXPORT_SYMBOL(dvb_dmxdev_init); void dvb_dmxdev_release(struct dmxdev *dmxdev) @@ -1138,5 +1133,5 @@ dvb_dmxdev_release(struct dmxdev *dmxdev) } dmxdev->demux->close(dmxdev->demux); } - +EXPORT_SYMBOL(dvb_dmxdev_release); diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index fff53542aeb0..63eccd4b67b2 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -181,10 +181,12 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen) { int i; - if (hlen < nlen) return NULL; + if (hlen < nlen) + return NULL; for(i=0; i<= hlen - nlen; i++) { - if (!strncmp(haystack+i, needle, nlen)) return haystack+i; + if (!strncmp(haystack + i, needle, nlen)) + return haystack + i; } return NULL; @@ -211,7 +213,7 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot) } /* poll mode */ - slot_status = ca->pub->poll_slot_status(ca->pub, slot); + slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open); cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0; cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0; @@ -250,7 +252,8 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot) * * @return 0 on success, nonzero on error. */ -static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8 waitfor, int timeout_hz) +static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, + u8 waitfor, int timeout_hz) { unsigned long timeout; unsigned long start; @@ -263,7 +266,8 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8 while(1) { /* read the status and check for error */ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); - if (res < 0) return -EIO; + if (res < 0) + return -EIO; /* if we got the flags, it was successful! */ if (res & waitfor) { @@ -311,24 +315,33 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot) ca->slot_info[slot].link_buf_size = 2; /* read the buffer size from the CAM */ - if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) return ret; - if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ/10)) != 0) return ret; - if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return -EIO; - if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret; + if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) + return ret; + if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0) + return ret; + if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) + return -EIO; + if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) + return ret; /* store it, and choose the minimum of our buffer and the CAM's buffer size */ buf_size = (buf[0] << 8) | buf[1]; - if (buf_size > HOST_LINK_BUF_SIZE) buf_size = HOST_LINK_BUF_SIZE; + if (buf_size > HOST_LINK_BUF_SIZE) + buf_size = HOST_LINK_BUF_SIZE; ca->slot_info[slot].link_buf_size = buf_size; buf[0] = buf_size >> 8; buf[1] = buf_size & 0xff; dprintk("Chosen link buffer size of %i\n", buf_size); /* write the buffer size to the CAM */ - if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) return ret; - if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret; - if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) return -EIO; - if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret; + if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) + return ret; + if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0) + return ret; + if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) + return -EIO; + if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) + return ret; /* success */ return 0; @@ -355,7 +368,8 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot, int _address = *address; /* grab the next tuple length and type */ - if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType; + if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) + return _tupleType; if (_tupleType == 0xff) { dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType); *address += 2; @@ -363,7 +377,8 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot, *tupleLength = 0; return 0; } - if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address+2)) < 0) return _tupleLength; + if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0) + return _tupleLength; _address += 4; dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength); @@ -371,7 +386,9 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot, /* read in the whole tuple */ for(i=0; i< _tupleLength; i++) { tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i*2)); - dprintk(" 0x%02x: 0x%02x %c\n", i, tuple[i] & 0xff, ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); + dprintk(" 0x%02x: 0x%02x %c\n", + i, tuple[i] & 0xff, + ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); } _address += (_tupleLength*2); @@ -409,40 +426,58 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot) // CISTPL_DEVICE_0A - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; - if (tupleType != 0x1D) return -EINVAL; + if ((status = + dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) + return status; + if (tupleType != 0x1D) + return -EINVAL; // CISTPL_DEVICE_0C - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; - if (tupleType != 0x1C) return -EINVAL; + if ((status = + dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) + return status; + if (tupleType != 0x1C) + return -EINVAL; // CISTPL_VERS_1 - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; - if (tupleType != 0x15) return -EINVAL; + if ((status = + dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) + return status; + if (tupleType != 0x15) + return -EINVAL; // CISTPL_MANFID - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; - if (tupleType != 0x20) return -EINVAL; - if (tupleLength != 4) return -EINVAL; + if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, + &tupleLength, tuple)) < 0) + return status; + if (tupleType != 0x20) + return -EINVAL; + if (tupleLength != 4) + return -EINVAL; manfid = (tuple[1] << 8) | tuple[0]; devid = (tuple[3] << 8) | tuple[2]; // CISTPL_CONFIG - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; - if (tupleType != 0x1A) return -EINVAL; - if (tupleLength < 3) return -EINVAL; + if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, + &tupleLength, tuple)) < 0) + return status; + if (tupleType != 0x1A) + return -EINVAL; + if (tupleLength < 3) + return -EINVAL; /* extract the configbase */ rasz = tuple[0] & 3; - if (tupleLength < (3 + rasz + 14)) return -EINVAL; + if (tupleLength < (3 + rasz + 14)) + return -EINVAL; ca->slot_info[slot].config_base = 0; for(i=0; i< rasz+1; i++) { ca->slot_info[slot].config_base |= (tuple[2+i] << (8*i)); @@ -450,8 +485,10 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot) /* check it contains the correct DVB string */ dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8); - if (dvb_str == NULL) return -EINVAL; - if (tupleLength < ((dvb_str - (char*) tuple) + 12)) return -EINVAL; + if (dvb_str == NULL) + return -EINVAL; + if (tupleLength < ((dvb_str - (char *) tuple) + 12)) + return -EINVAL; /* is it a version we support? */ if (strncmp(dvb_str + 8, "1.00", 4)) { @@ -462,20 +499,25 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot) /* process the CFTABLE_ENTRY tuples, and any after those */ while((!end_chain) && (address < 0x1000)) { - if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; + if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, + &tupleLength, tuple)) < 0) + return status; switch(tupleType) { case 0x1B: // CISTPL_CFTABLE_ENTRY - if (tupleLength < (2+11+17)) break; + if (tupleLength < (2 + 11 + 17)) + break; /* if we've already parsed one, just use it */ - if (got_cftableentry) break; + if (got_cftableentry) + break; /* get the config option */ ca->slot_info[slot].config_option = tuple[0] & 0x3f; /* OK, check it contains the correct strings */ if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) || - (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break; + (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) + break; got_cftableentry = 1; break; @@ -488,17 +530,17 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot) break; default: /* Unknown tuple type - just skip this tuple and move to the next one */ - dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, tupleLength); + dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, + tupleLength); break; } } - if ((address > 0x1000) || (!got_cftableentry)) return -EINVAL; + if ((address > 0x1000) || (!got_cftableentry)) + return -EINVAL; dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n", - manfid, devid, - ca->slot_info[slot].config_base, - ca->slot_info[slot].config_option); + manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); // success! return 0; @@ -518,7 +560,9 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private* ca, int slot) dprintk ("%s\n", __FUNCTION__); /* set the config option */ - ca->pub->write_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); + ca->pub->write_attribute_mem(ca->pub, slot, + ca->slot_info[slot].config_base, + ca->slot_info[slot].config_option); /* check it */ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base); @@ -558,6 +602,11 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu int buf_free; down_read(&ca->slot_info[slot].sem); + if (ca->slot_info[slot].rx_buffer.data == NULL) { + up_read(&ca->slot_info[slot].sem); + status = -EIO; + goto exit; + } buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer); up_read(&ca->slot_info[slot].sem); @@ -568,7 +617,8 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu } /* check if there is data available */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) + goto exit; if (!(status & STATUSREG_DA)) { /* no data */ status = 0; @@ -576,28 +626,33 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu } /* read the amount of data */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) + goto exit; bytes_read = status << 8; - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) + goto exit; bytes_read |= status; /* check it will fit */ if (ebuf == NULL) { if (bytes_read > ca->slot_info[slot].link_buf_size) { - printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size!\n", ca->dvbdev->adapter->num); + printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", + ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } if (bytes_read < 2) { - printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", ca->dvbdev->adapter->num); + printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } } else { if (bytes_read > ecount) { - printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", ca->dvbdev->adapter->num); + printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", + ca->dvbdev->adapter->num); status = -EIO; goto exit; } @@ -606,14 +661,16 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu /* fill the buffer */ for(i=0; i < bytes_read; i++) { /* read byte and check */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) + goto exit; /* OK, store it in the buffer */ buf[i] = status; } /* check for read error (RE should now be 0) */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) + goto exit; if (status & STATUSREG_RE) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; @@ -623,13 +680,19 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu /* OK, add it to the receive buffer, or copy into external buffer if supplied */ if (ebuf == NULL) { down_read(&ca->slot_info[slot].sem); + if (ca->slot_info[slot].rx_buffer.data == NULL) { + up_read(&ca->slot_info[slot].sem); + status = -EIO; + goto exit; + } dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read); up_read(&ca->slot_info[slot].sem); } else { memcpy(ebuf, buf, bytes_read); } - dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_read); + dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, + buf[0], (buf[1] & 0x80) == 0, bytes_read); /* wake up readers when a last_fragment is received */ if ((buf[1] & 0x80) == 0x00) { @@ -664,20 +727,25 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu // sanity check - if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL; + if (bytes_write > ca->slot_info[slot].link_buf_size) + return -EINVAL; /* check if interface is actually waiting for us to read from it, or if a read is in progress */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) + goto exitnowrite; if (status & (STATUSREG_DA|STATUSREG_RE)) { status = -EAGAIN; goto exitnowrite; } /* OK, set HC bit */ - if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC)) != 0) goto exit; + if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, + IRQEN | CMDREG_HC)) != 0) + goto exit; /* check if interface is still free */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) + goto exit; if (!(status & STATUSREG_FR)) { /* it wasn't free => try again later */ status = -EAGAIN; @@ -685,16 +753,21 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu } /* send the amount of data */ - if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) goto exit; - if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, bytes_write & 0xff)) != 0) goto exit; + if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) + goto exit; + if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, + bytes_write & 0xff)) != 0) + goto exit; /* send the buffer */ for(i=0; i < bytes_write; i++) { - if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) goto exit; + if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) + goto exit; } /* check for write error (WE should now be 0) */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; + if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) + goto exit; if (status & STATUSREG_WE) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; @@ -702,7 +775,8 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu } status = bytes_write; - dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_write); + dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, + buf[0], (buf[1] & 0x80) == 0, bytes_write); exit: ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); @@ -710,6 +784,7 @@ exit: exitnowrite: return status; } +EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); @@ -730,7 +805,8 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot) down_write(&ca->slot_info[slot].sem); ca->pub->slot_shutdown(ca->pub, slot); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; - if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data); + if (ca->slot_info[slot].rx_buffer.data) + vfree(ca->slot_info[slot].rx_buffer.data); ca->slot_info[slot].rx_buffer.data = NULL; up_write(&ca->slot_info[slot].sem); @@ -743,6 +819,7 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot) /* success */ return 0; } +EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); /** @@ -771,6 +848,7 @@ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int ch atomic_inc(&ca->slot_info[slot].camchange_count); dvb_ca_en50221_thread_wakeup(ca); } +EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); /** @@ -815,7 +893,8 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot) break; case DVB_CA_SLOTSTATE_RUNNING: - if (ca->open) dvb_ca_en50221_read_data(ca, slot, NULL, 0); + if (ca->open) + dvb_ca_en50221_read_data(ca, slot, NULL, 0); break; } } @@ -851,7 +930,8 @@ static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private* ca) ca->wakeup = 0; return 1; } - if (ca->exit) return 1; + if (ca->exit) + return 1; return 0; } @@ -901,7 +981,8 @@ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private* ca) break; } - if (delay < curdelay) curdelay = delay; + if (delay < curdelay) + curdelay = delay; } ca->delay = curdelay; @@ -918,6 +999,7 @@ static int dvb_ca_en50221_thread(void* data) char name[15]; int slot; int flags; + int status; int pktcount; void* rxbuf; @@ -938,7 +1020,9 @@ static int dvb_ca_en50221_thread(void* data) while(!ca->exit) { /* sleep for a bit */ if (!ca->wakeup) { - flags = wait_event_interruptible_timeout(ca->thread_queue, dvb_ca_en50221_thread_should_wakeup(ca), ca->delay); + flags = wait_event_interruptible_timeout(ca->thread_queue, + dvb_ca_en50221_thread_should_wakeup(ca), + ca->delay); if ((flags == -ERESTARTSYS) || ca->exit) { /* got signal or quitting */ break; @@ -952,7 +1036,8 @@ static int dvb_ca_en50221_thread(void* data) // check the cam status + deal with CAMCHANGEs while(dvb_ca_en50221_check_camstatus(ca, slot)) { /* clear down an old CI slot if necessary */ - if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) dvb_ca_en50221_slot_shutdown(ca, slot); + if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) + dvb_ca_en50221_slot_shutdown(ca, slot); /* if a CAM is NOW present, initialise it */ if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) { @@ -979,7 +1064,8 @@ static int dvb_ca_en50221_thread(void* data) case DVB_CA_SLOTSTATE_WAITREADY: if (time_after(jiffies, ca->slot_info[slot].timeout)) { - printk("dvb_ca adaptor %d: PC card did not respond :(\n", ca->dvbdev->adapter->num); + printk("dvb_ca adaptor %d: PC card did not respond :(\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; @@ -988,20 +1074,25 @@ static int dvb_ca_en50221_thread(void* data) break; case DVB_CA_SLOTSTATE_VALIDATE: - if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { - printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", ca->dvbdev->adapter->num); + if (dvb_ca_en50221_parse_attributes(ca, slot) + != 0) { + printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; } if (dvb_ca_en50221_set_configoption(ca, slot) != 0) { - printk("dvb_ca adapter %d: Unable to initialise CAM :(\n", ca->dvbdev->adapter->num); + printk("dvb_ca adapter %d: Unable to initialise CAM :(\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; } - if (ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS) != 0) { - printk("dvb_ca adapter %d: Unable to reset CAM IF\n", ca->dvbdev->adapter->num); + if (ca->pub->write_cam_control(ca->pub, slot, + CTRLIF_COMMAND, CMDREG_RS) != 0) { + printk("dvb_ca adapter %d: Unable to reset CAM IF\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; @@ -1016,7 +1107,8 @@ static int dvb_ca_en50221_thread(void* data) case DVB_CA_SLOTSTATE_WAITFR: if (time_after(jiffies, ca->slot_info[slot].timeout)) { - printk("dvb_ca adapter %d: DVB CAM did not respond :(\n", ca->dvbdev->adapter->num); + printk("dvb_ca adapter %d: DVB CAM did not respond :(\n", + ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; @@ -1044,7 +1136,9 @@ static int dvb_ca_en50221_thread(void* data) dvb_ca_en50221_thread_update_delay(ca); break; } + down_write(&ca->slot_info[slot].sem); dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE); + up_write(&ca->slot_info[slot].sem); ca->pub->slot_ts_enable(ca->pub, slot); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING; @@ -1053,15 +1147,18 @@ static int dvb_ca_en50221_thread(void* data) break; case DVB_CA_SLOTSTATE_RUNNING: - if (!ca->open) break; + if (!ca->open) + continue; // no need to poll if the CAM supports IRQs - if (ca->slot_info[slot].da_irq_supported) break; + if (ca->slot_info[slot].da_irq_supported) + break; // poll mode pktcount = 0; - while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) { - if (!ca->open) break; + while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) { + if (!ca->open) + break; /* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */ if (dvb_ca_en50221_check_camstatus(ca, slot)) { @@ -1105,7 +1202,8 @@ static int dvb_ca_en50221_thread(void* data) * * @return 0 on success, <0 on error. */ -static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) +static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) { struct dvb_device* dvbdev=(struct dvb_device*) file->private_data; struct dvb_ca_private* ca = (struct dvb_ca_private*) dvbdev->priv; @@ -1120,15 +1218,16 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, un if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) { dvb_ca_en50221_slot_shutdown(ca, slot); if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) - dvb_ca_en50221_camchange_irq(ca->pub, slot, DVB_CA_EN50221_CAMCHANGE_INSERTED); + dvb_ca_en50221_camchange_irq(ca->pub, + slot, + DVB_CA_EN50221_CAMCHANGE_INSERTED); } } ca->next_read_slot = 0; dvb_ca_en50221_thread_wakeup(ca); break; - case CA_GET_CAP: - { + case CA_GET_CAP: { struct ca_caps *caps = (struct ca_caps*) parg; caps->slot_num=ca->slot_count; @@ -1138,9 +1237,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, un break; } - - case CA_GET_SLOT_INFO: - { + case CA_GET_SLOT_INFO: { struct ca_slot_info *info=(struct ca_slot_info *)parg; if ((info->num > ca->slot_count) || (info->num < 0)) @@ -1148,8 +1245,8 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, un info->type = CA_CI_LINK; info->flags = 0; - if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) && - (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) { + if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) + && (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) { info->flags = CA_CI_MODULE_PRESENT; } if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) { @@ -1177,7 +1274,8 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, un * * @return 0 on success, <0 on error. */ -static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl); } @@ -1193,7 +1291,8 @@ static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, unsig * * @return Number of bytes read, or <0 on error. */ -static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +static ssize_t dvb_ca_en50221_io_write(struct file *file, + const char __user * buf, size_t count, loff_t * ppos) { struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv; @@ -1208,35 +1307,48 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf dprintk ("%s\n", __FUNCTION__); /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ - if (count < 2) return -EINVAL; + if (count < 2) + return -EINVAL; /* extract slot & connection id */ - if (copy_from_user(&slot, buf, 1)) return -EFAULT; - if (copy_from_user(&connection_id, buf+1, 1)) return -EFAULT; + if (copy_from_user(&slot, buf, 1)) + return -EFAULT; + if (copy_from_user(&connection_id, buf + 1, 1)) + return -EFAULT; buf+=2; count-=2; /* check if the slot is actually running */ - if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) return -EINVAL; + if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) + return -EINVAL; /* fragment the packets & store in the buffer */ while(fragpos < count) { fraglen = ca->slot_info[slot].link_buf_size - 2; - if ((count - fragpos) < fraglen) fraglen = count - fragpos; + if ((count - fragpos) < fraglen) + fraglen = count - fragpos; fragbuf[0] = connection_id; fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00; - if ((status = copy_from_user(fragbuf+2, buf+fragpos, fraglen)) != 0) goto exit; + if ((status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen)) != 0) + goto exit; timeout = jiffies + HZ/2; written = 0; while(!time_after(jiffies, timeout)) { + /* check the CAM hasn't been removed/reset in the meantime */ + if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) { + status = -EIO; + goto exit; + } + status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen+2); if (status == (fraglen+2)) { written = 1; break; } - if (status != -EAGAIN) goto exit; + if (status != -EAGAIN) + goto exit; msleep(1); } @@ -1269,14 +1381,21 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu slot = ca->next_read_slot; while((slot_count < ca->slot_count) && (!found)) { - if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot; + if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) + goto nextslot; down_read(&ca->slot_info[slot].sem); + if (ca->slot_info[slot].rx_buffer.data == NULL) { + up_read(&ca->slot_info[slot].sem); + return 0; + } + idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); while(idx != -1) { dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); - if (connection_id == -1) connection_id = hdr[0]; + if (connection_id == -1) + connection_id = hdr[0]; if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) { *_slot = slot; found = 1; @@ -1286,7 +1405,8 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); } - if (!found) up_read(&ca->slot_info[slot].sem); + if (!found) + up_read(&ca->slot_info[slot].sem); nextslot: slot = (slot + 1) % ca->slot_count; @@ -1308,7 +1428,8 @@ nextslot: * * @return Number of bytes read, or <0 on error. */ -static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) { struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv; @@ -1326,19 +1447,24 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_ dprintk ("%s\n", __FUNCTION__); /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ - if (count < 2) return -EINVAL; + if (count < 2) + return -EINVAL; /* wait for some data */ if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) { /* if we're in nonblocking mode, exit immediately */ - if (file->f_flags & O_NONBLOCK) return -EWOULDBLOCK; + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; /* wait for some data */ - status = wait_event_interruptible(ca->wait_queue, dvb_ca_en50221_io_read_condition(ca, &result, &slot)); + status = wait_event_interruptible(ca->wait_queue, + dvb_ca_en50221_io_read_condition + (ca, &result, &slot)); } if ((status < 0) || (result < 0)) { - if (result) return result; + if (result) + return result; return status; } @@ -1352,7 +1478,8 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_ } dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); - if (connection_id == -1) connection_id = hdr[0]; + if (connection_id == -1) + connection_id = hdr[0]; if (hdr[0] == connection_id) { if (pktlen < count) { if ((pktlen + fraglen - 2) > count) { @@ -1361,25 +1488,29 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_ fraglen -= 2; } - if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, buf + pktlen, fraglen, 1)) < 0) { + if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, + buf + pktlen, fraglen, 1)) < 0) { goto exit; } pktlen += fraglen; } - if ((hdr[1] & 0x80) == 0) last_fragment = 1; + if ((hdr[1] & 0x80) == 0) + last_fragment = 1; dispose = 1; } idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); - if (dispose) dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx); + if (dispose) + dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx); idx = idx2; dispose = 0; } while (!last_fragment); hdr[0] = slot; hdr[1] = connection_id; - if ((status = copy_to_user(buf, hdr, 2)) != 0) goto exit; + if ((status = copy_to_user(buf, hdr, 2)) != 0) + goto exit; status = pktlen; exit: @@ -1405,14 +1536,20 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) dprintk ("%s\n", __FUNCTION__); + if (!try_module_get(ca->pub->owner)) + return -EIO; + err=dvb_generic_open(inode, file); if (err<0) return err; for(i=0; i< ca->slot_count; i++) { + if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) { down_write(&ca->slot_info[i].sem); + if (ca->slot_info[i].rx_buffer.data != NULL) { dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer); + } up_write(&ca->slot_info[i].sem); } } @@ -1437,7 +1574,7 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) { struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv; - int err; + int err = 0; dprintk ("%s\n", __FUNCTION__); @@ -1446,8 +1583,9 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) dvb_ca_en50221_thread_update_delay(ca); err=dvb_generic_release(inode, file); - if (err<0) - return err; + + module_put(ca->pub->owner); + return 0; } @@ -1476,7 +1614,8 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) } /* if there is something, return now */ - if (mask) return mask; + if (mask) + return mask; /* wait for something to happen */ poll_wait(file, &ca->wait_queue, wait); @@ -1488,6 +1627,8 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) return mask; } +EXPORT_SYMBOL(dvb_ca_en50221_init); + static struct file_operations dvb_ca_fops = { .owner = THIS_MODULE, @@ -1521,7 +1662,8 @@ static struct dvb_device dvbdev_ca = { * * @return 0 on success, nonzero on failure */ -int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* pubca, int flags, int slot_count) +int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, + struct dvb_ca_en50221 *pubca, int flags, int slot_count) { int ret; struct dvb_ca_private* ca = NULL; @@ -1529,10 +1671,13 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* dprintk ("%s\n", __FUNCTION__); - if (slot_count < 1) return -EINVAL; + if (slot_count < 1) + return -EINVAL; /* initialise the system data */ - if ((ca = (struct dvb_ca_private*) kmalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) { + if ((ca = + (struct dvb_ca_private *) kmalloc(sizeof(struct dvb_ca_private), + GFP_KERNEL)) == NULL) { ret = -ENOMEM; goto error; } @@ -1556,7 +1701,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* /* register the DVB device */ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA); - if (ret) goto error; + if (ret) + goto error; /* now initialise each slot */ for(i=0; i< slot_count; i++) { @@ -1584,13 +1730,16 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* error: if (ca != NULL) { - if (ca->dvbdev != NULL) dvb_unregister_device(ca->dvbdev); - if (ca->slot_info != NULL) kfree(ca->slot_info); + if (ca->dvbdev != NULL) + dvb_unregister_device(ca->dvbdev); + if (ca->slot_info != NULL) + kfree(ca->slot_info); kfree(ca); } pubca->private = NULL; return ret; } +EXPORT_SYMBOL(dvb_ca_en50221_release); @@ -1610,7 +1759,8 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca) /* shutdown the thread if there was one */ if (ca->thread_pid) { if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) { - printk("dvb_ca_release adapter %d: thread PID %d already died\n", ca->dvbdev->adapter->num, ca->thread_pid); + printk("dvb_ca_release adapter %d: thread PID %d already died\n", + ca->dvbdev->adapter->num, ca->thread_pid); } else { ca->exit = 1; mb(); diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h index 882ca41a28dd..8467e63ddc0d 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h @@ -42,6 +42,9 @@ /* Structure describing a CA interface */ struct dvb_ca_en50221 { + /* the module owning this structure */ + struct module* owner; + /* NOTE: the read_*, write_* and poll_slot_status functions must use locks as * they may be called from several threads at once */ @@ -62,7 +65,7 @@ struct dvb_ca_en50221 { * Poll slot status. * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set */ - int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot); + int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open); /* private data, used by caller */ void* data; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 6b55e4910efa..62d1efc0c019 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -424,7 +424,7 @@ void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); } } - +EXPORT_SYMBOL(dvb_dmx_swfilter_packet); void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count) { @@ -439,6 +439,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t cou spin_unlock(&demux->lock); } +EXPORT_SYMBOL(dvb_dmx_swfilter_packets); void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) @@ -478,6 +479,7 @@ void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) bailout: spin_unlock(&demux->lock); } +EXPORT_SYMBOL(dvb_dmx_swfilter); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) { @@ -522,6 +524,7 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) bailout: spin_unlock(&demux->lock); } +EXPORT_SYMBOL(dvb_dmx_swfilter_204); static struct dvb_demux_filter * dvb_dmx_filter_alloc(struct dvb_demux *demux) @@ -1163,6 +1166,7 @@ int dvbdmx_connect_frontend(struct dmx_demux *demux, struct dmx_frontend *fronte up(&dvbdemux->mutex); return 0; } +EXPORT_SYMBOL(dvbdmx_connect_frontend); int dvbdmx_disconnect_frontend(struct dmx_demux *demux) @@ -1176,6 +1180,7 @@ int dvbdmx_disconnect_frontend(struct dmx_demux *demux) up(&dvbdemux->mutex); return 0; } +EXPORT_SYMBOL(dvbdmx_disconnect_frontend); static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 *pids) @@ -1256,6 +1261,7 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux) return 0; } +EXPORT_SYMBOL(dvb_dmx_init); int dvb_dmx_release(struct dvb_demux *dvbdemux) @@ -1269,3 +1275,5 @@ int dvb_dmx_release(struct dvb_demux *dvbdemux) vfree(dvbdemux->feed); return 0; } +EXPORT_SYMBOL(dvb_dmx_release); + diff --git a/drivers/media/dvb/dvb-core/dvb_filter.c b/drivers/media/dvb/dvb-core/dvb_filter.c index 829c43820d5c..5ce4f6569d0b 100644 --- a/drivers/media/dvb/dvb-core/dvb_filter.c +++ b/drivers/media/dvb/dvb-core/dvb_filter.c @@ -3,19 +3,20 @@ #include <linux/string.h> #include "dvb_filter.h" -unsigned int bitrates[3][16] = +#if 0 +static unsigned int bitrates[3][16] = {{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}}; +#endif -u32 freq[4] = {441, 480, 320, 0}; +static u32 freq[4] = {480, 441, 320, 0}; -unsigned int ac3_bitrates[32] = +static unsigned int ac3_bitrates[32] = {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, 0,0,0,0,0,0,0,0,0,0,0,0,0}; -u32 ac3_freq[4] = {480, 441, 320, 0}; -u32 ac3_frames[3][32] = +static u32 ac3_frames[3][32] = {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, @@ -389,6 +390,7 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p return 0; } +EXPORT_SYMBOL(dvb_filter_get_ac3info); #if 0 @@ -563,6 +565,7 @@ void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, p2ts->cb=cb; p2ts->priv=priv; } +EXPORT_SYMBOL(dvb_filter_pes2ts_init); int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, int len, int payload_start) @@ -597,4 +600,5 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, memcpy(buf+5+rest, pes, len); return p2ts->cb(p2ts->priv, buf); } +EXPORT_SYMBOL(dvb_filter_pes2ts); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index b9bc7b71a4b4..151160815e0a 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -34,6 +34,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/list.h> +#include <linux/suspend.h> #include <asm/processor.h> #include <asm/semaphore.h> @@ -42,21 +43,20 @@ static int dvb_frontend_debug; static int dvb_shutdown_timeout = 5; -static int dvb_override_frequency_bending; static int dvb_force_auto_inversion; static int dvb_override_tune_delay; -static int do_frequency_bending; +static int dvb_powerdown_on_sleep = 1; module_param_named(frontend_debug, dvb_frontend_debug, int, 0644); MODULE_PARM_DESC(dvb_frontend_debug, "Turn on/off frontend core debugging (default:off)."); module_param(dvb_shutdown_timeout, int, 0444); MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware"); -module_param(dvb_override_frequency_bending, int, 0444); -MODULE_PARM_DESC(dvb_override_frequency_bending, "0: normal (default), 1: never use frequency bending, 2: always use frequency bending"); module_param(dvb_force_auto_inversion, int, 0444); MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always"); module_param(dvb_override_tune_delay, int, 0444); MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt"); +module_param(dvb_powerdown_on_sleep, int, 0444); +MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volatage off on sleep (default)"); #define dprintk if (dvb_frontend_debug) printk @@ -87,176 +87,9 @@ MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in m * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. */ -#define MAX_EVENT 8 - -struct dvb_fe_events { - struct dvb_frontend_event events[MAX_EVENT]; - int eventw; - int eventr; - int overflow; - wait_queue_head_t wait_queue; - struct semaphore sem; -}; - - -struct dvb_frontend_data { - struct dvb_frontend_info *info; - struct dvb_frontend frontend; - struct dvb_device *dvbdev; - struct dvb_frontend_parameters parameters; - struct dvb_fe_events events; - struct module *module; - struct semaphore sem; - struct list_head list_head; - wait_queue_head_t wait_queue; - pid_t thread_pid; - unsigned long release_jiffies; - int state; - int bending; - int lnb_drift; - int inversion; - int auto_step; - int auto_sub_step; - int started_auto_step; - int min_delay; - int max_drift; - int step_size; - int exit; - int wakeup; - fe_status_t status; -}; - - -struct dvb_frontend_ioctl_data { - struct list_head list_head; - struct dvb_adapter *adapter; - int (*before_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg); - int (*after_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg); - void *before_after_data; -}; - - -struct dvb_frontend_notifier_data { - struct list_head list_head; - struct dvb_adapter *adapter; - void (*callback) (fe_status_t s, void *data); - void *data; -}; - - -static LIST_HEAD(frontend_list); -static LIST_HEAD(frontend_ioctl_list); -static LIST_HEAD(frontend_notifier_list); - static DECLARE_MUTEX(frontend_mutex); - -static int dvb_frontend_internal_ioctl (struct dvb_frontend *frontend, - unsigned int cmd, void *arg) -{ - int err = -EOPNOTSUPP; - - dprintk ("%s\n", __FUNCTION__); - - if (frontend->before_ioctl) - err = frontend->before_ioctl (frontend, cmd, arg); - - if (err == -EOPNOTSUPP) { - err = frontend->ioctl (frontend, cmd, arg); - - if ((err == -EOPNOTSUPP) && frontend->after_ioctl) - err = frontend->after_ioctl (frontend, cmd, arg); - } - - return err; -} - - -/** - * if 2 tuners are located side by side you can get interferences when - * they try to tune to the same frequency, so both lose sync. - * We will slightly mistune in this case. The AFC of the demodulator - * should make it still possible to receive the requested transponder - * on both tuners... - */ -static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive) -{ - struct list_head *entry; - int stepsize = this_fe->info->frequency_stepsize; - int this_fe_adap_num = this_fe->frontend.dvb_adapter->num; - int frequency; - - if (!stepsize || recursive > 10) { - printk ("%s: too deep recursion, check frequency_stepsize " - "in your frontend code!\n", __FUNCTION__); - return; - } - - dprintk ("%s\n", __FUNCTION__); - - if (!recursive) { - if (down_interruptible (&frontend_mutex)) - return; - - this_fe->bending = 0; - } - - list_for_each (entry, &frontend_list) { - struct dvb_frontend_data *fe; - int f; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.dvb_adapter->num != this_fe_adap_num) - continue; - - f = fe->parameters.frequency; - f += fe->lnb_drift; - f += fe->bending; - - frequency = this_fe->parameters.frequency; - frequency += this_fe->lnb_drift; - frequency += this_fe->bending; - - if (this_fe != fe && (fe->state != FESTATE_IDLE) && - frequency > f - stepsize && frequency < f + stepsize) - { - if (recursive % 2) - this_fe->bending += stepsize; - else - this_fe->bending = -this_fe->bending; - - dvb_bend_frequency (this_fe, recursive + 1); - goto done; - } - } -done: - if (!recursive) - up (&frontend_mutex); -} - - -static void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe, - fe_status_t s) -{ - dprintk ("%s\n", __FUNCTION__); - - if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK)) - msleep (fe->info->notifier_delay); - - fe->status = s; - - /** - * now tell the Demux about the TS status changes... - */ - if (fe->frontend.notifier_callback) - fe->frontend.notifier_callback(fe->status, fe->frontend.notifier_data); -} - - -static void dvb_frontend_add_event (struct dvb_frontend_data *fe, fe_status_t status) +static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) { struct dvb_fe_events *events = &fe->events; struct dvb_frontend_event *e; @@ -280,21 +113,19 @@ static void dvb_frontend_add_event (struct dvb_frontend_data *fe, fe_status_t st sizeof (struct dvb_frontend_parameters)); if (status & FE_HAS_LOCK) - dvb_frontend_internal_ioctl (&fe->frontend, - FE_GET_FRONTEND, - &e->parameters); + if (fe->ops->get_frontend) + fe->ops->get_frontend(fe, &e->parameters); + events->eventw = wp; up (&events->sem); e->status = status; - dvb_call_frontend_notifiers (fe, status); wake_up_interruptible (&events->wait_queue); } - -static int dvb_frontend_get_event (struct dvb_frontend_data *fe, +static int dvb_frontend_get_event(struct dvb_frontend *fe, struct dvb_frontend_event *event, int flags) { struct dvb_fe_events *events = &fe->events; @@ -337,15 +168,14 @@ static int dvb_frontend_get_event (struct dvb_frontend_data *fe, return 0; } -static void dvb_frontend_init (struct dvb_frontend_data *fe) +static void dvb_frontend_init(struct dvb_frontend *fe) { - struct dvb_frontend *frontend = &fe->frontend; - dprintk ("DVB: initialising frontend %i (%s)...\n", - frontend->dvb_adapter->num, - fe->info->name); + fe->dvb->num, + fe->ops->info.name); - dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL); + if (fe->ops->init) + fe->ops->init(fe); } static void update_delay (int *quality, int *delay, int min_delay, int locked) @@ -372,7 +202,7 @@ static void update_delay (int *quality, int *delay, int min_delay, int locked) * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT * @returns Number of complete iterations that have been performed. */ -static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped) +static int dvb_frontend_autotune(struct dvb_frontend *fe, int check_wrapped) { int autoinversion; int ready = 0; @@ -380,7 +210,7 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped u32 original_frequency = fe->parameters.frequency; /* are we using autoinversion? */ - autoinversion = ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && + autoinversion = ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO)); /* setup parameters correctly */ @@ -441,19 +271,18 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped return 1; } - /* perform frequency bending if necessary */ - if ((dvb_override_frequency_bending != 1) && do_frequency_bending) - dvb_bend_frequency(fe, 0); - - dprintk("%s: drift:%i bending:%i inversion:%i auto_step:%i " + dprintk("%s: drift:%i inversion:%i auto_step:%i " "auto_sub_step:%i started_auto_step:%i\n", - __FUNCTION__, fe->lnb_drift, fe->bending, fe->inversion, + __FUNCTION__, fe->lnb_drift, fe->inversion, fe->auto_step, fe->auto_sub_step, fe->started_auto_step); /* set the frontend itself */ - fe->parameters.frequency += fe->lnb_drift + fe->bending; - if (autoinversion) fe->parameters.inversion = fe->inversion; - dvb_frontend_internal_ioctl (&fe->frontend, FE_SET_FRONTEND, &fe->parameters); + fe->parameters.frequency += fe->lnb_drift; + if (autoinversion) + fe->parameters.inversion = fe->inversion; + if (fe->ops->set_frontend) + fe->ops->set_frontend(fe, &fe->parameters); + fe->parameters.frequency = original_frequency; fe->parameters.inversion = original_inversion; @@ -461,9 +290,7 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped return 0; } - - -static int dvb_frontend_is_exiting (struct dvb_frontend_data *fe) +static int dvb_frontend_is_exiting(struct dvb_frontend *fe) { if (fe->exit) return 1; @@ -475,7 +302,7 @@ static int dvb_frontend_is_exiting (struct dvb_frontend_data *fe) return 0; } -static int dvb_frontend_should_wakeup (struct dvb_frontend_data *fe) +static int dvb_frontend_should_wakeup(struct dvb_frontend *fe) { if (fe->wakeup) { fe->wakeup = 0; @@ -484,14 +311,18 @@ static int dvb_frontend_should_wakeup (struct dvb_frontend_data *fe) return dvb_frontend_is_exiting(fe); } -static void dvb_frontend_wakeup (struct dvb_frontend_data *fe) { +static void dvb_frontend_wakeup(struct dvb_frontend *fe) +{ fe->wakeup = 1; wake_up_interruptible(&fe->wait_queue); } +/* + * FIXME: use linux/kthread.h + */ static int dvb_frontend_thread (void *data) { - struct dvb_frontend_data *fe = (struct dvb_frontend_data *) data; + struct dvb_frontend *fe = (struct dvb_frontend *) data; unsigned long timeout; char name [15]; int quality = 0, delay = 3*HZ; @@ -500,27 +331,31 @@ static int dvb_frontend_thread (void *data) dprintk ("%s\n", __FUNCTION__); - snprintf (name, sizeof(name), "kdvb-fe-%i", - fe->frontend.dvb_adapter->num); + snprintf (name, sizeof(name), "kdvb-fe-%i", fe->dvb->num); lock_kernel (); daemonize (name); sigfillset (¤t->blocked); unlock_kernel (); - dvb_call_frontend_notifiers (fe, 0); + fe->status = 0; dvb_frontend_init (fe); fe->wakeup = 0; while (1) { up (&fe->sem); /* is locked when we enter the thread... */ - timeout = wait_event_interruptible_timeout(fe->wait_queue,0 != dvb_frontend_should_wakeup (fe), delay); - if (-ERESTARTSYS == timeout || 0 != dvb_frontend_is_exiting (fe)) { + timeout = wait_event_interruptible_timeout(fe->wait_queue, + dvb_frontend_should_wakeup(fe), + delay); + if (0 != dvb_frontend_is_exiting (fe)) { /* got signal or quitting */ break; } + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + if (down_interruptible (&fe->sem)) break; @@ -531,13 +366,16 @@ static int dvb_frontend_thread (void *data) continue; } +retune: /* get the frontend status */ if (fe->state & FESTATE_RETUNE) { s = 0; } else { - dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s); + if (fe->ops->read_status) + fe->ops->read_status(fe, &s); if (s != fe->status) { dvb_frontend_add_event (fe, s); + fe->status = s; } } /* if we're not tuned, and we have a lock, move to the TUNED state */ @@ -546,7 +384,7 @@ static int dvb_frontend_thread (void *data) fe->state = FESTATE_TUNED; /* if we're tuned, then we have determined the correct inversion */ - if ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && + if ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO)) { fe->parameters.inversion = fe->inversion; } @@ -572,7 +410,7 @@ static int dvb_frontend_thread (void *data) /* don't actually do anything if we're in the LOSTLOCK state, * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */ if ((fe->state & FESTATE_LOSTLOCK) && - (fe->info->caps & FE_CAN_RECOVER) && (fe->max_drift == 0)) { + (fe->ops->info.caps & FE_CAN_RECOVER) && (fe->max_drift == 0)) { update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); continue; } @@ -616,7 +454,7 @@ static int dvb_frontend_thread (void *data) * occurs */ if (fe->state & FESTATE_RETUNE) { fe->state = FESTATE_TUNING_FAST; - wake_up_interruptible(&fe->wait_queue); + goto retune; } } @@ -628,10 +466,15 @@ static int dvb_frontend_thread (void *data) * state until we get a lock */ dvb_frontend_autotune(fe, 0); } - }; + } - if (dvb_shutdown_timeout) - dvb_frontend_internal_ioctl (&fe->frontend, FE_SLEEP, NULL); + if (dvb_shutdown_timeout) { + if (dvb_powerdown_on_sleep) + if (fe->ops->set_voltage) + fe->ops->set_voltage(fe, SEC_VOLTAGE_OFF); + if (fe->ops->sleep) + fe->ops->sleep(fe); + } fe->thread_pid = 0; mb(); @@ -640,8 +483,7 @@ static int dvb_frontend_thread (void *data) return 0; } - -static void dvb_frontend_stop (struct dvb_frontend_data *fe) +static void dvb_frontend_stop(struct dvb_frontend *fe) { unsigned long ret; @@ -679,8 +521,7 @@ static void dvb_frontend_stop (struct dvb_frontend_data *fe) fe->thread_pid); } - -static int dvb_frontend_start (struct dvb_frontend_data *fe) +static int dvb_frontend_start(struct dvb_frontend *fe) { int ret; @@ -719,13 +560,12 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, unsigned int cmd, void *parg) { struct dvb_device *dvbdev = file->private_data; - struct dvb_frontend_data *fe = dvbdev->priv; - struct dvb_frontend_tune_settings fetunesettings; - int err = 0; + struct dvb_frontend *fe = dvbdev->priv; + int err = -EOPNOTSUPP; dprintk ("%s\n", __FUNCTION__); - if (!fe || !fe->frontend.ioctl || fe->exit) + if (!fe || fe->exit) return -ENODEV; if ((file->f_flags & O_ACCMODE) == O_RDONLY && @@ -737,18 +577,103 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, return -ERESTARTSYS; switch (cmd) { + case FE_GET_INFO: { + struct dvb_frontend_info* info = (struct dvb_frontend_info*) parg; + memcpy(info, &fe->ops->info, sizeof(struct dvb_frontend_info)); + + /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't + * do it, it is done for it. */ + info->caps |= FE_CAN_INVERSION_AUTO; + err = 0; + break; + } + + case FE_READ_STATUS: + if (fe->ops->read_status) + err = fe->ops->read_status(fe, (fe_status_t*) parg); + break; + + case FE_READ_BER: + if (fe->ops->read_ber) + err = fe->ops->read_ber(fe, (__u32*) parg); + break; + + case FE_READ_SIGNAL_STRENGTH: + if (fe->ops->read_signal_strength) + err = fe->ops->read_signal_strength(fe, (__u16*) parg); + break; + + case FE_READ_SNR: + if (fe->ops->read_snr) + err = fe->ops->read_snr(fe, (__u16*) parg); + break; + + case FE_READ_UNCORRECTED_BLOCKS: + if (fe->ops->read_ucblocks) + err = fe->ops->read_ucblocks(fe, (__u32*) parg); + break; + + + case FE_DISEQC_RESET_OVERLOAD: + if (fe->ops->diseqc_reset_overload) { + err = fe->ops->diseqc_reset_overload(fe); + fe->state = FESTATE_DISEQC; + fe->status = 0; + } + break; + case FE_DISEQC_SEND_MASTER_CMD: + if (fe->ops->diseqc_send_master_cmd) { + err = fe->ops->diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg); + fe->state = FESTATE_DISEQC; + fe->status = 0; + } + break; + case FE_DISEQC_SEND_BURST: + if (fe->ops->diseqc_send_burst) { + err = fe->ops->diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg); + fe->state = FESTATE_DISEQC; + fe->status = 0; + } + break; + case FE_SET_TONE: + if (fe->ops->set_tone) { + err = fe->ops->set_tone(fe, (fe_sec_tone_mode_t) parg); + fe->state = FESTATE_DISEQC; + fe->status = 0; + } + break; + case FE_SET_VOLTAGE: - if (fe->status) - dvb_call_frontend_notifiers (fe, 0); - dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg); + if (fe->ops->set_voltage) { + err = fe->ops->set_voltage(fe, (fe_sec_voltage_t) parg); fe->state = FESTATE_DISEQC; + fe->status = 0; + } break; - case FE_SET_FRONTEND: - fe->state = FESTATE_RETUNE; + case FE_DISHNETWORK_SEND_LEGACY_CMD: + if (fe->ops->dishnetwork_send_legacy_command) { + err = fe->ops->dishnetwork_send_legacy_command(fe, (unsigned int) parg); + fe->state = FESTATE_DISEQC; + fe->status = 0; + } + break; + + case FE_DISEQC_RECV_SLAVE_REPLY: + if (fe->ops->diseqc_recv_slave_reply) + err = fe->ops->diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg); + break; + + case FE_ENABLE_HIGH_LNB_VOLTAGE: + if (fe->ops->enable_high_lnb_voltage); + err = fe->ops->enable_high_lnb_voltage(fe, (int) parg); + break; + + case FE_SET_FRONTEND: { + struct dvb_frontend_tune_settings fetunesettings; memcpy (&fe->parameters, parg, sizeof (struct dvb_frontend_parameters)); @@ -762,7 +687,7 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, fe->parameters.inversion = INVERSION_AUTO; fetunesettings.parameters.inversion = INVERSION_AUTO; } - if (fe->info->type == FE_OFDM) { + if (fe->ops->info.type == FE_OFDM) { /* without hierachical coding code_rate_LP is irrelevant, * so we tolerate the otherwise invalid FEC_NONE setting */ if (fe->parameters.u.ofdm.hierarchy_information == HIERARCHY_NONE && @@ -771,14 +696,13 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, } /* get frontend-specific tuning settings */ - if (dvb_frontend_internal_ioctl(&fe->frontend, FE_GET_TUNE_SETTINGS, - &fetunesettings) == 0) { + if (fe->ops->get_tune_settings && (fe->ops->get_tune_settings(fe, &fetunesettings) == 0)) { fe->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000; fe->max_drift = fetunesettings.max_drift; fe->step_size = fetunesettings.step_size; } else { /* default values */ - switch(fe->info->type) { + switch(fe->ops->info.type) { case FE_QPSK: fe->min_delay = HZ/20; fe->step_size = fe->parameters.u.qpsk.symbol_rate / 16000; @@ -793,44 +717,38 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, case FE_OFDM: fe->min_delay = HZ/20; - fe->step_size = fe->info->frequency_stepsize * 2; - fe->max_drift = (fe->info->frequency_stepsize * 2) + 1; + fe->step_size = fe->ops->info.frequency_stepsize * 2; + fe->max_drift = (fe->ops->info.frequency_stepsize * 2) + 1; break; case FE_ATSC: printk("dvb-core: FE_ATSC not handled yet.\n"); break; } } - if (dvb_override_tune_delay > 0) { + if (dvb_override_tune_delay > 0) fe->min_delay = (dvb_override_tune_delay * HZ) / 1000; - } + fe->state = FESTATE_RETUNE; dvb_frontend_wakeup(fe); dvb_frontend_add_event (fe, 0); + fe->status = 0; + err = 0; break; + } case FE_GET_EVENT: err = dvb_frontend_get_event (fe, parg, file->f_flags); break; + case FE_GET_FRONTEND: - memcpy (parg, &fe->parameters, - sizeof (struct dvb_frontend_parameters)); - /* fall-through... */ - default: - err = dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg); + if (fe->ops->get_frontend) { + memcpy (parg, &fe->parameters, sizeof (struct dvb_frontend_parameters)); + err = fe->ops->get_frontend(fe, (struct dvb_frontend_parameters*) parg); + } + break; }; up (&fe->sem); - if (err < 0) - return err; - - /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't - * do it, it is done for it. */ - if ((cmd == FE_GET_INFO) && (err == 0)) { - struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) parg; - tmp->caps |= FE_CAN_INVERSION_AUTO; - } - return err; } @@ -838,7 +756,7 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, static unsigned int dvb_frontend_poll (struct file *file, struct poll_table_struct *wait) { struct dvb_device *dvbdev = file->private_data; - struct dvb_frontend_data *fe = dvbdev->priv; + struct dvb_frontend *fe = dvbdev->priv; dprintk ("%s\n", __FUNCTION__); @@ -854,7 +772,7 @@ static unsigned int dvb_frontend_poll (struct file *file, struct poll_table_stru static int dvb_frontend_open (struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; - struct dvb_frontend_data *fe = dvbdev->priv; + struct dvb_frontend *fe = dvbdev->priv; int ret; dprintk ("%s\n", __FUNCTION__); @@ -871,11 +789,6 @@ static int dvb_frontend_open (struct inode *inode, struct file *file) fe->events.eventr = fe->events.eventw = 0; } - if (!ret && fe->module) { - if (!try_module_get(fe->module)) - return -EINVAL; - } - return ret; } @@ -883,207 +796,14 @@ static int dvb_frontend_open (struct inode *inode, struct file *file) static int dvb_frontend_release (struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; - struct dvb_frontend_data *fe = dvbdev->priv; - int ret = 0; + struct dvb_frontend *fe = dvbdev->priv; dprintk ("%s\n", __FUNCTION__); if ((file->f_flags & O_ACCMODE) != O_RDONLY) fe->release_jiffies = jiffies; - ret = dvb_generic_release (inode, file); - - if (!ret && fe->module) - module_put(fe->module); - - return ret; -} - - - -int -dvb_add_frontend_ioctls (struct dvb_adapter *adapter, - int (*before_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - int (*after_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - void *before_after_data) -{ - struct dvb_frontend_ioctl_data *ioctl; - struct list_head *entry; - - dprintk ("%s\n", __FUNCTION__); - - if (down_interruptible (&frontend_mutex)) - return -ERESTARTSYS; - - ioctl = kmalloc (sizeof(struct dvb_frontend_ioctl_data), GFP_KERNEL); - - if (!ioctl) { - up (&frontend_mutex); - return -ENOMEM; - } - - ioctl->adapter = adapter; - ioctl->before_ioctl = before_ioctl; - ioctl->after_ioctl = after_ioctl; - ioctl->before_after_data = before_after_data; - - list_add_tail (&ioctl->list_head, &frontend_ioctl_list); - - list_for_each (entry, &frontend_list) { - struct dvb_frontend_data *fe; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.dvb_adapter == adapter && - fe->frontend.before_ioctl == NULL && - fe->frontend.after_ioctl == NULL) - { - fe->frontend.before_ioctl = before_ioctl; - fe->frontend.after_ioctl = after_ioctl; - fe->frontend.before_after_data = before_after_data; - } - } - - up (&frontend_mutex); - - return 0; -} - - -void -dvb_remove_frontend_ioctls (struct dvb_adapter *adapter, - int (*before_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - int (*after_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg)) -{ - struct list_head *entry, *n; - - dprintk ("%s\n", __FUNCTION__); - - down (&frontend_mutex); - - list_for_each (entry, &frontend_list) { - struct dvb_frontend_data *fe; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.dvb_adapter == adapter && - fe->frontend.before_ioctl == before_ioctl && - fe->frontend.after_ioctl == after_ioctl) - { - fe->frontend.before_ioctl = NULL; - fe->frontend.after_ioctl = NULL; - - } - } - - list_for_each_safe (entry, n, &frontend_ioctl_list) { - struct dvb_frontend_ioctl_data *ioctl; - - ioctl = list_entry (entry, struct dvb_frontend_ioctl_data, list_head); - - if (ioctl->adapter == adapter && - ioctl->before_ioctl == before_ioctl && - ioctl->after_ioctl == after_ioctl) - { - list_del (&ioctl->list_head); - kfree (ioctl); - - break; - } - } - - up (&frontend_mutex); -} - - -int -dvb_add_frontend_notifier (struct dvb_adapter *adapter, - void (*callback) (fe_status_t s, void *data), - void *data) -{ - struct dvb_frontend_notifier_data *notifier; - struct list_head *entry; - - dprintk ("%s\n", __FUNCTION__); - - if (down_interruptible (&frontend_mutex)) - return -ERESTARTSYS; - - notifier = kmalloc (sizeof(struct dvb_frontend_notifier_data), GFP_KERNEL); - - if (!notifier) { - up (&frontend_mutex); - return -ENOMEM; - } - - notifier->adapter = adapter; - notifier->callback = callback; - notifier->data = data; - - list_add_tail (¬ifier->list_head, &frontend_notifier_list); - - list_for_each (entry, &frontend_list) { - struct dvb_frontend_data *fe; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.dvb_adapter == adapter && - fe->frontend.notifier_callback == NULL) - { - fe->frontend.notifier_callback = callback; - fe->frontend.notifier_data = data; - } - } - - up (&frontend_mutex); - - return 0; -} - - -void -dvb_remove_frontend_notifier (struct dvb_adapter *adapter, - void (*callback) (fe_status_t s, void *data)) -{ - struct list_head *entry, *n; - - dprintk ("%s\n", __FUNCTION__); - - down (&frontend_mutex); - - list_for_each (entry, &frontend_list) { - struct dvb_frontend_data *fe; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.dvb_adapter == adapter && - fe->frontend.notifier_callback == callback) - { - fe->frontend.notifier_callback = NULL; - - } - } - - list_for_each_safe (entry, n, &frontend_notifier_list) { - struct dvb_frontend_notifier_data *notifier; - - notifier = list_entry (entry, struct dvb_frontend_notifier_data, list_head); - - if (notifier->adapter == adapter && - notifier->callback == callback) - { - list_del (¬ifier->list_head); - kfree (notifier); - - break; - } - } - - up (&frontend_mutex); + return dvb_generic_release (inode, file); } @@ -1095,18 +815,9 @@ static struct file_operations dvb_frontend_fops = { .release = dvb_frontend_release }; - - -int -dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - struct dvb_adapter *dvb_adapter, - void *data, - struct dvb_frontend_info *info, - struct module *module) +int dvb_register_frontend(struct dvb_adapter* dvb, + struct dvb_frontend* fe) { - struct list_head *entry; - struct dvb_frontend_data *fe; static const struct dvb_device dvbdev_template = { .users = ~0, .writers = 1, @@ -1120,99 +831,39 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, if (down_interruptible (&frontend_mutex)) return -ERESTARTSYS; - if (!(fe = kmalloc (sizeof (struct dvb_frontend_data), GFP_KERNEL))) { - up (&frontend_mutex); - return -ENOMEM; - } - - memset (fe, 0, sizeof (struct dvb_frontend_data)); - init_MUTEX (&fe->sem); init_waitqueue_head (&fe->wait_queue); init_waitqueue_head (&fe->events.wait_queue); init_MUTEX (&fe->events.sem); fe->events.eventw = fe->events.eventr = 0; fe->events.overflow = 0; - fe->module = module; - - fe->frontend.ioctl = ioctl; - fe->frontend.dvb_adapter = dvb_adapter; - fe->frontend.data = data; - fe->info = info; + fe->dvb = dvb; fe->inversion = INVERSION_OFF; - list_for_each (entry, &frontend_ioctl_list) { - struct dvb_frontend_ioctl_data *ioctl; - - ioctl = list_entry (entry, - struct dvb_frontend_ioctl_data, - list_head); - - if (ioctl->adapter == dvb_adapter) { - fe->frontend.before_ioctl = ioctl->before_ioctl; - fe->frontend.after_ioctl = ioctl->after_ioctl; - fe->frontend.before_after_data = ioctl->before_after_data; - break; - } - } - - list_for_each (entry, &frontend_notifier_list) { - struct dvb_frontend_notifier_data *notifier; - - notifier = list_entry (entry, - struct dvb_frontend_notifier_data, - list_head); - - if (notifier->adapter == dvb_adapter) { - fe->frontend.notifier_callback = notifier->callback; - fe->frontend.notifier_data = notifier->data; - break; - } - } - - list_add_tail (&fe->list_head, &frontend_list); - printk ("DVB: registering frontend %i (%s)...\n", - fe->frontend.dvb_adapter->num, - fe->info->name); + fe->dvb->num, + fe->ops->info.name); - dvb_register_device (dvb_adapter, &fe->dvbdev, &dvbdev_template, + dvb_register_device (fe->dvb, &fe->dvbdev, &dvbdev_template, fe, DVB_DEVICE_FRONTEND); - if ((info->caps & FE_NEEDS_BENDING) || (dvb_override_frequency_bending == 2)) - do_frequency_bending = 1; - up (&frontend_mutex); - return 0; } +EXPORT_SYMBOL(dvb_register_frontend); -int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - struct dvb_adapter *dvb_adapter) +int dvb_unregister_frontend(struct dvb_frontend* fe) { - struct list_head *entry, *n; - dprintk ("%s\n", __FUNCTION__); down (&frontend_mutex); - - list_for_each_safe (entry, n, &frontend_list) { - struct dvb_frontend_data *fe; - - fe = list_entry (entry, struct dvb_frontend_data, list_head); - - if (fe->frontend.ioctl == ioctl && fe->frontend.dvb_adapter == dvb_adapter) { dvb_unregister_device (fe->dvbdev); - list_del (entry); - up (&frontend_mutex); dvb_frontend_stop (fe); - kfree (fe); - return 0; - } - } - + if (fe->ops->release) + fe->ops->release(fe); + else + printk("dvb_frontend: Demodulator (%s) does not have a release callback!\n", fe->ops->info.name); up (&frontend_mutex); - return -EINVAL; + return 0; } - +EXPORT_SYMBOL(dvb_unregister_frontend); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 5d0f5dffbbfa..038abac17b8d 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -1,9 +1,12 @@ /* * dvb_frontend.h * - * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH - * overhauled by Holger Waechtler for Convergence GmbH + * Copyright (C) 2001 convergence integrated media GmbH + * Copyright (C) 2004 convergence GmbH * + * Written by Ralph Metzler + * Overhauled by Holger Waechtler + * Kernel I2C stuff by Michael Hunold <hunold@convergence.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -38,8 +41,8 @@ #include "dvbdev.h" /* FIXME: Move to i2c-id.h */ -#define I2C_DRIVERID_DVBFE_ALPS_TDLB7 I2C_DRIVERID_EXP2 -#define I2C_DRIVERID_DVBFE_ALPS_TDMB7 I2C_DRIVERID_EXP2 +#define I2C_DRIVERID_DVBFE_SP8870 I2C_DRIVERID_EXP2 +#define I2C_DRIVERID_DVBFE_CX22700 I2C_DRIVERID_EXP2 #define I2C_DRIVERID_DVBFE_AT76C651 I2C_DRIVERID_EXP2 #define I2C_DRIVERID_DVBFE_CX24110 I2C_DRIVERID_EXP2 #define I2C_DRIVERID_DVBFE_CX22702 I2C_DRIVERID_EXP2 @@ -56,22 +59,8 @@ #define I2C_DRIVERID_DVBFE_TDA8083 I2C_DRIVERID_EXP2 #define I2C_DRIVERID_DVBFE_VES1820 I2C_DRIVERID_EXP2 #define I2C_DRIVERID_DVBFE_VES1X93 I2C_DRIVERID_EXP2 +#define I2C_DRIVERID_DVBFE_TDA80XX I2C_DRIVERID_EXP2 -/** - * when before_ioctl is registered and returns value 0, ioctl and after_ioctl - * are not executed. - */ - -struct dvb_frontend { - int (*before_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); - int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); - int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); - void (*notifier_callback) (fe_status_t s, void *data); - struct dvb_adapter *dvb_adapter; - void *before_after_data; /* can be used by hardware module... */ - void *notifier_data; /* can be used by hardware module... */ - void *data; /* can be used by hardware module... */ -}; struct dvb_frontend_tune_settings { int min_delay_ms; @@ -80,67 +69,79 @@ struct dvb_frontend_tune_settings { struct dvb_frontend_parameters parameters; }; +struct dvb_frontend; -/** - * private frontend command ioctl's. - * keep them in sync with the public ones defined in linux/dvb/frontend.h - * - * FE_SLEEP. Ioctl used to put frontend into a low power mode. - * FE_INIT. Ioctl used to initialise the frontend. - * FE_GET_TUNE_SETTINGS. Get the frontend-specific tuning loop settings for the supplied set of parameters. - */ -#define FE_SLEEP _IO('v', 80) -#define FE_INIT _IO('v', 81) -#define FE_GET_TUNE_SETTINGS _IOWR('v', 83, struct dvb_frontend_tune_settings) -#define FE_REGISTER _IO ('v', 84) -#define FE_UNREGISTER _IO ('v', 85) - -extern int -dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - struct dvb_adapter *dvb_adapter, - void *data, - struct dvb_frontend_info *info, - struct module *module); - -extern int -dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - struct dvb_adapter *dvb_adapter); - - -/** - * Add special ioctl code performed before and after the main ioctl - * to all frontend devices on the specified DVB adapter. - * This is necessairy because the 22kHz/13V-18V/DiSEqC stuff depends - * heavily on the hardware around the frontend, the same tuner can create - * these signals on about a million different ways... - * - * Return value: number of frontends where the ioctl's were applied. - */ -extern int -dvb_add_frontend_ioctls (struct dvb_adapter *adapter, - int (*before_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - int (*after_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - void *before_after_data); - - -extern void -dvb_remove_frontend_ioctls (struct dvb_adapter *adapter, - int (*before_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg), - int (*after_ioctl) (struct dvb_frontend *frontend, - unsigned int cmd, void *arg)); - -extern int -dvb_add_frontend_notifier (struct dvb_adapter *adapter, - void (*callback) (fe_status_t s, void *data), - void *data); -extern void -dvb_remove_frontend_notifier (struct dvb_adapter *adapter, - void (*callback) (fe_status_t s, void *data)); +struct dvb_frontend_ops { -#endif + struct dvb_frontend_info info; + + void (*release)(struct dvb_frontend* fe); + + int (*init)(struct dvb_frontend* fe); + int (*sleep)(struct dvb_frontend* fe); + + int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + int (*get_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings); + + int (*read_status)(struct dvb_frontend* fe, fe_status_t* status); + int (*read_ber)(struct dvb_frontend* fe, u32* ber); + int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength); + int (*read_snr)(struct dvb_frontend* fe, u16* snr); + int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks); + int (*diseqc_reset_overload)(struct dvb_frontend* fe); + int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); + int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply); + int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); + int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); + int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, int arg); + int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd); +}; + +#define MAX_EVENT 8 + +struct dvb_fe_events { + struct dvb_frontend_event events[MAX_EVENT]; + int eventw; + int eventr; + int overflow; + wait_queue_head_t wait_queue; + struct semaphore sem; +}; + +struct dvb_frontend { + struct dvb_frontend_ops* ops; + struct dvb_adapter *dvb; + void* demodulator_priv; + + struct dvb_device *dvbdev; + struct dvb_frontend_parameters parameters; + struct dvb_fe_events events; + struct semaphore sem; + struct list_head list_head; + wait_queue_head_t wait_queue; + pid_t thread_pid; + unsigned long release_jiffies; + int state; + int bending; + int lnb_drift; + int inversion; + int auto_step; + int auto_sub_step; + int started_auto_step; + int min_delay; + int max_drift; + int step_size; + int exit; + int wakeup; + fe_status_t status; +}; + +extern int dvb_register_frontend(struct dvb_adapter* dvb, + struct dvb_frontend* fe); + +extern int dvb_unregister_frontend(struct dvb_frontend* fe); + +#endif diff --git a/drivers/media/dvb/dvb-core/dvb_ksyms.c b/drivers/media/dvb/dvb-core/dvb_ksyms.c deleted file mode 100644 index 553200e9e06d..000000000000 --- a/drivers/media/dvb/dvb-core/dvb_ksyms.c +++ /dev/null @@ -1,52 +0,0 @@ -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/ioctl.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <asm/uaccess.h> - -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" -#include "dvb_filter.h" -#include "dvb_ca_en50221.h" - -EXPORT_SYMBOL(dvb_dmxdev_init); -EXPORT_SYMBOL(dvb_dmxdev_release); -EXPORT_SYMBOL(dvb_dmx_init); -EXPORT_SYMBOL(dvb_dmx_release); -EXPORT_SYMBOL(dvb_dmx_swfilter_packet); -EXPORT_SYMBOL(dvb_dmx_swfilter_packets); -EXPORT_SYMBOL(dvb_dmx_swfilter); -EXPORT_SYMBOL(dvb_dmx_swfilter_204); -EXPORT_SYMBOL(dvbdmx_connect_frontend); -EXPORT_SYMBOL(dvbdmx_disconnect_frontend); - -EXPORT_SYMBOL(dvb_register_frontend); -EXPORT_SYMBOL(dvb_unregister_frontend); -EXPORT_SYMBOL(dvb_add_frontend_ioctls); -EXPORT_SYMBOL(dvb_remove_frontend_ioctls); -EXPORT_SYMBOL(dvb_add_frontend_notifier); -EXPORT_SYMBOL(dvb_remove_frontend_notifier); - -EXPORT_SYMBOL(dvb_net_init); -EXPORT_SYMBOL(dvb_net_release); - -EXPORT_SYMBOL(dvb_register_adapter); -EXPORT_SYMBOL(dvb_unregister_adapter); -EXPORT_SYMBOL(dvb_register_device); -EXPORT_SYMBOL(dvb_unregister_device); -EXPORT_SYMBOL(dvb_generic_ioctl); -EXPORT_SYMBOL(dvb_generic_open); -EXPORT_SYMBOL(dvb_generic_release); - -EXPORT_SYMBOL(dvb_filter_pes2ts_init); -EXPORT_SYMBOL(dvb_filter_pes2ts); -EXPORT_SYMBOL(dvb_filter_get_ac3info); - -EXPORT_SYMBOL(dvb_ca_en50221_init); -EXPORT_SYMBOL(dvb_ca_en50221_release); -EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); -EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); -EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index e229215883c1..805cf3c5dbcb 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -6,13 +6,13 @@ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> * * ULE Decapsulation code: - * Copyright (C) 2003 gcs - Global Communication & Services GmbH. - * and Institute for Computer Sciences - * Salzburg University. + * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH. + * and Department of Scientific Computing + * Paris Lodron University of Salzburg. * Hilmar Linder <hlinder@cosy.sbg.ac.at> * and Wolfram Stering <wstering@cosy.sbg.ac.at> * - * ULE Decaps according to draft-fair-ipdvb-ule-01.txt. + * ULE Decaps according to draft-ietf-ipdvb-ule-03.txt. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +30,31 @@ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */ +/* + * ULE ChangeLog: + * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt + * + * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt: + * ULE Extension header handling. + * Bugreports by Moritz Vieth and Hanno Tersteegen, + * Fraunhofer Institute for Open Communication Systems + * Competence Center for Advanced Satellite Communications. + * Bugfixes and robustness improvements. + * Filtering on dest MAC addresses, if present (D-Bit = 0) + * ULE_DEBUG compile-time option. + */ + +/* + * FIXME / TODO (dvb_net.c): + * + * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero. + * + * TS_FEED callback is called once for every single TS cell although it is + * registered (in dvb_net_feed_start()) for 100 TS cells (used for dvb_net_ule()). + * + */ + +#include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -60,6 +85,10 @@ static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) #define DVB_NET_MULTICAST_MAX 10 +#undef ULE_DEBUG + +#ifdef ULE_DEBUG + #define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) static void hexdump( const unsigned char *buf, unsigned short len ) @@ -89,6 +118,7 @@ static void hexdump( const unsigned char *buf, unsigned short len ) } } +#endif struct dvb_net_priv { int in_use; @@ -110,18 +140,19 @@ struct dvb_net_priv { #define RX_MODE_PROMISC 3 struct work_struct set_multicast_list_wq; struct work_struct restart_net_feed_wq; - unsigned char feedtype; - int need_pusi; - unsigned char tscc; /* TS continuity counter after sync. */ - struct sk_buff *ule_skb; - unsigned short ule_sndu_len; - unsigned short ule_sndu_type; - unsigned char ule_sndu_type_1; - unsigned char ule_dbit; /* whether the DestMAC address present - * bit is set or not. */ - unsigned char ule_ethhdr_complete; /* whether we have completed the Ethernet - * header for the current ULE SNDU. */ - int ule_sndu_remain; + unsigned char feedtype; /* Either FEED_TYPE_ or FEED_TYPE_ULE */ + int need_pusi; /* Set to 1, if synchronization on PUSI required. */ + unsigned char tscc; /* TS continuity counter after sync on PUSI. */ + struct sk_buff *ule_skb; /* ULE SNDU decodes into this buffer. */ + unsigned char *ule_next_hdr; /* Pointer into skb to next ULE extension header. */ + unsigned short ule_sndu_len; /* ULE SNDU length in bytes, w/o D-Bit. */ + unsigned short ule_sndu_type; /* ULE SNDU type field, complete. */ + unsigned char ule_sndu_type_1; /* ULE SNDU type field, if split across 2 TS cells. */ + unsigned char ule_dbit; /* Whether the DestMAC address present + * or not (bit is set). */ + unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */ + int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */ + unsigned long ts_count; /* Current ts cell counter. */ }; @@ -141,7 +172,11 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, skb->mac.raw=skb->data; skb_pull(skb,dev->hard_header_len); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8) + eth = skb->mac.ethernet; +#else eth = eth_hdr(skb); +#endif if (*eth->h_dest & 1) { if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) @@ -173,78 +208,194 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, #define TS_SZ 188 #define TS_SYNC 0x47 #define TS_TEI 0x80 +#define TS_SC 0xC0 #define TS_PUSI 0x40 #define TS_AF_A 0x20 #define TS_AF_D 0x10 +/* ULE Extension Header handlers. */ + #define ULE_TEST 0 #define ULE_BRIDGED 1 -#define ULE_LLC 2 +int ule_test_sndu( struct dvb_net_priv *p ) +{ + return -1; +} + +int ule_bridged_sndu( struct dvb_net_priv *p ) +{ + /* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt. + * This has to be the last extension header, otherwise it won't work. + * Blame the authors! + */ + p->ule_bridged = 1; + return 0; +} + + +/** Handle ULE extension headers. + * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding. + * Returns: >= 0: nr. of bytes consumed by next extension header + * -1: Mandatory extension header that is not recognized or TEST SNDU; discard. + */ +static int handle_one_ule_extension( struct dvb_net_priv *p ) +{ + /* Table of mandatory extension header handlers. The header type is the index. */ + static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) = + { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, }; + + /* Table of optional extension header handlers. The header type is the index. */ + static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = { NULL, }; + + int ext_len = 0; + unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8; + unsigned char htype = p->ule_sndu_type & 0x00FF; + + /* Discriminate mandatory and optional extension headers. */ + if (hlen == 0) { + /* Mandatory extension header */ + if (ule_mandatory_ext_handlers[htype]) { + ext_len = ule_mandatory_ext_handlers[htype]( p ); + p->ule_next_hdr += ext_len; + if (! p->ule_bridged) { + p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); + p->ule_next_hdr += 2; + } else { + p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)) ); + /* This assures the extension handling loop will terminate. */ + } + } else + ext_len = -1; /* SNDU has to be discarded. */ + } else { + /* Optional extension header. Calculate the length. */ + ext_len = hlen << 2; + /* Process the optional extension header according to its type. */ + if (ule_optional_ext_handlers[htype]) + (void)ule_optional_ext_handlers[htype]( p ); + p->ule_next_hdr += ext_len; + p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); + p->ule_next_hdr += 2; + } + + return ext_len; +} + +static int handle_ule_extensions( struct dvb_net_priv *p ) +{ + int total_ext_len = 0, l; + + p->ule_next_hdr = p->ule_skb->data; + do { + l = handle_one_ule_extension( p ); + if (l == -1) return -1; /* Stop extension header processing and discard SNDU. */ + total_ext_len += l; + + } while (p->ule_sndu_type < 1536); + + return total_ext_len; +} + + +/** Prepare for a new ULE SNDU: reset the decoder state. */ static inline void reset_ule( struct dvb_net_priv *p ) { p->ule_skb = NULL; + p->ule_next_hdr = NULL; p->ule_sndu_len = 0; p->ule_sndu_type = 0; p->ule_sndu_type_1 = 0; p->ule_sndu_remain = 0; p->ule_dbit = 0xFF; - p->ule_ethhdr_complete = 0; + p->ule_bridged = 0; } -static const char eth_dest_addr[] = { 0x0b, 0x0a, 0x09, 0x08, 0x04, 0x03 }; - +/** + * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of + * TS cells of a single PID. + */ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) { struct dvb_net_priv *priv = (struct dvb_net_priv *)dev->priv; - unsigned long skipped = 0L, skblen = 0L; + unsigned long skipped = 0L; u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1; struct ethhdr *ethh = NULL; - unsigned int emergency_count = 0; + +#ifdef ULE_DEBUG + /* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */ + static unsigned char ule_hist[100*TS_SZ]; + static unsigned char *ule_where = ule_hist, ule_dump = 0; +#endif if (dev == NULL) { printk( KERN_ERR "NO netdev struct!\n" ); return; } - for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; ) { + /* For all TS cells in current buffer. + * Appearently, we are called for every single TS cell. + */ + for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; /* no default incr. */ ) { - if (emergency_count++ > 200) { - /* Huh?? */ - hexdump(ts, TS_SZ); - printk(KERN_WARNING "*** LOOP ALERT! ts %p ts_remain %u " - "how_much %u, ule_skb %p, ule_len %u, ule_remain %u\n", - ts, ts_remain, how_much, priv->ule_skb, - priv->ule_sndu_len, priv->ule_sndu_remain); - break; + if (new_ts) { + /* We are about to process a new TS cell. */ + +#ifdef ULE_DEBUG + if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist; + memcpy( ule_where, ts, TS_SZ ); + if (ule_dump) { + hexdump( ule_where, TS_SZ ); + ule_dump = 0; } + ule_where += TS_SZ; +#endif - if (new_ts) { - if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI)) { - printk(KERN_WARNING "Invalid TS cell: SYNC %#x, TEI %u.\n", - ts[0], ts[1] & TS_TEI >> 7); + /* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */ + if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) { + printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n", + priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6); + + /* Drop partly decoded SNDU, reset state, resync on PUSI. */ + if (priv->ule_skb) { + dev_kfree_skb( priv->ule_skb ); + /* Prepare for next SNDU. */ + ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; + ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; + } + reset_ule(priv); + priv->need_pusi = 1; + + /* Continue with next TS cell. */ + ts += TS_SZ; + priv->ts_count++; continue; } + ts_remain = 184; from_where = ts + 4; } /* Synchronize on PUSI, if required. */ if (priv->need_pusi) { if (ts[1] & TS_PUSI) { - /* Find beginning of first ULE SNDU in current TS cell. - * priv->need_pusi = 0; */ + /* Find beginning of first ULE SNDU in current TS cell. */ + /* Synchronize continuity counter. */ priv->tscc = ts[3] & 0x0F; /* There is a pointer field here. */ if (ts[4] > ts_remain) { - printk(KERN_ERR "Invalid ULE packet " - "(pointer field %d)\n", ts[4]); + printk(KERN_ERR "%lu: Invalid ULE packet " + "(pointer field %d)\n", priv->ts_count, ts[4]); + ts += TS_SZ; + priv->ts_count++; continue; } + /* Skip to destination of pointer field. */ from_where = &ts[5] + ts[4]; ts_remain -= 1 + ts[4]; skipped = 0; } else { skipped++; + ts += TS_SZ; + priv->ts_count++; continue; } } @@ -255,32 +406,45 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) priv->tscc = (priv->tscc + 1) & 0x0F; else { /* TS discontinuity handling: */ + printk(KERN_WARNING "%lu: TS discontinuity: got %#x, " + "exptected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); + /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ - reset_ule(priv); + // reset_ule(priv); moved to below. ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; } + reset_ule(priv); /* skip to next PUSI. */ - printk(KERN_WARNING "TS discontinuity: got %#x, " - "exptected %#x.\n", ts[3] & 0x0F, priv->tscc); priv->need_pusi = 1; + ts += TS_SZ; + priv->ts_count++; continue; } /* If we still have an incomplete payload, but PUSI is - * set, some TS cells are missing. + * set; some TS cells are missing. * This is only possible here, if we missed exactly 16 TS - * cells (continuity counter). */ + * cells (continuity counter wrap). */ if (ts[1] & TS_PUSI) { if (! priv->need_pusi) { - /* printk(KERN_WARNING "Skipping pointer field %u.\n", *from_where); */ if (*from_where > 181) { - printk(KERN_WARNING "*** Invalid pointer " - "field: %u. Current TS cell " - "follows:\n", *from_where); - hexdump( ts, TS_SZ ); - printk(KERN_WARNING "-------------------\n"); + /* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */ + printk(KERN_WARNING "%lu: Invalid pointer " + "field: %u.\n", priv->ts_count, *from_where); + + /* Drop partly decoded SNDU, reset state, resync on PUSI. */ + if (priv->ule_skb) { + dev_kfree_skb( priv->ule_skb ); + ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; + ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; + } + reset_ule(priv); + priv->need_pusi = 1; + ts += TS_SZ; + priv->ts_count++; + continue; } /* Skip pointer field (we're processing a * packed payload). */ @@ -290,21 +454,26 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) priv->need_pusi = 0; if (priv->ule_sndu_remain > 183) { + /* Current SNDU lacks more data than there could be available in the + * current TS cell. */ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++; - printk(KERN_WARNING "Expected %d more SNDU bytes, but " - "got PUSI. Flushing incomplete payload.\n", - priv->ule_sndu_remain); + printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " + "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", + priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); dev_kfree_skb(priv->ule_skb); /* Prepare for next SNDU. */ reset_ule(priv); + /* Resync: go to where pointer field points to: start of next ULE SNDU. */ + from_where += ts[4]; + ts_remain -= ts[4]; } } } /* Check if new payload needs to be started. */ if (priv->ule_skb == NULL) { - /* Start a new payload w/ skb. + /* Start a new payload with skb. * Find ULE header. It is only guaranteed that the * length field (2 bytes) is contained in the current * TS. @@ -318,6 +487,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } if (! priv->ule_sndu_len) { + /* Got at least two bytes, thus extrace the SNDU length. */ priv->ule_sndu_len = from_where[0] << 8 | from_where[1]; if (priv->ule_sndu_len & 0x8000) { /* D-Bit is set: no dest mac present. */ @@ -326,17 +496,14 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } else priv->ule_dbit = 0; - /* printk(KERN_WARNING "ULE D-Bit: %d, SNDU len %u.\n", - priv->ule_dbit, priv->ule_sndu_len); */ - if (priv->ule_sndu_len > 32763) { - printk(KERN_WARNING "Invalid ULE SNDU length %u. " - "Resyncing.\n", priv->ule_sndu_len); - hexdump(ts, TS_SZ); + printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " + "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); priv->ule_sndu_len = 0; priv->need_pusi = 1; new_ts = 1; ts += TS_SZ; + priv->ts_count++; continue; } ts_remain -= 2; /* consume the 2 bytes SNDU length. */ @@ -354,11 +521,12 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) case 1: priv->ule_sndu_type = from_where[0] << 8; priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */ - /* ts_remain -= 1; from_where += 1; - * here not necessary, because we continue. */ + ts_remain -= 1; from_where += 1; + /* Continue w/ next TS. */ case 0: new_ts = 1; ts += TS_SZ; + priv->ts_count++; continue; default: /* complete ULE header is present in current TS. */ @@ -376,24 +544,9 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) break; } - if (priv->ule_sndu_type == ULE_TEST) { - /* Test SNDU, discarded by the receiver. */ - printk(KERN_WARNING "Discarding ULE Test SNDU (%d bytes). " - "Resyncing.\n", priv->ule_sndu_len); - priv->ule_sndu_len = 0; - priv->need_pusi = 1; - continue; - } - - skblen = priv->ule_sndu_len; /* Including CRC32 */ - if (priv->ule_sndu_type != ULE_BRIDGED) { - skblen += ETH_HLEN; -#if 1 - if (! priv->ule_dbit) - skblen -= ETH_ALEN; -#endif - } - priv->ule_skb = dev_alloc_skb(skblen); + /* Allocate the skb (decoder target buffer) with the correct size, as follows: + * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */ + priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN ); if (priv->ule_skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); @@ -401,130 +554,129 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) return; } -#if 0 - if (priv->ule_sndu_type != ULE_BRIDGED) { - // skb_reserve(priv->ule_skb, 2); /* longword align L3 header */ - // Create Ethernet header. - ethh = (struct ethhdr *)skb_put( priv->ule_skb, ETH_HLEN ); - memset( ethh->h_source, 0x00, ETH_ALEN ); - if (priv->ule_dbit) { - // Dest MAC address not present --> generate our own. - memcpy( ethh->h_dest, eth_dest_addr, ETH_ALEN ); - } else { - // Dest MAC address could be split across two TS cells. - // FIXME: implement. - - printk( KERN_WARNING "%s: got destination MAC " - "address.\n", dev->name ); - memcpy( ethh->h_dest, eth_dest_addr, ETH_ALEN ); - } - ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ? - priv->ule_sndu_len : priv->ule_sndu_type); - } -#endif - /* this includes the CRC32 _and_ dest mac, if !dbit! */ + /* This includes the CRC32 _and_ dest mac, if !dbit. */ priv->ule_sndu_remain = priv->ule_sndu_len; priv->ule_skb->dev = dev; + /* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */ + skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN ); } /* Copy data into our current skb. */ how_much = min(priv->ule_sndu_remain, (int)ts_remain); - if ((priv->ule_ethhdr_complete < ETH_ALEN) && - (priv->ule_sndu_type != ULE_BRIDGED)) { - ethh = (struct ethhdr *)priv->ule_skb->data; - if (! priv->ule_dbit) { - if (how_much >= (ETH_ALEN - priv->ule_ethhdr_complete)) { - /* copy dest mac address. */ - memcpy(skb_put(priv->ule_skb, - (ETH_ALEN - priv->ule_ethhdr_complete)), - from_where, - (ETH_ALEN - priv->ule_ethhdr_complete)); - memset(ethh->h_source, 0x00, ETH_ALEN); - ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ? - priv->ule_sndu_len : - priv->ule_sndu_type); - skb_put(priv->ule_skb, ETH_ALEN + 2); - - how_much -= (ETH_ALEN - priv->ule_ethhdr_complete); - priv->ule_sndu_remain -= (ETH_ALEN - - priv->ule_ethhdr_complete); - ts_remain -= (ETH_ALEN - priv->ule_ethhdr_complete); - from_where += (ETH_ALEN - priv->ule_ethhdr_complete); - priv->ule_ethhdr_complete = ETH_ALEN; - } - } else { - /* Generate whole Ethernet header. */ - memcpy(ethh->h_dest, eth_dest_addr, ETH_ALEN); - memset(ethh->h_source, 0x00, ETH_ALEN); - ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ? - priv->ule_sndu_len : priv->ule_sndu_type); - skb_put(priv->ule_skb, ETH_HLEN); - priv->ule_ethhdr_complete = ETH_ALEN; - } - } - /* printk(KERN_WARNING "Copying %u bytes, ule_sndu_remain = %u, " - "ule_sndu_len = %u.\n", how_much, priv->ule_sndu_remain, - priv->ule_sndu_len); */ memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much); priv->ule_sndu_remain -= how_much; ts_remain -= how_much; from_where += how_much; - if ((priv->ule_ethhdr_complete < ETH_ALEN) && - (priv->ule_sndu_type != ULE_BRIDGED)) { - priv->ule_ethhdr_complete += how_much; - } - /* Check for complete payload. */ if (priv->ule_sndu_remain <= 0) { /* Check CRC32, we've got it in our skb already. */ unsigned short ulen = htons(priv->ule_sndu_len); unsigned short utype = htons(priv->ule_sndu_type); - struct kvec iov[4] = { + struct kvec iov[3] = { { &ulen, sizeof ulen }, { &utype, sizeof utype }, - { NULL, 0 }, - { priv->ule_skb->data + ETH_HLEN, - priv->ule_skb->len - ETH_HLEN - 4 } + { priv->ule_skb->data, priv->ule_skb->len - 4 } }; unsigned long ule_crc = ~0L, expected_crc; if (priv->ule_dbit) { /* Set D-bit for CRC32 verification, * if it was set originally. */ ulen |= 0x0080; - } else { - iov[2].iov_base = priv->ule_skb->data; - iov[2].iov_len = ETH_ALEN; } - ule_crc = iov_crc32(ule_crc, iov, 4); + + ule_crc = iov_crc32(ule_crc, iov, 3); expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 | *((u8 *)priv->ule_skb->tail - 3) << 16 | *((u8 *)priv->ule_skb->tail - 2) << 8 | *((u8 *)priv->ule_skb->tail - 1); if (ule_crc != expected_crc) { - printk(KERN_WARNING "CRC32 check %s: %#lx / %#lx.\n", - ule_crc != expected_crc ? "FAILED" : "OK", - ule_crc, expected_crc); - hexdump(priv->ule_skb->data + ETH_HLEN, - priv->ule_skb->len - ETH_HLEN); + printk(KERN_WARNING "%lu: CRC32 check FAILED: %#lx / %#lx, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n", + priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0); + +#ifdef ULE_DEBUG + hexdump( iov[0].iov_base, iov[0].iov_len ); + hexdump( iov[1].iov_base, iov[1].iov_len ); + hexdump( iov[2].iov_base, iov[2].iov_len ); + + if (ule_where == ule_hist) { + hexdump( &ule_hist[98*TS_SZ], TS_SZ ); + hexdump( &ule_hist[99*TS_SZ], TS_SZ ); + } else if (ule_where == &ule_hist[TS_SZ]) { + hexdump( &ule_hist[99*TS_SZ], TS_SZ ); + hexdump( ule_hist, TS_SZ ); + } else { + hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ ); + hexdump( ule_where - TS_SZ, TS_SZ ); + } + ule_dump = 1; +#endif ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++; dev_kfree_skb(priv->ule_skb); } else { + /* CRC32 verified OK. */ + /* Handle ULE Extension Headers. */ + if (priv->ule_sndu_type < 1536) { + /* There is an extension header. Handle it accordingly. */ + int l = handle_ule_extensions( priv ); + if (l < 0) { + /* Mandatory extension header unknown or TEST SNDU. Drop it. */ + // printk( KERN_WARNING "Dropping SNDU, extension headers.\n" ); + dev_kfree_skb( priv->ule_skb ); + goto sndu_done; + } + skb_pull( priv->ule_skb, l ); + } + /* CRC32 was OK. Remove it from skb. */ priv->ule_skb->tail -= 4; priv->ule_skb->len -= 4; + + /* Filter on receiver's destination MAC address, if present. */ + if (!priv->ule_dbit) { + /* The destination MAC address is the next data in the skb. */ + if (memcmp( priv->ule_skb->data, dev->dev_addr, ETH_ALEN )) { + /* MAC addresses don't match. Drop SNDU. */ + // printk( KERN_WARNING "Dropping SNDU, MAC address.\n" ); + dev_kfree_skb( priv->ule_skb ); + goto sndu_done; + } + if (! priv->ule_bridged) { + skb_push( priv->ule_skb, ETH_ALEN + 2 ); + ethh = (struct ethhdr *)priv->ule_skb->data; + memcpy( ethh->h_dest, ethh->h_source, ETH_ALEN ); + memset( ethh->h_source, 0, ETH_ALEN ); + ethh->h_proto = htons( priv->ule_sndu_type ); + } else { + /* Skip the Receiver destination MAC address. */ + skb_pull( priv->ule_skb, ETH_ALEN ); + } + } else { + if (! priv->ule_bridged) { + skb_push( priv->ule_skb, ETH_HLEN ); + ethh = (struct ethhdr *)priv->ule_skb->data; + memcpy( ethh->h_dest, dev->dev_addr, ETH_ALEN ); + memset( ethh->h_source, 0, ETH_ALEN ); + ethh->h_proto = htons( priv->ule_sndu_type ); + } else { + /* skb is in correct state; nothing to do. */ + } + } + priv->ule_bridged = 0; + /* Stuff into kernel's protocol stack. */ priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev); /* If D-bit is set (i.e. destination MAC address not present), - * receive the packet anyhw. */ - /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) */ - priv->ule_skb->pkt_type = PACKET_HOST; + * receive the packet anyhow. */ + /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) + priv->ule_skb->pkt_type = PACKET_HOST; */ ((struct dvb_net_priv *) dev->priv)->stats.rx_packets++; ((struct dvb_net_priv *) dev->priv)->stats.rx_bytes += priv->ule_skb->len; netif_rx(priv->ule_skb); } + sndu_done: /* Prepare for next SNDU. */ reset_ule(priv); } @@ -544,6 +696,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } else { new_ts = 1; ts += TS_SZ; + priv->ts_count++; if (priv->ule_skb == NULL) { priv->need_pusi = 1; priv->ule_sndu_type_1 = 0; @@ -661,6 +814,7 @@ static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev) { + dev_kfree_skb(skb); return 0; } @@ -722,7 +876,7 @@ static int dvb_net_feed_start(struct net_device *dev) unsigned char *mac = (unsigned char *) dev->dev_addr; dprintk("%s: rx_mode %i\n", __FUNCTION__, priv->rx_mode); - if (priv->secfeed || priv->secfilter || priv->multi_secfilter[0]) + if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0]) printk("%s: BUG %d\n", __FUNCTION__, __LINE__); priv->secfeed=NULL; @@ -1047,13 +1201,14 @@ static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype) } -static int dvb_net_remove_if(struct dvb_net *dvbnet, int num) +static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned int num) { struct net_device *net = dvbnet->device[num]; - struct dvb_net_priv *priv = net->priv; + struct dvb_net_priv *priv; if (!dvbnet->state[num]) return -EINVAL; + priv = net->priv; if (priv->in_use) return -EBUSY; @@ -1114,10 +1269,18 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file, break; } case NET_REMOVE_IF: + { + int ret; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - module_put(dvbdev->adapter->module); - return dvb_net_remove_if(dvbnet, (int) (long) parg); + if ((unsigned int) parg >= DVB_NET_DEVICES_MAX) + return -EINVAL; + ret = dvb_net_remove_if(dvbnet, (unsigned int) parg); + if (!ret) + module_put(dvbdev->adapter->module); + return ret; + } /* binary compatiblity cruft */ case __NET_ADD_IF_OLD: @@ -1193,6 +1356,7 @@ void dvb_net_release (struct dvb_net *dvbnet) dvb_net_remove_if(dvbnet, i); } } +EXPORT_SYMBOL(dvb_net_release); int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, @@ -1210,4 +1374,4 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, return 0; } - +EXPORT_SYMBOL(dvb_net_init); diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index 606c19d30bc9..837d16a34b91 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -132,6 +132,7 @@ int dvb_generic_open(struct inode *inode, struct file *file) dvbdev->users--; return 0; } +EXPORT_SYMBOL(dvb_generic_open); int dvb_generic_release(struct inode *inode, struct file *file) @@ -150,6 +151,7 @@ int dvb_generic_release(struct inode *inode, struct file *file) dvbdev->users++; return 0; } +EXPORT_SYMBOL(dvb_generic_release); int dvb_generic_ioctl(struct inode *inode, struct file *file, @@ -165,6 +167,7 @@ int dvb_generic_ioctl(struct inode *inode, struct file *file, return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl); } +EXPORT_SYMBOL(dvb_generic_ioctl); static int dvbdev_get_free_id (struct dvb_adapter *adap, int type) @@ -235,6 +238,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, return 0; } +EXPORT_SYMBOL(dvb_register_device); void dvb_unregister_device(struct dvb_device *dvbdev) @@ -251,6 +255,7 @@ void dvb_unregister_device(struct dvb_device *dvbdev) list_del(&dvbdev->list_head); kfree(dvbdev); } +EXPORT_SYMBOL(dvb_unregister_device); static int dvbdev_get_free_adapter_num (void) @@ -309,6 +314,7 @@ int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct mo return num; } +EXPORT_SYMBOL(dvb_register_adapter); int dvb_unregister_adapter(struct dvb_adapter *adap) @@ -322,6 +328,7 @@ int dvb_unregister_adapter(struct dvb_adapter *adap) kfree (adap); return 0; } +EXPORT_SYMBOL(dvb_unregister_adapter); /* if the miracle happens and "generic_usercopy()" is included into the kernel, then this can vanish. please don't make the mistake and diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index fcad5f21c605..333ada77ce7b 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -49,6 +49,7 @@ struct dvb_adapter { struct list_head device_list; const char *name; u8 proposed_mac [6]; + void* priv; struct module *module; }; diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 361e76ba6ece..226a59bfa947 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -1,3 +1,6 @@ +menu "Customise DVB Frontends" + depends on DVB_CORE + comment "DVB-S (satellite) frontends" depends on DVB_CORE @@ -7,32 +10,23 @@ config DVB_STV0299 help A DVB-S tuner module. Say Y when you want to support this frontend. - Some examples are the Alps BSRU6, the Philips SU1278 and - the LG TDQB-S00x. - - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_CX24110 - tristate "Connexant CX24110 based" + tristate "Conexant CX24110 based" depends on DVB_CORE help A DVB-S tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - -config DVB_GRUNDIG_29504_491 - tristate "Grundig 29504-491 based" +config DVB_TDA8083 + tristate "Philips TDA8083 based" depends on DVB_CORE help A DVB-S tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. +config DVB_TDA80XX + tristate "Philips TDA8044 or TDA8083 based" + depends on DVB_CORE + help + A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_MT312 tristate "Zarlink MT312 based" @@ -40,81 +34,53 @@ config DVB_MT312 help A DVB-S tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_VES1X93 tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE help A DVB-S tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - comment "DVB-T (terrestrial) frontends" depends on DVB_CORE -config DVB_SP887X - tristate "Microtune sp887x based (i.e. Microtune DTF7072)" +config DVB_SP8870 + tristate "Spase sp8870 based" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. This driver needs external firmware. Please use the command - "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to + "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to download/extract it, and then copy it to /usr/lib/hotplug/firmware. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - -config DVB_ALPS_TDLB7 - tristate "Alps TDLB7 based" +config DVB_SP887X + tristate "Spase sp887x based" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. This driver needs external firmware. Please use the command - "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to + "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to download/extract it, and then copy it to /usr/lib/hotplug/firmware. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - -config DVB_ALPS_TDMB7 - tristate "Alps TDMB7 based" +config DVB_CX22700 + tristate "Conexant CX22700 based" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_CX22702 tristate "Conexant cx22702 demodulator (OFDM)" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - -config DVB_GRUNDIG_29504_401 - tristate "Grundig 29504-401 based" +config DVB_L64781 + tristate "LSI L64781" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" depends on DVB_CORE @@ -126,30 +92,18 @@ config DVB_TDA1004X "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to download/extract them, and then copy them to /usr/lib/hotplug/firmware. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_NXT6000 tristate "NxtWave Communications NXT6000 based" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_MT352 tristate "Zarlink MT352 based" depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_DIB3000MB tristate "DiBcom 3000-MB" depends on DVB_CORE @@ -157,13 +111,12 @@ config DVB_DIB3000MB A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. - Used on USB-powered devices. You should also say Y to DVB_DIBUSB - (DiBcom USB DVB-T Adapter) to support the actual device, - this is "only" the frontend/tuner. - - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. +config DVB_DIB3000MC + tristate "DiBcom 3000-MC/P" + depends on DVB_CORE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -174,27 +127,22 @@ config DVB_ATMEL_AT76C651 help A DVB-C tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - config DVB_VES1820 tristate "VLSI VES1820 based" depends on DVB_CORE help A DVB-C tuner module. Say Y when you want to support this frontend. - If you don't know what tuner module is soldered on your - DVB adapter simply enable all supported frontends, the - right one will get autodetected. - -comment "Misc. Frontend Modules" +config DVB_TDA10021 + tristate "Philips TDA10021 based" depends on DVB_CORE + help + A DVB-C tuner module. Say Y when you want to support this frontend. -config DVB_TWINHAN_DST - tristate "Twinhan DST based DVB-S/-T frontend" - depends on DVB_CORE && DVB_BT8XX +config DVB_STV0297 + tristate "ST STV0297 based" + depends on DVB_CORE help - Used in such cards as the VP-1020/1030, Twinhan DST, - VVmer TV@SAT. Say Y when you want to support frontends - using this asic. + A DVB-C tuner module. Say Y when you want to support this frontend. + +endmenu diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 9a0b7f44a081..f84a4980251a 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -4,15 +4,15 @@ EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -obj-$(CONFIG_DVB_TWINHAN_DST) += dst.o obj-$(CONFIG_DVB_STV0299) += stv0299.o -obj-$(CONFIG_DVB_ALPS_TDLB7) += alps_tdlb7.o -obj-$(CONFIG_DVB_ALPS_TDMB7) += alps_tdmb7.o +obj-$(CONFIG_DVB_SP8870) += sp8870.o +obj-$(CONFIG_DVB_CX22700) += cx22700.o obj-$(CONFIG_DVB_ATMEL_AT76C651) += at76c651.o obj-$(CONFIG_DVB_CX24110) += cx24110.o -obj-$(CONFIG_DVB_GRUNDIG_29504_491) += grundig_29504-491.o -obj-$(CONFIG_DVB_GRUNDIG_29504_401) += grundig_29504-401.o -obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o +obj-$(CONFIG_DVB_TDA8083) += tda8083.o +obj-$(CONFIG_DVB_L64781) += l64781.o +obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o dib3000-common.o +obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dib3000-common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o @@ -21,4 +21,6 @@ obj-$(CONFIG_DVB_SP887X) += sp887x.o obj-$(CONFIG_DVB_NXT6000) += nxt6000.o obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_CX22702) += cx22702.o - +obj-$(CONFIG_DVB_TDA80XX) += tda80xx.o +obj-$(CONFIG_DVB_TDA10021) += tda10021.o +obj-$(CONFIG_DVB_STV0297) += stv0297.o diff --git a/drivers/media/dvb/frontends/alps_tdlb7.c b/drivers/media/dvb/frontends/alps_tdlb7.c deleted file mode 100644 index 423ca8824863..000000000000 --- a/drivers/media/dvb/frontends/alps_tdlb7.c +++ /dev/null @@ -1,746 +0,0 @@ -/* - Driver for Alps TDLB7 Frontend - - Copyright (C) 1999 Juergen Peitz - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ -/* - * This driver needs external firmware. Please use the command - * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to - * download/extract it, and then copy it to /usr/lib/hotplug/firmware. - */ -#define SP887X_DEFAULT_FIRMWARE "dvb-fe-tdlb7-2.16.fw" - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/firmware.h> -#include <linux/delay.h> - -#include "dvb_frontend.h" - -#define FRONTEND_NAME "dvbfe_alps_tdlb7" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - - -/* firmware size for sp8870 */ -#define SP8870_FIRMWARE_SIZE 16382 - -/* starting point for firmware in file 'Sc_main.mc' */ -#define SP8870_FIRMWARE_OFFSET 0x0A - -static struct dvb_frontend_info tdlb7_info = { - .name = "Alps TDLB7", - .type = FE_OFDM, - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166666, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | - FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER -}; - -struct tdlb7_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - -static int sp8870_writereg (struct i2c_adapter *i2c, u16 reg, u16 data) -{ - u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { .addr = 0x71, .flags = 0, .buf = buf, .len = 4 }; - int err; - - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { - dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); - return -EREMOTEIO; - } - - return 0; -} - -static u16 sp8870_readreg (struct i2c_adapter *i2c, u16 reg) -{ - int ret; - u8 b0 [] = { reg >> 8 , reg & 0xff }; - u8 b1 [] = { 0, 0 }; - struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 }, - { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; - - ret = i2c_transfer (i2c, msg, 2); - - if (ret != 2) { - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - return -1; - } - - return (b1[0] << 8 | b1[1]); -} - -static int sp5659_write (struct i2c_adapter *i2c, u8 data [4]) -{ - int ret; - - u8 buf_open [] = { 0x206 >> 8, 0x206 & 0xff, 0x001 >> 8, 0x001 & 0xff }; - u8 buf_close [] = { 0x206 >> 8, 0x206 & 0xff, 0x000 >> 8, 0x000 & 0xff }; - - struct i2c_msg msg[3] = { {.addr = 0x71, .flags = 0, .buf = buf_open, .len = 4 }, - {.addr = 0x60, .flags = 0, .buf = data, .len = 4 }, - {.addr = 0x71, .flags = 0, .buf = buf_close, .len = 4 } }; - - ret = i2c_transfer (i2c, &msg[0], 3); - - if (ret != 3) - printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 3) ? -EREMOTEIO : 0; -} - -static void sp5659_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u32 div = (freq + 36200000) / 166666; - u8 buf [4]; - int pwr; - - if (freq <= 782000000) - pwr = 1; - else - pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x85; - buf[3] = pwr << 6; - - /* open i2c gate for PLL message transmission... */ - sp5659_write (i2c, buf); -} - -static int sp8870_firmware_upload (struct i2c_adapter *i2c, const struct firmware *fw) -{ - struct i2c_msg msg; - char *fw_buf = fw->data; - int fw_pos; - u8 tx_buf[255]; - int tx_len; - int err = 0; - - dprintk ("%s: ...\n", __FUNCTION__); - - if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) - return -EINVAL; - - // system controller stop - sp8870_writereg(i2c, 0x0F00, 0x0000); - - // instruction RAM register hiword - sp8870_writereg(i2c, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); - - // instruction RAM MWR - sp8870_writereg(i2c, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); - - // do firmware upload - fw_pos = SP8870_FIRMWARE_OFFSET; - while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ - tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; - // write register 0xCF0A - tx_buf[0] = 0xCF; - tx_buf[1] = 0x0A; - memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); - msg.addr = 0x71; - msg.flags = 0; - msg.buf = tx_buf; - msg.len = tx_len + 2; - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { - printk("%s: firmware upload failed!\n", __FUNCTION__); - printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); - return err; - } - fw_pos += tx_len; - } - - dprintk ("%s: done!\n", __FUNCTION__); - return 0; -}; - -static void sp8870_microcontroller_stop (struct i2c_adapter *i2c) -{ - sp8870_writereg(i2c, 0x0F08, 0x000); - sp8870_writereg(i2c, 0x0F09, 0x000); - - // microcontroller STOP - sp8870_writereg(i2c, 0x0F00, 0x000); -} - -static void sp8870_microcontroller_start (struct i2c_adapter *i2c) -{ - sp8870_writereg(i2c, 0x0F08, 0x000); - sp8870_writereg(i2c, 0x0F09, 0x000); - - // microcontroller START - sp8870_writereg(i2c, 0x0F00, 0x001); - // not documented but if we don't read 0x0D01 out here - // we don't get a correct data valid signal - sp8870_readreg(i2c, 0x0D01); -} - -static int sp8870_init (struct i2c_adapter *i2c) -{ - dprintk ("%s\n", __FUNCTION__); - - /* enable TS output and interface pins */ - sp8870_writereg(i2c, 0xc18, 0x00d); - - // system controller stop - sp8870_microcontroller_stop(i2c); - - // ADC mode - sp8870_writereg(i2c, 0x0301, 0x0003); - - // Reed Solomon parity bytes passed to output - sp8870_writereg(i2c, 0x0C13, 0x0001); - - // MPEG clock is suppressed if no valid data - sp8870_writereg(i2c, 0x0C14, 0x0001); - - /* bit 0x010: enable data valid signal */ - sp8870_writereg(i2c, 0x0D00, 0x010); - sp8870_writereg(i2c, 0x0D01, 0x000); - - return 0; -} - -static int sp8870_read_status (struct i2c_adapter *i2c, fe_status_t * fe_status) -{ - int status; - int signal; - - *fe_status = 0; - - status = sp8870_readreg (i2c, 0x0200); - if (status < 0) - return -EIO; - - signal = sp8870_readreg (i2c, 0x0303); - if (signal < 0) - return -EIO; - - if (signal > 0x0F) - *fe_status |= FE_HAS_SIGNAL; - if (status & 0x08) - *fe_status |= FE_HAS_SYNC; - if (status & 0x04) - *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; - - return 0; -} - -static int sp8870_read_ber (struct i2c_adapter *i2c, u32 * ber) -{ - int ret; - u32 tmp; - - *ber = 0; - - ret = sp8870_readreg(i2c, 0xC08); - if (ret < 0) - return -EIO; - - tmp = ret & 0x3F; - - ret = sp8870_readreg(i2c, 0xC07); - if (ret < 0) - return -EIO; - - tmp = ret << 6; - - if (tmp >= 0x3FFF0) - tmp = ~0; - - *ber = tmp; - - return 0; -} - -static int sp8870_read_signal_strength (struct i2c_adapter *i2c, u16 * signal) -{ - int ret; - u16 tmp; - - *signal = 0; - - ret = sp8870_readreg (i2c, 0x306); - if (ret < 0) - return -EIO; - - tmp = ret << 8; - - ret = sp8870_readreg (i2c, 0x303); - if (ret < 0) - return -EIO; - - tmp |= ret; - - if (tmp) - *signal = 0xFFFF - tmp; - - return 0; -} - -static int sp8870_read_snr(struct i2c_adapter *i2c, u32* snr) -{ - *snr = 0; - return -EOPNOTSUPP; -} - -static int sp8870_read_uncorrected_blocks (struct i2c_adapter *i2c, u32* ublocks) -{ - int ret; - - *ublocks = 0; - - ret = sp8870_readreg(i2c, 0xC0C); - if (ret < 0) - return -EIO; - - if (ret == 0xFFFF) - ret = ~0; - - *ublocks = ret; - - return 0; -} - -static int sp8870_read_data_valid_signal(struct i2c_adapter *i2c) -{ - return (sp8870_readreg(i2c, 0x0D02) > 0); -} - -static -int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05) -{ - int known_parameters = 1; - - *reg0xc05 = 0x000; - - switch (p->u.ofdm.constellation) { - case QPSK: - break; - case QAM_16: - *reg0xc05 |= (1 << 10); - break; - case QAM_64: - *reg0xc05 |= (2 << 10); - break; - case QAM_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - }; - - switch (p->u.ofdm.hierarchy_information) { - case HIERARCHY_NONE: - break; - case HIERARCHY_1: - *reg0xc05 |= (1 << 7); - break; - case HIERARCHY_2: - *reg0xc05 |= (2 << 7); - break; - case HIERARCHY_4: - *reg0xc05 |= (3 << 7); - break; - case HIERARCHY_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - }; - - switch (p->u.ofdm.code_rate_HP) { - case FEC_1_2: - break; - case FEC_2_3: - *reg0xc05 |= (1 << 3); - break; - case FEC_3_4: - *reg0xc05 |= (2 << 3); - break; - case FEC_5_6: - *reg0xc05 |= (3 << 3); - break; - case FEC_7_8: - *reg0xc05 |= (4 << 3); - break; - case FEC_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - }; - - if (known_parameters) - *reg0xc05 |= (2 << 1); /* use specified parameters */ - else - *reg0xc05 |= (1 << 1); /* enable autoprobing */ - - return 0; -} - -static int sp8870_set_frontend_parameters (struct i2c_adapter *i2c, - struct dvb_frontend_parameters *p) -{ - int err; - u16 reg0xc05; - - if ((err = configure_reg0xc05(p, ®0xc05))) - return err; - - // system controller stop - sp8870_microcontroller_stop(i2c); - - // set tuner parameters - sp5659_set_tv_freq (i2c, p->frequency); - - // sample rate correction bit [23..17] - sp8870_writereg(i2c, 0x0319, 0x000A); - - // sample rate correction bit [16..0] - sp8870_writereg(i2c, 0x031A, 0x0AAB); - - // integer carrier offset - sp8870_writereg(i2c, 0x0309, 0x0400); - - // fractional carrier offset - sp8870_writereg(i2c, 0x030A, 0x0000); - - // filter for 6/7/8 Mhz channel - if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) - sp8870_writereg(i2c, 0x0311, 0x0002); - else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) - sp8870_writereg(i2c, 0x0311, 0x0001); - else - sp8870_writereg(i2c, 0x0311, 0x0000); - - // scan order: 2k first = 0x0000, 8k first = 0x0001 - if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) - sp8870_writereg(i2c, 0x0338, 0x0000); - else - sp8870_writereg(i2c, 0x0338, 0x0001); - - sp8870_writereg(i2c, 0xc05, reg0xc05); - - // read status reg in order to clear pending irqs - sp8870_readreg(i2c, 0x200); - - // system controller start - sp8870_microcontroller_start(i2c); - - return 0; -} - -// number of trials to recover from lockup -#define MAXTRIALS 5 -// maximum checks for data valid signal -#define MAXCHECKS 100 - -// only for debugging: counter for detected lockups -static int lockups = 0; -// only for debugging: counter for channel switches -static int switches = 0; - -static int sp8870_set_frontend (struct i2c_adapter *i2c, struct dvb_frontend_parameters *p) -{ - /* - The firmware of the sp8870 sometimes locks up after setting frontend parameters. - We try to detect this by checking the data valid signal. - If it is not set after MAXCHECKS we try to recover the lockup by setting - the frontend parameters again. - */ - - int err = 0; - int valid = 0; - int trials = 0; - int check_count = 0; - - dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency); - - for (trials = 1; trials <= MAXTRIALS; trials++) { - - if ((err = sp8870_set_frontend_parameters(i2c, p))) - return err; - - for (check_count = 0; check_count < MAXCHECKS; check_count++) { -// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); - valid = sp8870_read_data_valid_signal(i2c); - if (valid) { - dprintk("%s: delay = %i usec\n", - __FUNCTION__, check_count * 10); - break; - } - udelay(10); - } - if (valid) - break; - } - - if (!valid) { - printk("%s: firmware crash!!!!!!\n", __FUNCTION__); - return -EIO; - } - - if (debug) { - if (valid) { - if (trials > 1) { - printk("%s: firmware lockup!!!\n", __FUNCTION__); - printk("%s: recovered after %i trial(s))\n", __FUNCTION__, trials - 1); - lockups++; - } - } - switches++; - printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups); - } - - return 0; -} - -static int sp8870_sleep(struct i2c_adapter *i2c) -{ - // tristate TS output and disable interface pins - return sp8870_writereg(i2c, 0xC18, 0x000); -} - -static int sp8870_wake_up(struct i2c_adapter *i2c) -{ - // enable TS output and interface pins - return sp8870_writereg(i2c, 0xC18, 0x00D); -} - -static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct tdlb7_state *state = (struct tdlb7_state *) fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &tdlb7_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - return sp8870_read_status(i2c, (fe_status_t *) arg); - - case FE_READ_BER: - return sp8870_read_ber(i2c, (u32 *) arg); - - case FE_READ_SIGNAL_STRENGTH: - return sp8870_read_signal_strength(i2c, (u16 *) arg); - - case FE_READ_SNR: // not supported by hardware? - return sp8870_read_snr(i2c, (u32 *) arg); - - case FE_READ_UNCORRECTED_BLOCKS: - return sp8870_read_uncorrected_blocks(i2c, (u32 *) arg); - - case FE_SET_FRONTEND: - return sp8870_set_frontend(i2c, (struct dvb_frontend_parameters*) arg); - - case FE_GET_FRONTEND: // FIXME: read known values back from Hardware... - return -EOPNOTSUPP; - - case FE_SLEEP: - return sp8870_sleep(i2c); - - case FE_INIT: - sp8870_wake_up(i2c); - if (fe->data == NULL) { // first time initialisation... - fe->data = (void*) ~0; - sp8870_init (i2c); - } - break; - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - fesettings->min_delay_ms = 150; - fesettings->step_size = 166667; - fesettings->max_drift = 166667*2; - return 0; - } - - default: - return -EOPNOTSUPP; - }; - - return 0; -} - -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) -{ - struct i2c_client *client; - struct tdlb7_state *state; - const struct firmware *fw; - int ret; - - u8 b0 [] = { 0x02 , 0x00 }; - u8 b1 [] = { 0, 0 }; - struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 }, - { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; - - dprintk ("%s\n", __FUNCTION__); - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - return -ENOMEM; - } - - if (NULL == (state = kmalloc(sizeof(struct tdlb7_state), GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - state->i2c = adapter; - - if (i2c_transfer (adapter, msg, 2) != 2) { - kfree(state); - kfree(client); - return -ENODEV; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; - } - - /* request the firmware, this will block until someone uploads it */ - printk("tdlb7: waiting for firmware upload...\n"); - ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev); - if (ret) { - printk("tdlb7: no firmware upload (timeout or file not found?)\n"); - goto out; - } - - ret = sp8870_firmware_upload(adapter, fw); - if (ret) { - printk("tdlb7: writing firmware to device failed\n"); - release_firmware(fw); - goto out; - } - - ret = dvb_register_frontend(tdlb7_ioctl, state->dvb, state, - &tdlb7_info, THIS_MODULE); - if (ret) { - printk("tdlb7: registering frontend to dvb-core failed.\n"); - release_firmware(fw); - goto out; - } - - return 0; -out: - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; -} - -static int detach_client(struct i2c_client *client) -{ - struct tdlb7_state *state = (struct tdlb7_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - dvb_unregister_frontend (tdlb7_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct tdlb7_state *state = (struct tdlb7_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter*)arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_ALPS_TDLB7, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_tdlb7(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_tdlb7(void) -{ - if (i2c_del_driver(&driver)) - printk("tdlb7: driver deregistration failed\n"); -} - -module_init(init_tdlb7); -module_exit(exit_tdlb7); - -MODULE_DESCRIPTION("TDLB7 DVB-T Frontend"); -MODULE_AUTHOR("Juergen Peitz"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/media/dvb/frontends/alps_tdmb7.c b/drivers/media/dvb/frontends/alps_tdmb7.c deleted file mode 100644 index 67043a838b55..000000000000 --- a/drivers/media/dvb/frontends/alps_tdmb7.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - Alps TDMB7 DVB OFDM frontend driver - - Copyright (C) 2001-2002 Convergence Integrated Media GmbH - Holger Waechtler <holger@convergence.de> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/string.h> -#include <linux/slab.h> - -#include "dvb_frontend.h" - -#define FRONTEND_NAME "dvbfe_alps_tdmb7" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - - -static struct dvb_frontend_info tdmb7_info = { - .name = "Alps TDMB7", - .type = FE_OFDM, - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166667, -#if 0 - .frequency_tolerance = ???, - .symbol_rate_min = ???, - .symbol_rate_max = ???, - .symbol_rate_tolerance = 500, /* ppm */ - .notifier_delay = 0, -#endif - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | - FE_CAN_RECOVER -}; - -struct tdmb7_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - -static u8 init_tab [] = { - 0x04, 0x10, - 0x05, 0x09, - 0x06, 0x00, - 0x08, 0x04, - 0x09, 0x00, - 0x0a, 0x01, - 0x15, 0x40, - 0x16, 0x10, - 0x17, 0x87, - 0x18, 0x17, - 0x1a, 0x10, - 0x25, 0x04, - 0x2e, 0x00, - 0x39, 0x00, - 0x3a, 0x04, - 0x45, 0x08, - 0x46, 0x02, - 0x47, 0x05, -}; - -static int cx22700_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) -{ - int ret; - u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x43, .flags = 0, .buf = buf, .len = 2 }; - - dprintk ("%s\n", __FUNCTION__); - - ret = i2c_transfer (i2c, &msg, 1); - - if (ret != 1) - printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", - __FUNCTION__, reg, data, ret); - - return (ret != 1) ? -1 : 0; -} - -static u8 cx22700_readreg (struct i2c_adapter *i2c, u8 reg) -{ - int ret; - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x43, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x43, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - - dprintk ("%s\n", __FUNCTION__); - - ret = i2c_transfer (i2c, msg, 2); - - if (ret != 2) - printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - -static int pll_write (struct i2c_adapter *i2c, u8 data [4]) -{ - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 }; - int ret; - - cx22700_writereg (i2c, 0x0a, 0x00); /* open i2c bus switch */ - ret = i2c_transfer (i2c, &msg, 1); - cx22700_writereg (i2c, 0x0a, 0x01); /* close i2c bus switch */ - - if (ret != 1) - printk("%s: i/o error (addr == 0x%02x, ret == %i)\n", __FUNCTION__, msg.addr, ret); - - return (ret != 1) ? -1 : 0; -} - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int pll_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u32 div = (freq + 36166667) / 166667; -#if 1 //ALPS_SETTINGS - u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, ((div >> 10) & 0x60) | 0x85, - freq < 592000000 ? 0x40 : 0x80 }; -#else - u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, ((div >> 10) & 0x60) | 0x85, - freq < 470000000 ? 0x42 : freq < 862000000 ? 0x41 : 0x81 }; -#endif - - dprintk ("%s: freq == %i, div == %i\n", __FUNCTION__, (int) freq, (int) div); - - return pll_write (i2c, buf); -} - -static int cx22700_init (struct i2c_adapter *i2c) -{ - int i; - - dprintk("cx22700_init: init chip\n"); - - cx22700_writereg (i2c, 0x00, 0x02); /* soft reset */ - cx22700_writereg (i2c, 0x00, 0x00); - - msleep(10); - - for (i=0; i<sizeof(init_tab); i+=2) - cx22700_writereg (i2c, init_tab[i], init_tab[i+1]); - - cx22700_writereg (i2c, 0x00, 0x01); - - return 0; -} - -static int cx22700_set_inversion (struct i2c_adapter *i2c, int inversion) -{ - u8 val; - - dprintk ("%s\n", __FUNCTION__); - - switch (inversion) { - case INVERSION_AUTO: - return -EOPNOTSUPP; - case INVERSION_ON: - val = cx22700_readreg (i2c, 0x09); - return cx22700_writereg (i2c, 0x09, val | 0x01); - case INVERSION_OFF: - val = cx22700_readreg (i2c, 0x09); - return cx22700_writereg (i2c, 0x09, val & 0xfe); - default: - return -EINVAL; - } -} - -static int cx22700_set_tps (struct i2c_adapter *i2c, struct dvb_ofdm_parameters *p) -{ - static const u8 qam_tab [4] = { 0, 1, 0, 2 }; - static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; - u8 val; - - dprintk ("%s\n", __FUNCTION__); - - if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) - return -EINVAL; - - if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) - - if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) - return -EINVAL; - - if (p->guard_interval < GUARD_INTERVAL_1_32 || - p->guard_interval > GUARD_INTERVAL_1_4) - return -EINVAL; - - if (p->transmission_mode != TRANSMISSION_MODE_2K && - p->transmission_mode != TRANSMISSION_MODE_8K) - return -EINVAL; - - if (p->constellation != QPSK && - p->constellation != QAM_16 && - p->constellation != QAM_64) - return -EINVAL; - - if (p->hierarchy_information < HIERARCHY_NONE || - p->hierarchy_information > HIERARCHY_4) - return -EINVAL; - - if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ) - return -EINVAL; - - if (p->bandwidth == BANDWIDTH_7_MHZ) - cx22700_writereg (i2c, 0x09, cx22700_readreg (i2c, 0x09 | 0x10)); - else - cx22700_writereg (i2c, 0x09, cx22700_readreg (i2c, 0x09 & ~0x10)); - - val = qam_tab[p->constellation - QPSK]; - val |= p->hierarchy_information - HIERARCHY_NONE; - - cx22700_writereg (i2c, 0x04, val); - - val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; - val |= fec_tab[p->code_rate_LP - FEC_1_2]; - - cx22700_writereg (i2c, 0x05, val); - - val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; - val |= p->transmission_mode - TRANSMISSION_MODE_2K; - - cx22700_writereg (i2c, 0x06, val); - - cx22700_writereg (i2c, 0x08, 0x04 | 0x02); /* use user tps parameters */ - cx22700_writereg (i2c, 0x08, 0x04); /* restart aquisition */ - - return 0; -} - -static int cx22700_get_tps (struct i2c_adapter *i2c, struct dvb_ofdm_parameters *p) -{ - static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; - static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, - FEC_5_6, FEC_7_8 }; - u8 val; - - dprintk ("%s\n", __FUNCTION__); - - if (!(cx22700_readreg(i2c, 0x07) & 0x20)) /* tps valid? */ - return -EAGAIN; - - val = cx22700_readreg (i2c, 0x01); - - if ((val & 0x7) > 4) - p->hierarchy_information = HIERARCHY_AUTO; - else - p->hierarchy_information = HIERARCHY_NONE + (val & 0x7); - - if (((val >> 3) & 0x3) > 2) - p->constellation = QAM_AUTO; - else - p->constellation = qam_tab[(val >> 3) & 0x3]; - - - val = cx22700_readreg (i2c, 0x02); - - if (((val >> 3) & 0x07) > 4) - p->code_rate_HP = FEC_AUTO; - else - p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; - - if ((val & 0x07) > 4) - p->code_rate_LP = FEC_AUTO; - else - p->code_rate_LP = fec_tab[val & 0x07]; - - - val = cx22700_readreg (i2c, 0x03); - - p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); - p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); - - return 0; -} - -static int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct tdmb7_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &tdmb7_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; - u16 rs_ber = (cx22700_readreg (i2c, 0x0d) << 9) - | (cx22700_readreg (i2c, 0x0e) << 1); - u8 sync = cx22700_readreg (i2c, 0x07); - - *status = 0; - - if (rs_ber < 0xff00) - *status |= FE_HAS_SIGNAL; - - if (sync & 0x20) - *status |= FE_HAS_CARRIER; - - if (sync & 0x10) - *status |= FE_HAS_VITERBI; - - if (sync & 0x10) - *status |= FE_HAS_SYNC; - - if (*status == 0x0f) - *status |= FE_HAS_LOCK; - - break; - } - - case FE_READ_BER: - *((u32*) arg) = cx22700_readreg (i2c, 0x0c) & 0x7f; - cx22700_writereg (i2c, 0x0c, 0x00); - break; - - case FE_READ_SIGNAL_STRENGTH: - { - u16 rs_ber = (cx22700_readreg (i2c, 0x0d) << 9) - | (cx22700_readreg (i2c, 0x0e) << 1); - *((u16*) arg) = ~rs_ber; - break; - } - case FE_READ_SNR: - { - u16 rs_ber = (cx22700_readreg (i2c, 0x0d) << 9) - | (cx22700_readreg (i2c, 0x0e) << 1); - *((u16*) arg) = ~rs_ber; - break; - } - case FE_READ_UNCORRECTED_BLOCKS: - *((u32*) arg) = cx22700_readreg (i2c, 0x0f); - cx22700_writereg (i2c, 0x0f, 0x00); - break; - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - cx22700_writereg (i2c, 0x00, 0x02); /* XXX CHECKME: soft reset*/ - cx22700_writereg (i2c, 0x00, 0x00); - - pll_set_tv_freq (i2c, p->frequency); - cx22700_set_inversion (i2c, p->inversion); - cx22700_set_tps (i2c, &p->u.ofdm); - cx22700_writereg (i2c, 0x37, 0x01); /* PAL loop filter off */ - cx22700_writereg (i2c, 0x00, 0x01); /* restart acquire */ - break; - } - - case FE_GET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - u8 reg09 = cx22700_readreg (i2c, 0x09); - - p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; - return cx22700_get_tps (i2c, &p->u.ofdm); - } - - case FE_INIT: - return cx22700_init (i2c); - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - fesettings->min_delay_ms = 150; - fesettings->step_size = 166667; - fesettings->max_drift = 166667*2; - return 0; - } - - default: - return -EOPNOTSUPP; - }; - - return 0; -} - -static struct i2c_client client_template; - -static int attach_adapter (struct i2c_adapter *adapter) -{ - struct tdmb7_state *state; - struct i2c_client *client; - int ret; - - u8 b0 [] = { 0x7 }; - u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x43, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x43, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - - dprintk ("%s\n", __FUNCTION__); - - if (i2c_transfer(adapter, msg, 2) != 2) - return -ENODEV; - - if (NULL == (state = kmalloc(sizeof(struct tdmb7_state), GFP_KERNEL))) - return -ENOMEM; - - state->i2c = adapter; - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; -} - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - i2c_set_clientdata(client, state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(state); - kfree(client); - return ret; - } - - BUG_ON(!state->dvb); - - ret = dvb_register_frontend (tdmb7_ioctl, state->dvb, state, - &tdmb7_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(state); - kfree(client); - return ret; - } - - return 0; -} - -static int detach_client (struct i2c_client *client) -{ - struct tdmb7_state *state = i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - dvb_unregister_frontend (tdmb7_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct tdmb7_state *state = i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_ALPS_TDMB7, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_tdmb7 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_tdmb7 (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "alps_tdmb7: driver deregistration failed.\n"); -} - -module_init (init_tdmb7); -module_exit (exit_tdmb7); - -MODULE_DESCRIPTION("TDMB7 DVB Frontend driver"); -MODULE_AUTHOR("Holger Waechtler"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/media/dvb/frontends/at76c651.c b/drivers/media/dvb/frontends/at76c651.c index d221723ac909..4b6c539a0d67 100644 --- a/drivers/media/dvb/frontends/at76c651.c +++ b/drivers/media/dvb/frontends/at76c651.c @@ -24,9 +24,6 @@ * AT76C651 * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf * http://www.atmel.com/atmel/acrobat/doc1320.pdf - * - * TUA6010XS - * http://www.infineon.com/cgi/ecrm.dll/ecrm/scripts/public_download.jsp?oid=19512 */ #include <linux/init.h> @@ -36,48 +33,34 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/bitops.h> - #include "dvb_frontend.h" +#include "at76c651.h" -#define FRONTEND_NAME "dvbfe_at76c651" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct at76c651_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + const struct at76c651_config* config; -static struct dvb_frontend_info at76c651_info = { - .name = "Atmel AT76C651B with TUA6010XS", - .type = FE_QAM, - .frequency_min = 48250000, - .frequency_max = 863250000, - .frequency_stepsize = 62500, - /*.frequency_tolerance = */ /* FIXME: 12% of SR */ - .symbol_rate_min = 0, /* FIXME */ - .symbol_rate_max = 9360000, /* FIXME */ - .symbol_rate_tolerance = 4000, - .notifier_delay = 0, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | - FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER -}; + struct dvb_frontend frontend; -struct at76c651_state { + /* revision of the chip */ u8 revision; + + /* last QAM value set */ u8 qam; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; }; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "at76c651: " args); \ + } while (0) + + #if ! defined(__powerpc__) static __inline__ int __ilog2(unsigned long x) { @@ -93,14 +76,14 @@ static __inline__ int __ilog2(unsigned long x) } #endif -static int at76c651_writereg(struct i2c_adapter *i2c, u8 reg, u8 data) +static int at76c651_writereg(struct at76c651_state* state, u8 reg, u8 data) { int ret; u8 buf[] = { reg, data }; struct i2c_msg msg = - { .addr = 0x1a >> 1, .flags = 0, .buf = buf, .len = 2 }; + { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) dprintk("%s: writereg error " @@ -112,16 +95,16 @@ static int at76c651_writereg(struct i2c_adapter *i2c, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } -static u8 at76c651_readreg(struct i2c_adapter *i2c, u8 reg) +static u8 at76c651_readreg(struct at76c651_state* state, u8 reg) { int ret; u8 val; struct i2c_msg msg[] = { - { .addr = 0x1a >> 1, .flags = 0, .buf = ®, .len = 1 }, - { .addr = 0x1a >> 1, .flags = I2C_M_RD, .buf = &val, .len = 1 } + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = &val, .len = 1 } }; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -129,112 +112,64 @@ static u8 at76c651_readreg(struct i2c_adapter *i2c, u8 reg) return val; } -static int at76c651_reset(struct i2c_adapter *i2c) +static int at76c651_reset(struct at76c651_state* state) { - return at76c651_writereg(i2c, 0x07, 0x01); + return at76c651_writereg(state, 0x07, 0x01); } -static void at76c651_disable_interrupts(struct i2c_adapter *i2c) +static void at76c651_disable_interrupts(struct at76c651_state* state) { - at76c651_writereg(i2c, 0x0b, 0x00); + at76c651_writereg(state, 0x0b, 0x00); } static int at76c651_set_auto_config(struct at76c651_state *state) { - struct i2c_adapter *i2c = state->i2c; - /* * Autoconfig */ - at76c651_writereg(i2c, 0x06, 0x01); + at76c651_writereg(state, 0x06, 0x01); /* * Performance optimizations, should be done after autoconfig */ - at76c651_writereg(i2c, 0x10, 0x06); - at76c651_writereg(i2c, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10); - at76c651_writereg(i2c, 0x15, 0x28); - at76c651_writereg(i2c, 0x20, 0x09); - at76c651_writereg(i2c, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90); - at76c651_writereg(i2c, 0x30, 0x90); + at76c651_writereg(state, 0x10, 0x06); + at76c651_writereg(state, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10); + at76c651_writereg(state, 0x15, 0x28); + at76c651_writereg(state, 0x20, 0x09); + at76c651_writereg(state, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90); + at76c651_writereg(state, 0x30, 0x90); if (state->qam == 5) - at76c651_writereg(i2c, 0x35, 0x2A); + at76c651_writereg(state, 0x35, 0x2A); /* * Initialize A/D-converter */ if (state->revision == 0x11) { - at76c651_writereg(i2c, 0x2E, 0x38); - at76c651_writereg(i2c, 0x2F, 0x13); + at76c651_writereg(state, 0x2E, 0x38); + at76c651_writereg(state, 0x2F, 0x13); } - at76c651_disable_interrupts(i2c); + at76c651_disable_interrupts(state); /* * Restart operation */ - at76c651_reset(i2c); + at76c651_reset(state); return 0; } -static void at76c651_set_bbfreq(struct i2c_adapter *i2c) +static void at76c651_set_bbfreq(struct at76c651_state* state) { - at76c651_writereg(i2c, 0x04, 0x3f); - at76c651_writereg(i2c, 0x05, 0xee); + at76c651_writereg(state, 0x04, 0x3f); + at76c651_writereg(state, 0x05, 0xee); } -static int at76c651_pll_write(struct i2c_adapter *i2c, u8 *buf, size_t len) -{ - int ret; - struct i2c_msg msg = - { .addr = 0xc2 >> 1, .flags = 0, .buf = buf, .len = len }; - - at76c651_writereg(i2c, 0x0c, 0xc3); - - ret = i2c_transfer(i2c, &msg, 1); - - at76c651_writereg(i2c, 0x0c, 0xc2); - - if (ret < 0) - return ret; - else if (ret != 1) - return -EREMOTEIO; - - return 0; -} - -static int tua6010_setfreq(struct i2c_adapter *i2c, u32 freq) -{ - u32 div; - u8 buf[4]; - u8 vu, p2, p1, p0; - - if ((freq < 50000000) || (freq > 900000000)) - return -EINVAL; - - div = (freq + 36125000) / 62500; - - if (freq > 400000000) - vu = 1, p2 = 1, p1 = 0, p0 = 1; - else if (freq > 140000000) - vu = 0, p2 = 1, p1 = 1, p0 = 0; - else - vu = 0, p2 = 0, p1 = 1, p0 = 1; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = (div >> 0) & 0xff; - buf[2] = 0x8e; - buf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0; - - return at76c651_pll_write(i2c, buf, 4); -} - -static int at76c651_set_symbol_rate(struct i2c_adapter *i2c, u32 symbol_rate) +static int at76c651_set_symbol_rate(struct at76c651_state* state, u32 symbol_rate) { u8 exponent; u32 mantissa; @@ -251,9 +186,9 @@ static int at76c651_set_symbol_rate(struct i2c_adapter *i2c, u32 symbol_rate) exponent = __ilog2((symbol_rate << 4) / 903125); mantissa = ((symbol_rate / 3125) * (1 << (24 - exponent))) / 289; - at76c651_writereg(i2c, 0x00, mantissa >> 13); - at76c651_writereg(i2c, 0x01, mantissa >> 5); - at76c651_writereg(i2c, 0x02, (mantissa << 3) | exponent); + at76c651_writereg(state, 0x00, mantissa >> 13); + at76c651_writereg(state, 0x01, mantissa >> 5); + at76c651_writereg(state, 0x02, (mantissa << 3) | exponent); return 0; } @@ -292,13 +227,12 @@ static int at76c651_set_qam(struct at76c651_state *state, fe_modulation_t qam) } - return at76c651_writereg(state->i2c, 0x03, state->qam); + return at76c651_writereg(state, 0x03, state->qam); } -static int at76c651_set_inversion(struct i2c_adapter *i2c, - fe_spectral_inversion_t inversion) +static int at76c651_set_inversion(struct at76c651_state* state, fe_spectral_inversion_t inversion) { - u8 feciqinv = at76c651_readreg(i2c, 0x60); + u8 feciqinv = at76c651_readreg(state, 0x60); switch (inversion) { case INVERSION_OFF: @@ -318,59 +252,63 @@ static int at76c651_set_inversion(struct i2c_adapter *i2c, return -EINVAL; } - return at76c651_writereg(i2c, 0x60, feciqinv); + return at76c651_writereg(state, 0x60, feciqinv); } -static int at76c651_set_parameters(struct at76c651_state *state, + + + + + + + + +static int at76c651_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct i2c_adapter *i2c = state->i2c; int ret; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - if ((ret = tua6010_setfreq(i2c, p->frequency))) - return ret; + at76c651_writereg(state, 0x0c, 0xc3); + state->config->pll_set(fe, p); + at76c651_writereg(state, 0x0c, 0xc2); - if ((ret = at76c651_set_symbol_rate(i2c, p->u.qam.symbol_rate))) + if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate))) return ret; - if ((ret = at76c651_set_inversion(i2c, p->inversion))) + if ((ret = at76c651_set_inversion(state, p->inversion))) return ret; return at76c651_set_auto_config(state); } -static int at76c651_set_defaults(struct at76c651_state *state) +static int at76c651_set_defaults(struct dvb_frontend* fe) { - struct i2c_adapter *i2c = state->i2c; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - at76c651_set_symbol_rate(i2c, 6900000); + at76c651_set_symbol_rate(state, 6900000); at76c651_set_qam(state, QAM_64); - at76c651_set_bbfreq(i2c); + at76c651_set_bbfreq(state); at76c651_set_auto_config(state); - return 0; + if (state->config->pll_init) { + at76c651_writereg(state, 0x0c, 0xc3); + state->config->pll_init(fe); + at76c651_writereg(state, 0x0c, 0xc2); } -static int at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct at76c651_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &at76c651_info, sizeof (struct dvb_frontend_info)); - break; + return 0; +} - case FE_READ_STATUS: +static int at76c651_read_status(struct dvb_frontend* fe, fe_status_t* status) { - fe_status_t *status = arg; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; u8 sync; /* * Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0) */ - sync = at76c651_readreg(i2c, 0x80); - + sync = at76c651_readreg(state, 0x80); *status = 0; if (sync & (0x04 | 0x10)) /* AGC1 || TIM */ @@ -388,186 +326,138 @@ static int at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) if ((sync & 0xF0) == 0xF0) /* TIM && EQU && CAR && FEC */ *status |= FE_HAS_LOCK; - break; - + return 0; } - case FE_READ_BER: +static int at76c651_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = arg; - *ber = (at76c651_readreg(i2c, 0x81) & 0x0F) << 16; - *ber |= at76c651_readreg(i2c, 0x82) << 8; - *ber |= at76c651_readreg(i2c, 0x83); + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; + + *ber = (at76c651_readreg(state, 0x81) & 0x0F) << 16; + *ber |= at76c651_readreg(state, 0x82) << 8; + *ber |= at76c651_readreg(state, 0x83); *ber *= 10; - break; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int at76c651_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u8 gain = ~at76c651_readreg(i2c, 0x91); - *(u16 *) arg = (gain << 8) | gain; - break; - } + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - case FE_READ_SNR: - *(u16 *)arg = 0xFFFF - - ((at76c651_readreg(i2c, 0x8F) << 8) | - at76c651_readreg(i2c, 0x90)); - break; + u8 gain = ~at76c651_readreg(state, 0x91); + *strength = (gain << 8) | gain; - case FE_READ_UNCORRECTED_BLOCKS: - *(u32 *)arg = at76c651_readreg(i2c, 0x82); - break; + return 0; +} - case FE_SET_FRONTEND: - return at76c651_set_parameters(state, arg); +static int at76c651_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - case FE_GET_FRONTEND: - break; + *snr = 0xFFFF - + ((at76c651_readreg(state, 0x8F) << 8) | + at76c651_readreg(state, 0x90)); - case FE_SLEEP: - break; - case FE_INIT: - return at76c651_set_defaults(state); + return 0; +} - case FE_GET_TUNE_SETTINGS: +static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - struct dvb_frontend_tune_settings *fesettings = arg; - fesettings->min_delay_ms = 50; - fesettings->step_size = 0; - fesettings->max_drift = 0; - break; - } + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - default: - return -ENOIOCTLCMD; - } + *ucblocks = at76c651_readreg(state, 0x82); return 0; } -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings) { - struct at76c651_state *state; - struct i2c_client *client; - int ret; - - if (at76c651_readreg(adapter, 0x0E) != 0x65) - return -ENODEV; - - if (!(state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL))) - return -ENOMEM; - - state->i2c = adapter; - state->revision = at76c651_readreg(adapter, 0x0F) & 0xFE; - - switch (state->revision) { - case 0x10: - at76c651_info.name[14] = 'A'; - break; - case 0x11: - at76c651_info.name[14] = 'B'; - break; - default: - kfree(state); - return -ENODEV; - } - - if (!(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; + fesettings->min_delay_ms = 50; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0x1a >> 1; - i2c_set_clientdata(client, state); - - ret = i2c_attach_client(client); - if (ret) { +static void at76c651_release(struct dvb_frontend* fe) +{ + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; kfree(state); - kfree(client); - return ret; } - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(at76c651_ioctl, state->dvb, state, - &at76c651_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; - } +static struct dvb_frontend_ops at76c651_ops; - return 0; - } - -static int detach_client(struct i2c_client *client) +struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, + struct i2c_adapter* i2c) { - struct at76c651_state *state = i2c_get_clientdata(client); + struct at76c651_state* state = NULL; - dvb_unregister_frontend(at76c651_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); + /* allocate memory for the internal state */ + state = (struct at76c651_state*) kmalloc(sizeof(struct at76c651_state), GFP_KERNEL); + if (state == NULL) goto error; - return 0; -} + /* setup the state */ + state->config = config; + state->qam = 0; -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct at76c651_state *state = i2c_get_clientdata(client); + /* check if the demod is there */ + if (at76c651_readreg(state, 0x0e) != 0x65) goto error; - switch (cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} + /* finalise state setup */ + state->i2c = i2c; + state->revision = at76c651_readreg(state, 0x0f) & 0xfe; + memcpy(&state->ops, &at76c651_ops, sizeof(struct dvb_frontend_ops)); -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_AT76C651, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; -static int __init at76c651_init(void) -{ - return i2c_add_driver(&driver); +error: + if (state) kfree(state); + return NULL; } -static void __exit at76c651_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "at76c651: driver deregistration failed.\n"); -} +static struct dvb_frontend_ops at76c651_ops = { + + .info = { + .name = "Atmel AT76C651B DVB-C", + .type = FE_QAM, + .frequency_min = 48250000, + .frequency_max = 863250000, + .frequency_stepsize = 62500, + /*.frequency_tolerance = */ /* FIXME: 12% of SR */ + .symbol_rate_min = 0, /* FIXME */ + .symbol_rate_max = 9360000, /* FIXME */ + .symbol_rate_tolerance = 4000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | + FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER + }, + + .release = at76c651_release, + + .init = at76c651_set_defaults, + + .set_frontend = at76c651_set_parameters, + .get_tune_settings = at76c651_get_tune_settings, + + .read_status = at76c651_read_status, + .read_ber = at76c651_read_ber, + .read_signal_strength = at76c651_read_signal_strength, + .read_snr = at76c651_read_snr, + .read_ucblocks = at76c651_read_ucblocks, +}; -module_init(at76c651_init); -module_exit(at76c651_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("at76c651/tua6010xs dvb-c frontend driver"); +MODULE_DESCRIPTION("Atmel AT76C651 DVB-C Demodulator Driver"); MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(at76c651_attach); diff --git a/drivers/media/dvb/frontends/at76c651.h b/drivers/media/dvb/frontends/at76c651.h new file mode 100644 index 000000000000..34054df93608 --- /dev/null +++ b/drivers/media/dvb/frontends/at76c651.h @@ -0,0 +1,47 @@ +/* + * at76c651.c + * + * Atmel DVB-C Frontend Driver (at76c651) + * + * Copyright (C) 2001 fnbrd <fnbrd@gmx.de> + * & 2002-2004 Andreas Oberritter <obi@linuxtv.org> + * & 2003 Wolfram Joost <dbox2@frokaschwei.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * AT76C651 + * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf + * http://www.atmel.com/atmel/acrobat/doc1320.pdf + */ + +#ifndef AT76C651_H +#define AT76C651_H + +#include <linux/dvb/frontend.h> + +struct at76c651_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, + struct i2c_adapter* i2c); + +#endif // AT76C651_H diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c new file mode 100644 index 000000000000..26744e71f873 --- /dev/null +++ b/drivers/media/dvb/frontends/cx22700.c @@ -0,0 +1,445 @@ +#/* + Conexant cx22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "dvb_frontend.h" +#include "cx22700.h" + + +struct cx22700_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct cx22700_config* config; + + struct dvb_frontend frontend; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx22700: " args); \ + } while (0) + +static u8 init_tab [] = { + 0x04, 0x10, + 0x05, 0x09, + 0x06, 0x00, + 0x08, 0x04, + 0x09, 0x00, + 0x0a, 0x01, + 0x15, 0x40, + 0x16, 0x10, + 0x17, 0x87, + 0x18, 0x17, + 0x1a, 0x10, + 0x25, 0x04, + 0x2e, 0x00, + 0x39, 0x00, + 0x3a, 0x04, + 0x45, 0x08, + 0x46, 0x02, + 0x47, 0x05, +}; + + +static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + dprintk ("%s\n", __FUNCTION__); + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int cx22700_readreg (struct cx22700_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk ("%s\n", __FUNCTION__); + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) return -EIO; + + return b1[0]; +} + +static int cx22700_set_inversion (struct cx22700_state* state, int inversion) +{ + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + switch (inversion) { + case INVERSION_AUTO: + return -EOPNOTSUPP; + case INVERSION_ON: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val | 0x01); + case INVERSION_OFF: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val & 0xfe); + default: + return -EINVAL; + } +} + +static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p) +{ + static const u8 qam_tab [4] = { 0, 1, 0, 2 }; + static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) + return -EINVAL; + + if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) + + if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) + return -EINVAL; + + if (p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if (p->constellation != QPSK && + p->constellation != QAM_16 && + p->constellation != QAM_64) + return -EINVAL; + + if (p->hierarchy_information < HIERARCHY_NONE || + p->hierarchy_information > HIERARCHY_4) + return -EINVAL; + + if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ) + return -EINVAL; + + if (p->bandwidth == BANDWIDTH_7_MHZ) + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); + else + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); + + val = qam_tab[p->constellation - QPSK]; + val |= p->hierarchy_information - HIERARCHY_NONE; + + cx22700_writereg (state, 0x04, val); + + val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; + val |= fec_tab[p->code_rate_LP - FEC_1_2]; + + cx22700_writereg (state, 0x05, val); + + val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; + val |= p->transmission_mode - TRANSMISSION_MODE_2K; + + cx22700_writereg (state, 0x06, val); + + cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ + cx22700_writereg (state, 0x08, 0x04); /* restart aquisition */ + + return 0; +} + +static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p) +{ + static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; + static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, + FEC_5_6, FEC_7_8 }; + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ + return -EAGAIN; + + val = cx22700_readreg (state, 0x01); + + if ((val & 0x7) > 4) + p->hierarchy_information = HIERARCHY_AUTO; + else + p->hierarchy_information = HIERARCHY_NONE + (val & 0x7); + + if (((val >> 3) & 0x3) > 2) + p->constellation = QAM_AUTO; + else + p->constellation = qam_tab[(val >> 3) & 0x3]; + + val = cx22700_readreg (state, 0x02); + + if (((val >> 3) & 0x07) > 4) + p->code_rate_HP = FEC_AUTO; + else + p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; + + if ((val & 0x07) > 4) + p->code_rate_LP = FEC_AUTO; + else + p->code_rate_LP = fec_tab[val & 0x07]; + + val = cx22700_readreg (state, 0x03); + + p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); + p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); + + return 0; +} + + + + + + + + + + + +static int cx22700_init (struct dvb_frontend* fe) + +{ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + int i; + + dprintk("cx22700_init: init chip\n"); + + cx22700_writereg (state, 0x00, 0x02); /* soft reset */ + cx22700_writereg (state, 0x00, 0x00); + + msleep(10); + + for (i=0; i<sizeof(init_tab); i+=2) + cx22700_writereg (state, init_tab[i], init_tab[i+1]); + + cx22700_writereg (state, 0x00, 0x01); + + if (state->config->pll_init) { + cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ + state->config->pll_init(fe); + cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + } + + return 0; +} + +static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + u8 sync = cx22700_readreg (state, 0x07); + + *status = 0; + + if (rs_ber < 0xff00) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x20) + *status |= FE_HAS_CARRIER; + + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if (*status == 0x0f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + *ber = cx22700_readreg (state, 0x0c) & 0x7f; + cx22700_writereg (state, 0x0c, 0x00); + + return 0; +} + +static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *signal_strength = ~rs_ber; + + return 0; +} + +static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *snr = ~rs_ber; + + return 0; +} + +static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + *ucblocks = cx22700_readreg (state, 0x0f); + cx22700_writereg (state, 0x0f, 0x00); + + return 0; +} + +static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ + cx22700_writereg (state, 0x00, 0x00); + + cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ + state->config->pll_set(fe, p); + cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + cx22700_set_inversion (state, p->inversion); + cx22700_set_tps (state, &p->u.ofdm); + cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ + cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ + + return 0; +} + +static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + u8 reg09 = cx22700_readreg (state, 0x09); + + p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22700_get_tps (state, &p->u.ofdm); +} + +static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 150; + fesettings->step_size = 166667; + fesettings->max_drift = 166667*2; + return 0; +} + +static void cx22700_release(struct dvb_frontend* fe) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops cx22700_ops; + +struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c) +{ + struct cx22700_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if (cx22700_readreg(state, 0x07) < 0) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops cx22700_ops = { + + .info = { + .name = "Conexant CX22700 DVB-T", + .type = FE_OFDM, + .frequency_min = 470000000, + .frequency_max = 860000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_RECOVER + }, + + .release = cx22700_release, + + .init = cx22700_init, + + .set_frontend = cx22700_set_frontend, + .get_frontend = cx22700_get_frontend, + .get_tune_settings = cx22700_get_tune_settings, + + .read_status = cx22700_read_status, + .read_ber = cx22700_read_ber, + .read_signal_strength = cx22700_read_signal_strength, + .read_snr = cx22700_read_snr, + .read_ucblocks = cx22700_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(cx22700_attach); diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h new file mode 100644 index 000000000000..c9145b45874b --- /dev/null +++ b/drivers/media/dvb/frontends/cx22700.h @@ -0,0 +1,41 @@ +/* + Conexant CX22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef CX22700_H +#define CX22700_H + +#include <linux/dvb/frontend.h> + +struct cx22700_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c); + +#endif // CX22700_H diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index bf6d6778ce24..15ae90316c1f 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -1,8 +1,8 @@ /* - Conexant 22702 DVB OFDM frontend driver + Conexant 22702 DVB OFDM demodulator driver based on: - Alps TDMB7 DVB OFDM frontend driver + Alps TDMB7 DVB OFDM demodulator driver Copyright (C) 2001-2002 Convergence Integrated Media GmbH Holger Waechtler <holger@convergence.de> @@ -31,44 +31,28 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/delay.h> - #include "dvb_frontend.h" +#include "cx22702.h" -#define FRONTEND_NAME "dvbfe_cx22702" -#define I2C_EEPROM_SLAVE_ADDR 0x50 +struct cx22702_state { -#define PLLTYPE_DTT7592 1 -#define PLLTYPE_DTT7595 2 -#define PLLTYPE_DTT7579 3 + struct i2c_adapter* i2c; -static int debug = 0; + struct dvb_frontend_ops ops; -#define dprintk if (debug) printk + /* configuration settings */ + const struct cx22702_config* config; -static struct dvb_frontend_info cx22702_info = { - .name = "CX22702 Demod Thomson 759x/7579 PLL", - .type = FE_OFDM, - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER -}; + struct dvb_frontend frontend; -struct cx22702_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - struct dvb_frontend_info cx22702_info; - char pll_type; - int pll_addr; - int demod_addr; + /* previous uncorrected block counter */ u8 prevUCBlocks; }; +static int debug = 0; +#define dprintk if (debug) printk + /* Register values to initialise the demod */ static u8 init_tab [] = { 0x00, 0x00, /* Stop aquisition */ @@ -99,15 +83,13 @@ static u8 init_tab [] = { 0xfd, 0x00, }; -static struct i2c_client client_template; - -static int cx22702_writereg (struct i2c_adapter *i2c, int demod_addr, u8 reg, u8 data) +static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data) { int ret; u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = demod_addr, .flags = 0, .buf = buf, .len = 2 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", @@ -116,17 +98,17 @@ static int cx22702_writereg (struct i2c_adapter *i2c, int demod_addr, u8 reg, u8 return (ret != 1) ? -1 : 0; } -static u8 cx22702_readreg (struct i2c_adapter *i2c, int demod_addr, u8 reg) +static u8 cx22702_readreg (struct cx22702_state* state, u8 reg) { int ret; u8 b0 [] = { reg }; u8 b1 [] = { 0 }; struct i2c_msg msg [] = { - { .addr = demod_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -134,232 +116,121 @@ static u8 cx22702_readreg (struct i2c_adapter *i2c, int demod_addr, u8 reg) return b1[0]; } -static int pll_readreg(struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 reg) +static int cx22702_set_inversion (struct cx22702_state *state, int inversion) { - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; + u8 val; - struct i2c_msg msg [] = { - { .addr = pll_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } - }; - - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe); // Enable PLL bus - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c pll request failed\n", __FUNCTION__); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus - return -ENODEV; - } - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus + switch (inversion) { - return b1[0]; -} + case INVERSION_AUTO: + return -EOPNOTSUPP; -static int pll_write (struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 data [4]) -{ - int ret=0; - struct i2c_msg msg = { .addr = pll_addr, .flags = 0, .buf = data, .len = 4 }; + case INVERSION_ON: + val = cx22702_readreg (state, 0x0C); + return cx22702_writereg (state, 0x0C, val | 0x01); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe); // Enable PLL bus - ret = i2c_transfer(i2c, &msg, 1); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus + case INVERSION_OFF: + val = cx22702_readreg (state, 0x0C); + return cx22702_writereg (state, 0x0C, val & 0xfe); - if (ret != 1) - printk("%s: i/o error (addr == 0x%02x, ret == %i)\n", __FUNCTION__, msg.addr, ret); + default: + return -EINVAL; + + } - return (ret != 1) ? -1 : 0; } -static int pll_dtt759x_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth) +/* Retrieve the demod settings */ +static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p) { - int ret; - - u32 div = (freq + 36166667) / 166666; - - /* dividerhigh, dividerlow, control, bandwidth switch tuner args */ - unsigned char buf [4] = { - (div >> 8) & 0x7f, - div & 0xff, - 0x84, - 0x00 - }; + u8 val; - if(freq < 470000000) { - buf[3] = 0x02; - } else { - buf[3] = 0x08; - } + /* Make sure the TPS regs are valid */ + if (!(cx22702_readreg(state, 0x0A) & 0x20)) + return -EAGAIN; - if(bandwidth == BANDWIDTH_7_MHZ) { - buf[3] |= 0x10; + val = cx22702_readreg (state, 0x01); + switch( (val&0x18)>>3) { + case 0: p->constellation = QPSK; break; + case 1: p->constellation = QAM_16; break; + case 2: p->constellation = QAM_64; break; } - - // Now compensate for the charge pump osc - if(freq <= 264000000) { - buf[2] = buf[2] | 0x30; - } else if (freq <= 735000000) { - buf[2] = buf[2] | 0x38; - } else if (freq <= 835000000) { - buf[2] = buf[2] | 0x70; - } else if (freq <= 896000000) { - buf[2] = buf[2] | 0x78; + switch( val&0x07 ) { + case 0: p->hierarchy_information = HIERARCHY_NONE; break; + case 1: p->hierarchy_information = HIERARCHY_1; break; + case 2: p->hierarchy_information = HIERARCHY_2; break; + case 3: p->hierarchy_information = HIERARCHY_4; break; } - dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div); - ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: first pll_write failed\n",__FUNCTION__); - return ret; - } - - /* Set the AGC during search */ - buf[2]=(buf[2] & 0xc7) | 0x18; - buf[3]=0xa0; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: second pll_write failed\n",__FUNCTION__); - return ret; + val = cx22702_readreg (state, 0x02); + switch( (val&0x38)>>3 ) { + case 0: p->code_rate_HP = FEC_1_2; break; + case 1: p->code_rate_HP = FEC_2_3; break; + case 2: p->code_rate_HP = FEC_3_4; break; + case 3: p->code_rate_HP = FEC_5_6; break; + case 4: p->code_rate_HP = FEC_7_8; break; } - - /* Tuner needs a small amount of time */ - msleep(100); - - /* Set the AGC post-search */ - buf[3]=0x20; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: third pll_write failed\n",__FUNCTION__); - return ret; + switch( val&0x07 ) { + case 0: p->code_rate_LP = FEC_1_2; break; + case 1: p->code_rate_LP = FEC_2_3; break; + case 2: p->code_rate_LP = FEC_3_4; break; + case 3: p->code_rate_LP = FEC_5_6; break; + case 4: p->code_rate_LP = FEC_7_8; break; } - return ret; + val = cx22702_readreg (state, 0x03); + switch( (val&0x0c)>>2 ) { + case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; } - -static int pll_dtt7579_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth) -{ - int ret; - - u32 div = (freq + 36166667) / 166666; - - /* dividerhigh, dividerlow */ - unsigned char buf [4] = { - div >> 8, - div & 0xff, - 0x00, - 0x00 - }; - - // FIXME: bandwidth setting unknown - - // Now compensate for the charge pump osc - if(freq <= 506000000) { - buf[2] = 0xb4; - buf[3] = 0x02; - } else if (freq <= 735000000) { - buf[2] = 0xbc; - buf[3] = 0x08; - } else if (freq <= 835000000) { - buf[2] = 0xf4; - buf[3] = 0x08; - } else if (freq <= 896000000) { - buf[2] = 0xfc; - buf[3] = 0x08; - } - - dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div); - - ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: first pll_write failed\n",__FUNCTION__); - return ret; - } - - /* Set the AGC to search */ - buf[2]=(buf[2] & 0xdc) | 0x9c; - buf[3]=0xa0; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: second pll_write failed\n",__FUNCTION__); - return ret; + switch( val&0x03 ) { + case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; } - return ret; - + return 0; } -/* Reset the demod hardware and reset all of the configuration registers - to a default state. */ -static int cx22702_init (struct i2c_adapter *i2c, struct cx22702_state *state) -{ - int i; - - cx22702_writereg (i2c, state->demod_addr, 0x00, 0x02); - msleep(10); - for (i=0; i<sizeof(init_tab); i+=2) - cx22702_writereg (i2c, state->demod_addr, init_tab[i], init_tab[i+1]); - return 0; -} -static int cx22702_set_inversion (struct i2c_adapter *i2c, struct cx22702_state *state, int inversion) -{ - u8 val; - switch (inversion) { - case INVERSION_AUTO: - return -EOPNOTSUPP; - case INVERSION_ON: - val = cx22702_readreg (i2c, state->demod_addr, 0x0C); - return cx22702_writereg (i2c, state->demod_addr, 0x0C, val | 0x01); - case INVERSION_OFF: - val = cx22702_readreg (i2c, state->demod_addr, 0x0C); - return cx22702_writereg (i2c, state->demod_addr, 0x0C, val & 0xfe); - default: - return -EINVAL; - } -} /* Talk to the demod, set the FEC, GUARD, QAM settings etc */ -static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state, - struct dvb_frontend_parameters *p) +static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { u8 val; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; /* set PLL */ - switch(state->pll_type) { - case PLLTYPE_DTT7592: - case PLLTYPE_DTT7595: - pll_dtt759x_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth); - break; - - case PLLTYPE_DTT7579: - pll_dtt7579_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth); - break; - } + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe); + state->config->pll_set(fe, p); + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1); /* set inversion */ - cx22702_set_inversion (i2c, state, p->inversion); + cx22702_set_inversion (state, p->inversion); /* set bandwidth */ switch(p->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x20 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 ); break; case BANDWIDTH_7_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x10 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 ); break; case BANDWIDTH_8_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, cx22702_readreg(i2c, state->demod_addr, 0x0C) &0xcf ); + cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf ); break; default: dprintk ("%s: invalid bandwidth\n",__FUNCTION__); @@ -378,12 +249,12 @@ static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) { /* TPS Source - use hardware driven values */ - cx22702_writereg(i2c, state->demod_addr, 0x06, 0x10); - cx22702_writereg(i2c, state->demod_addr, 0x07, 0x9); - cx22702_writereg(i2c, state->demod_addr, 0x08, 0xC1); - cx22702_writereg(i2c, state->demod_addr, 0x0B, cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc ); - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 ); - cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01); /* Begin aquisition */ + cx22702_writereg(state, 0x06, 0x10); + cx22702_writereg(state, 0x07, 0x9); + cx22702_writereg(state, 0x08, 0xC1); + cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 ); + cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */ printk("%s: Autodetecting\n",__FUNCTION__); return 0; } @@ -407,7 +278,7 @@ static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state dprintk ("%s: invalid hierarchy\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x06, val); + cx22702_writereg (state, 0x06, val); val=0; switch(p->u.ofdm.code_rate_HP) { @@ -432,7 +303,7 @@ static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x07, val); + cx22702_writereg (state, 0x07, val); val=0; switch(p->u.ofdm.guard_interval) { @@ -451,93 +322,52 @@ static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state dprintk ("%s: invalid transmission_mode\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x08, val); - cx22702_writereg(i2c, state->demod_addr, 0x0B, (cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc) | 0x02 ); - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 ); + cx22702_writereg(state, 0x08, val); + cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 ); /* Begin channel aquisition */ - cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01); + cx22702_writereg(state, 0x00, 0x01); return 0; } -/* Retrieve the demod settings */ -static int cx22702_get_tps (struct i2c_adapter *i2c, struct cx22702_state *state, - struct dvb_ofdm_parameters *p) + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int cx22702_init (struct dvb_frontend* fe) { - u8 val; + int i; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - /* Make sure the TPS regs are valid */ - if (!(cx22702_readreg(i2c, state->demod_addr, 0x0A) & 0x20)) - return -EAGAIN; - - val = cx22702_readreg (i2c, state->demod_addr, 0x01); - switch( (val&0x18)>>3) { - case 0: p->constellation = QPSK; break; - case 1: p->constellation = QAM_16; break; - case 2: p->constellation = QAM_64; break; - } - switch( val&0x07 ) { - case 0: p->hierarchy_information = HIERARCHY_NONE; break; - case 1: p->hierarchy_information = HIERARCHY_1; break; - case 2: p->hierarchy_information = HIERARCHY_2; break; - case 3: p->hierarchy_information = HIERARCHY_4; break; - } + cx22702_writereg (state, 0x00, 0x02); + msleep(10); - val = cx22702_readreg (i2c, state->demod_addr, 0x02); - switch( (val&0x38)>>3 ) { - case 0: p->code_rate_HP = FEC_1_2; break; - case 1: p->code_rate_HP = FEC_2_3; break; - case 2: p->code_rate_HP = FEC_3_4; break; - case 3: p->code_rate_HP = FEC_5_6; break; - case 4: p->code_rate_HP = FEC_7_8; break; - } - switch( val&0x07 ) { - case 0: p->code_rate_LP = FEC_1_2; break; - case 1: p->code_rate_LP = FEC_2_3; break; - case 2: p->code_rate_LP = FEC_3_4; break; - case 3: p->code_rate_LP = FEC_5_6; break; - case 4: p->code_rate_LP = FEC_7_8; break; - } + for (i=0; i<sizeof(init_tab); i+=2) + cx22702_writereg (state, init_tab[i], init_tab[i+1]); - val = cx22702_readreg (i2c, state->demod_addr, 0x03); - switch( (val&0x0c)>>2 ) { - case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; - case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; - case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; - case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; - } - switch( val&0x03 ) { - case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; - case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; + /* init PLL */ + if (state->config->pll_init) { + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe); + state->config->pll_init(fe); + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1); } return 0; } -static int cx22702_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct cx22702_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; u8 reg0A; u8 reg23; - u8 ucblocks; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &state->cx22702_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; *status = 0; - reg0A = cx22702_readreg (i2c, state->demod_addr, 0x0A); - reg23 = cx22702_readreg (i2c, state->demod_addr, 0x23); + reg0A = cx22702_readreg (state, 0x0A); + reg23 = cx22702_readreg (state, 0x23); dprintk ("%s: status demod=0x%02x agc=0x%02x\n" ,__FUNCTION__,reg0A,reg23); @@ -554,347 +384,149 @@ static int cx22702_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if(reg23 < 0xf0) *status |= FE_HAS_SIGNAL; - break; - - } - - case FE_READ_BER: - if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) { - /* Realtime statistics */ - *((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | (cx22702_readreg (i2c, state->demod_addr, 0xDF)&0x7F); - } else { - /* Averagtine statistics */ - *((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | cx22702_readreg (i2c, state->demod_addr, 0xDF); + return 0; } - break; - case FE_READ_SIGNAL_STRENGTH: +static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber) { - u16 ss = cx22702_readreg (i2c, state->demod_addr, 0x23); - *((u16*) arg) = ss; - break; - } + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - /* We don't have an register for this */ - /* We'll take the inverse of the BER register */ - case FE_READ_SNR: - { - u16 rs_ber=0; - if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) { + if(cx22702_readreg (state, 0xE4) & 0x02) { /* Realtime statistics */ - rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | (cx22702_readreg (i2c, state->demod_addr, 0xDF)& 0x7F); + *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg (state, 0xDF)&0x7F); } else { - /* Averagine statistics */ - rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 8 - | cx22702_readreg (i2c, state->demod_addr, 0xDF); - } - *((u16*) arg) = ~rs_ber; - break; + /* Averagtine statistics */ + *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | cx22702_readreg (state, 0xDF); } - case FE_READ_UNCORRECTED_BLOCKS: - /* RS Uncorrectable Packet Count then reset */ - ucblocks = cx22702_readreg (i2c, state->demod_addr, 0xE3); - if (state->prevUCBlocks < ucblocks) *((u32*) arg) = (ucblocks - state->prevUCBlocks); - else *((u32*) arg) = state->prevUCBlocks - ucblocks; - state->prevUCBlocks = ucblocks; - break; - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - int ret; - - if((ret=cx22702_set_tps (i2c, state, p))<0) { - dprintk ("%s: set_tps failed ret=%d\n",__FUNCTION__,ret); - return ret; - } - break; + return 0; } - case FE_GET_FRONTEND: +static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) { - struct dvb_frontend_parameters *p = arg; - u8 reg0C = cx22702_readreg (i2c, state->demod_addr, 0x0C); - - p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; - return cx22702_get_tps (i2c, state, &p->u.ofdm); - } + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - case FE_INIT: - return cx22702_init (i2c, state); - - default: - return -EOPNOTSUPP; - }; + *signal_strength = cx22702_readreg (state, 0x23); return 0; } -/* Validate the eeprom contents, make sure content look ok. - Get the eeprom data. */ -static int cx22702_validate_eeprom(struct i2c_adapter *i2c, int* minfreq, int* pll_type, int* pll_addr, int* demod_addr) +static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr) { - u8 b0 [] = { 0 }; - u8 b1 [128]; - u32 model=0; - u8 tuner=0; - int i,j; - - struct i2c_msg msg [] = { - { .addr = I2C_EEPROM_SLAVE_ADDR, .flags = 0, .buf = b0, .len = 1 }, - { .addr = I2C_EEPROM_SLAVE_ADDR, .flags = I2C_M_RD, .buf = b1, .len = 128 } - }; - - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c eeprom request failed\n", __FUNCTION__); - return -ENODEV; - } - - if(debug) { - dprintk ("i2c eeprom content:\n"); - j=0; - for(i=0;i<128;i++) { - dprintk("%02x ",b1[i]); - if(j++==16) { - dprintk("\n"); - j=0; - } - } - dprintk("\n"); - } + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - if( (b1[8]!=0x84) || (b1[10]!=0x00) ) { - printk ("%s eeprom content is not valid\n", __FUNCTION__); - return -ENODEV; - } - - /* Make sure we support the board model */ - model = b1[0x1f] << 24 | b1[0x1e] << 16 | b1[0x1d] << 8 | b1[0x1c]; - switch(model) { - case 90002: - case 90500: - case 90501: - dprintk ("%s: Model #%d\n",__FUNCTION__,model); - break; - default: - printk ("%s: Unknown model #%d not supported\n",__FUNCTION__,model); - return -ENODEV; + u16 rs_ber=0; + if(cx22702_readreg (state, 0xE4) & 0x02) { + /* Realtime statistics */ + rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg (state, 0xDF)& 0x7F); + } else { + /* Averagine statistics */ + rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8 + | cx22702_readreg (state, 0xDF); } + *snr = ~rs_ber; - /* Make sure we support the tuner */ - tuner = b1[0x2d]; - switch(tuner) { - case 0x4B: - dprintk ("%s: Tuner Thomson DTT 7595\n",__FUNCTION__); - *minfreq = 177000000; - *pll_type = PLLTYPE_DTT7595; - break; - case 0x4C: - dprintk ("%s: Tuner Thomson DTT 7592\n",__FUNCTION__); - *minfreq = 474000000; - *pll_type = PLLTYPE_DTT7592; - break; - default: - printk ("%s: Unknown tuner 0x%02x not supported\n",__FUNCTION__,tuner); - return -ENODEV; - } - *pll_addr = 0x61; - *demod_addr = 0x43; return 0; } - -/* Validate the demod, make sure we understand the hardware */ -static int cx22702_validate_demod(struct i2c_adapter *i2c, int demod_addr) +static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - u8 b0 [] = { 0x1f }; - u8 b1 [] = { 0 }; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - struct i2c_msg msg [] = { - { .addr = demod_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } - }; - - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c demod request failed\n", __FUNCTION__); - return -ENODEV; - } + u8 _ucblocks; - if( (b1[0]!=0x3) ) { - printk ("%s i2c demod type 0x(%02x) not known\n", __FUNCTION__,b1[0]); - return -ENODEV; - } + /* RS Uncorrectable Packet Count then reset */ + _ucblocks = cx22702_readreg (state, 0xE3); + if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks); + else *ucblocks = state->prevUCBlocks - _ucblocks; + state->prevUCBlocks = _ucblocks; return 0; } -/* Validate the tuner PLL, make sure we understand the hardware */ -static int cx22702_validate_pll(struct i2c_adapter *i2c, int pll_addr, int demod_addr) +static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - int result=0; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - if( (result=pll_readreg(i2c,pll_addr,demod_addr,0xc2)) < 0) - return result; + u8 reg0C = cx22702_readreg (state, 0x0C); - if( (result >= 0) && (result&0x30) ) - return 0; - - return result; + p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22702_get_tps (state, &p->u.ofdm); } -/* Check we can see the I2c clients */ -static int cx22702_attach_adapter(struct i2c_adapter *adapter) +static void cx22702_release(struct dvb_frontend* fe) { - struct cx22702_state *state; - struct i2c_client *client; - int ret; - int minfreq; - int pll_type; - int pll_addr; - int demod_addr; - - if (0 == (adapter->class & I2C_CLASS_TV_DIGITAL)) { - dprintk("Ignoring adapter 0x%x:%s (no digital tv card).\n", - adapter->id, adapter->name); - return 0; - } - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if (!strcmp(adapter->name, "Conexant DVB-T reference design")) { - printk("cx22702: Detected Conexant DVB-T card - PLL Thomson DTT7579\n"); - pll_type = PLLTYPE_DTT7579; - pll_addr = 0x60; - demod_addr = 0x43; - minfreq = 177000000; // guess - } else { - // default to Hauppauge Nova-T for the moment - printk("cx22702: Detected Hauppauge Nova-T DVB-T - PLL Thomson DTT759x\n"); - ret=cx22702_validate_eeprom(adapter, &minfreq, &pll_type, &pll_addr, &demod_addr); - if(ret < 0) - return ret; - } - - ret=cx22702_validate_demod(adapter, demod_addr); - if(ret < 0) - return ret; - - ret=cx22702_validate_pll(adapter, pll_addr, demod_addr); - if(ret < 0) - return ret; - - if ( !(state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct cx22702_state)); - state->i2c = adapter; - memcpy(&state->cx22702_info, &cx22702_info, sizeof(struct dvb_frontend_info)); - state->cx22702_info.frequency_min = minfreq; - state->pll_type = pll_type; - state->pll_addr = pll_addr; - state->demod_addr = demod_addr; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; kfree(state); - return -ENOMEM; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = state->demod_addr; - i2c_set_clientdata(client, state); +static struct dvb_frontend_ops cx22702_ops; - if ((ret = i2c_attach_client(client))) { - printk("cx22702: attach failed %i\n", ret); - kfree(client); - kfree(state); - return ret; - } - return 0; -} - -static int cx22702_detach_client(struct i2c_client *client) +struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, + struct i2c_adapter* i2c) { - struct cx22702_state *state = i2c_get_clientdata(client); + struct cx22702_state* state = NULL; - if (NULL != state->dvb) { - dvb_unregister_frontend (cx22702_ioctl, state->dvb); - state->dvb = NULL; - } - i2c_detach_client(client); - kfree(client); - return 0; -} + /* allocate memory for the internal state */ + state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL); + if (state == NULL) goto error; -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct cx22702_state *state = i2c_get_clientdata(client); - int rc; - - switch(cmd) { - case FE_REGISTER: - if (NULL != state->dvb) - break; - state->dvb = arg; - rc = dvb_register_frontend(cx22702_ioctl, state->dvb, state, - &state->cx22702_info, THIS_MODULE); - if (0 != rc) { - printk("cx22702: dvb_register_frontend failed with rc=%d\n",rc); - state->dvb = NULL; - return rc; - } - break; - case FE_UNREGISTER: - if (NULL == state->dvb) - break; - dvb_unregister_frontend (cx22702_ioctl, state->dvb); - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_CX22702, - .flags = I2C_DF_NOTIFY, - .attach_adapter = cx22702_attach_adapter, - .detach_client = cx22702_detach_client, - .command = command, -}; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops)); + state->prevUCBlocks = 0; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + /* check if the demod is there */ + if (cx22702_readreg(state, 0x1f) != 0x3) goto error; -static int __init init_cx22702 (void) -{ - return i2c_add_driver(&driver); -} + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; -static void __exit exit_cx22702 (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "cx22702: driver deregistration failed.\n"); +error: + if (state) kfree(state); + return NULL; } -module_init (init_cx22702); -module_exit (exit_cx22702); +static struct dvb_frontend_ops cx22702_ops = { + + .info = { + .name = "Conexant CX22702 DVB-T", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = cx22702_release, + + .init = cx22702_init, + + .set_frontend = cx22702_set_tps, + .get_frontend = cx22702_get_frontend, + + .read_status = cx22702_read_status, + .read_ber = cx22702_read_ber, + .read_signal_strength = cx22702_read_signal_strength, + .read_snr = cx22702_read_snr, + .read_ucblocks = cx22702_read_ucblocks, +}; -MODULE_PARM(debug,"i"); +module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Enable verbose debug messages"); -MODULE_DESCRIPTION("CX22702 / Thomson DTT 759x / Thomson DTT 7579 PLL DVB Frontend driver"); + +MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(cx22702_attach); diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h new file mode 100644 index 000000000000..6e34f997aba2 --- /dev/null +++ b/drivers/media/dvb/frontends/cx22702.h @@ -0,0 +1,46 @@ +/* + Conexant 22702 DVB OFDM demodulator driver + + based on: + Alps TDMB7 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk> + + 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 CX22702_H +#define CX22702_H + +#include <linux/dvb/frontend.h> + +struct cx22702_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, + struct i2c_adapter* i2c); + +#endif // CX22702_H diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index 32e36122d278..f8d5ec9ea11f 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -1,6 +1,5 @@ /* cx24110 - Single Chip Satellite Channel Receiver driver module - used on the the Pinnacle PCTV Sat cards Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on work @@ -23,15 +22,6 @@ */ -/* currently drives the Conexant cx24110 and cx24106 QPSK decoder chips, - connected via i2c to a Conexant Fusion 878 (this uses the standard - linux bttv driver). The tuner chip is supposed to be the Conexant - cx24108 digital satellite tuner, driven through the tuner interface - of the cx24110. SEC is also supplied by the cx24110. - - Oct-2002: Migrate to API V3 (formerly known as NEWSTRUCT) -*/ - #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> @@ -39,44 +29,30 @@ #include <linux/init.h> #include "dvb_frontend.h" +#include "cx24110.h" -#define FRONTEND_NAME "dvbfe_cx24110" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct cx24110_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + const struct cx24110_config* config; -static struct dvb_frontend_info cx24110_info = { - .name = "Conexant CX24110 with CX24108 tuner, aka HM1221/HM1811", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends, can be reduced - to 253kHz on the cx24108 tuner */ - .frequency_tolerance = 29500, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, -/* .symbol_rate_tolerance = ???,*/ - .notifier_delay = 50, /* 1/20 s */ - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_RECOVER -}; -/* fixme: are these values correct? especially ..._tolerance and caps */ + struct dvb_frontend frontend; -struct cx24110_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; + u32 lastber; + u32 lastbler; + u32 lastesn0; }; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx24110: " args); \ + } while (0) + static struct {u8 reg; u8 data;} cx24110_regdata[]= /* Comments beginning with @ denote this value should be the default */ @@ -140,15 +116,13 @@ static struct {u8 reg; u8 data;} cx24110_regdata[]= }; -static int cx24110_writereg (struct i2c_adapter *i2c, int reg, int data) +static int cx24110_writereg (struct cx24110_state* state, int reg, int data) { u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x55, .flags = 0, .buf = buf, .len = 2 }; -/* fixme (medium): HW allows any i2c address. 0x55 is the default, but the - cx24110 might show up at any address */ + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; int err; - if ((err = i2c_transfer(i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x," " data == 0x%02x)\n", __FUNCTION__, err, reg, data); return -EREMOTEIO; @@ -158,161 +132,46 @@ static int cx24110_writereg (struct i2c_adapter *i2c, int reg, int data) } -static u8 cx24110_readreg (struct i2c_adapter *i2c, u8 reg) +static int cx24110_readreg (struct cx24110_state* state, u8 reg) { int ret; u8 b0 [] = { reg }; u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; -/* fixme (medium): address might be different from 0x55 */ - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - - -static int cx24108_write (struct i2c_adapter *i2c, u32 data) -{ -/* tuner data is 21 bits long, must be left-aligned in data */ -/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ -/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ - -dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data); - - cx24110_writereg(i2c,0x6d,0x30); /* auto mode at 62kHz */ - cx24110_writereg(i2c,0x70,0x15); /* auto mode 21 bits */ - /* if the auto tuner writer is still busy, clear it out */ - while (cx24110_readreg(i2c,0x6d)&0x80) - cx24110_writereg(i2c,0x72,0); - /* write the topmost 8 bits */ - cx24110_writereg(i2c,0x72,(data>>24)&0xff); - /* wait for the send to be completed */ - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* send another 8 bytes */ - cx24110_writereg(i2c,0x72,(data>>16)&0xff); - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* and the topmost 5 bits of this byte */ - cx24110_writereg(i2c,0x72,(data>>8)&0xff); - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* now strobe the enable line once */ - cx24110_writereg(i2c,0x6d,0x32); - cx24110_writereg(i2c,0x6d,0x30); - - return 0; -} - - -static int cx24108_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ -/* fixme (low): error handling */ - int i, a, n, pump; - u32 band, pll; - - - static const u32 osci[]={ 950000,1019000,1075000,1178000, - 1296000,1432000,1576000,1718000, - 1856000,2036000,2150000}; - static const u32 bandsel[]={0,0x00020000,0x00040000,0x00100800, - 0x00101000,0x00102000,0x00104000, - 0x00108000,0x00110000,0x00120000, - 0x00140000}; - -#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ - dprintk("cx24110 debug: cx24108_set_tv_freq, freq=%d\n",freq); - - 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++) - ; - dprintk("cx24110 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 */ - dprintk("cx24110 debug: pump=%d, n=%d, a=%d\n",pump,n,a); - cx24108_write(i2c,band); - /* set vga and vca to their widest-band settings, as a precaution. - SetSymbolrate might not be called to set this up */ - cx24108_write(i2c,0x500c0000); - cx24108_write(i2c,0x83f1f800); - cx24108_write(i2c,pll); - cx24110_writereg(i2c,0x56,0x7f); - - msleep(10); /* wait a moment for the tuner pll to lock */ - - /* tuner pll lock can be monitored on GPIO pin 4 of cx24110 */ - while (!(cx24110_readreg(i2c,0x66)&0x20)&&i<1000) - i++; - dprintk("cx24110 debug: GPIO IN=%2.2x(loop=%d)\n", - cx24110_readreg(i2c,0x66),i); - return 0; - -} - + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; -static int cx24110_initfe(struct i2c_adapter *i2c) -{ -/* fixme (low): error handling */ - int i; - - dprintk("%s: init chip\n", __FUNCTION__); + ret = i2c_transfer(state->i2c, msg, 2); - for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) { - cx24110_writereg(i2c,cx24110_regdata[i].reg,cx24110_regdata[i].data); - }; + if (ret != 2) return ret; - return 0; + return b1[0]; } - -static int cx24110_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion) +static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion) { /* fixme (low): error handling */ switch (inversion) { case INVERSION_OFF: - cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x1); + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); /* AcqSpectrInvDis on. No idea why someone should want this */ - cx24110_writereg(i2c,0x5,cx24110_readreg(i2c,0x5)&0xf7); + cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7); /* Initial value 0 at start of acq */ - cx24110_writereg(i2c,0x22,cx24110_readreg(i2c,0x22)&0xef); + cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef); /* current value 0 */ /* The cx24110 manual tells us this reg is read-only. But what the heck... set it ayways */ break; case INVERSION_ON: - cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x1); + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); /* AcqSpectrInvDis on. No idea why someone should want this */ - cx24110_writereg(i2c,0x5,cx24110_readreg(i2c,0x5)|0x08); + cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08); /* Initial value 1 at start of acq */ - cx24110_writereg(i2c,0x22,cx24110_readreg(i2c,0x22)|0x10); + cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10); /* current value 1 */ break; case INVERSION_AUTO: - cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)&0xfe); + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe); /* AcqSpectrInvDis off. Leave initial & current states as is */ break; default: @@ -323,7 +182,7 @@ static int cx24110_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion } -static int cx24110_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) +static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec) { /* fixme (low): error handling */ @@ -339,27 +198,27 @@ static int cx24110_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) fec=FEC_AUTO; if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */ - cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)&0xdf); + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf); /* clear AcqVitDis bit */ - cx24110_writereg(i2c,0x18,0xae); + cx24110_writereg(state,0x18,0xae); /* allow all DVB standard code rates */ - cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|0x3); + cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3); /* set nominal Viterbi rate 3/4 */ - cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|0x3); + cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3); /* set current Viterbi rate 3/4 */ - cx24110_writereg(i2c,0x1a,0x05); cx24110_writereg(i2c,0x1b,0x06); + cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06); /* set the puncture registers for code rate 3/4 */ return 0; } else { - cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x20); + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20); /* set AcqVitDis bit */ if(rate[fec]>0) { - cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|rate[fec]); + cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]); /* set nominal Viterbi rate */ - cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|rate[fec]); + cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]); /* set current Viterbi rate */ - cx24110_writereg(i2c,0x1a,g1[fec]); - cx24110_writereg(i2c,0x1b,g2[fec]); + cx24110_writereg(state,0x1a,g1[fec]); + cx24110_writereg(state,0x1b,g2[fec]); /* not sure if this is the right way: I always used AutoAcq mode */ } else return -EOPNOTSUPP; @@ -369,11 +228,11 @@ static int cx24110_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) } -static fe_code_rate_t cx24110_get_fec (struct i2c_adapter *i2c) +static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) { int i; - i=cx24110_readreg(i2c,0x22)&0x0f; + i=cx24110_readreg(state,0x22)&0x0f; if(!(i&0x08)) { return FEC_1_2 + i - 1; } else { @@ -386,16 +245,13 @@ static fe_code_rate_t cx24110_get_fec (struct i2c_adapter *i2c) } -static int cx24110_set_symbolrate (struct i2c_adapter *i2c, u32 srate) +static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) { /* fixme (low): add error handling */ u32 ratio; u32 tmp, fclk, BDRI; static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; - static const u32 vca[]={0x80f03800,0x81f0f800,0x83f1f800}; - static const u32 vga[]={0x5f8fc000,0x580f0000,0x500c0000}; - static const u8 filtune[]={0xa2,0xcc,0x66}; int i; dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); @@ -409,22 +265,22 @@ dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, R06[3:0] PLLphaseDetGain */ - tmp=cx24110_readreg(i2c,0x07)&0xfc; + tmp=cx24110_readreg(state,0x07)&0xfc; if(srate<90999000UL/4) { /* sample rate 45MHz*/ - cx24110_writereg(i2c,0x07,tmp); - cx24110_writereg(i2c,0x06,0x78); + cx24110_writereg(state,0x07,tmp); + cx24110_writereg(state,0x06,0x78); fclk=90999000UL/2; } else if(srate<60666000UL/2) { /* sample rate 60MHz */ - cx24110_writereg(i2c,0x07,tmp|0x1); - cx24110_writereg(i2c,0x06,0xa5); + cx24110_writereg(state,0x07,tmp|0x1); + cx24110_writereg(state,0x06,0xa5); fclk=60666000UL; } else if(srate<80888000UL/2) { /* sample rate 80MHz */ - cx24110_writereg(i2c,0x07,tmp|0x2); - cx24110_writereg(i2c,0x06,0x87); + cx24110_writereg(state,0x07,tmp|0x2); + cx24110_writereg(state,0x06,0x87); fclk=80888000UL; } else { /* sample rate 90MHz */ - cx24110_writereg(i2c,0x07,tmp|0x3); - cx24110_writereg(i2c,0x06,0x78); + cx24110_writereg(state,0x07,tmp|0x3); + cx24110_writereg(state,0x06,0x78); fclk=90999000UL; }; dprintk("cx24110 debug: fclk %d Hz\n",fclk); @@ -453,64 +309,123 @@ dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); dprintk("fclk = %d\n", fclk); dprintk("ratio= %08x\n", ratio); - cx24110_writereg(i2c, 0x1, (ratio>>16)&0xff); - cx24110_writereg(i2c, 0x2, (ratio>>8)&0xff); - cx24110_writereg(i2c, 0x3, (ratio)&0xff); + cx24110_writereg(state, 0x1, (ratio>>16)&0xff); + cx24110_writereg(state, 0x2, (ratio>>8)&0xff); + cx24110_writereg(state, 0x3, (ratio)&0xff); + + return 0; + +} + + - /* please see the cx24108 data sheet, this controls tuner gain - and bandwidth settings depending on the symbol rate */ - cx24108_write(i2c,vga[i]); - cx24108_write(i2c,vca[i]); /* gain is set on tuner chip */ - cx24110_writereg(i2c,0x56,filtune[i]); /* bw is contolled by filtune voltage */ + + + + + + + + + +int cx24110_pll_write (struct dvb_frontend* fe, u32 data) +{ + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + +/* tuner data is 21 bits long, must be left-aligned in data */ +/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ +/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ + + dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data); + + cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ + cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ + + /* if the auto tuner writer is still busy, clear it out */ + while (cx24110_readreg(state,0x6d)&0x80) + cx24110_writereg(state,0x72,0); + + /* write the topmost 8 bits */ + cx24110_writereg(state,0x72,(data>>24)&0xff); + + /* wait for the send to be completed */ + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* send another 8 bytes */ + cx24110_writereg(state,0x72,(data>>16)&0xff); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* and the topmost 5 bits of this byte */ + cx24110_writereg(state,0x72,(data>>8)&0xff); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* now strobe the enable line once */ + cx24110_writereg(state,0x6d,0x32); + cx24110_writereg(state,0x6d,0x30); return 0; +} + + + +static int cx24110_initfe(struct dvb_frontend* fe) +{ + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; +/* fixme (low): error handling */ + int i; + + dprintk("%s: init chip\n", __FUNCTION__); + + for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) { + cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); + }; + + if (state->config->pll_init) state->config->pll_init(fe); + return 0; } -static int cx24110_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage) +static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + switch (voltage) { case SEC_VOLTAGE_13: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0xc0); + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); case SEC_VOLTAGE_18: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0x40); + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); default: return -EINVAL; }; } -static void cx24110_send_diseqc_msg(struct i2c_adapter *i2c, +static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { int i, rv; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; for (i = 0; i < cmd->msg_len; i++) - cx24110_writereg(i2c, 0x79 + i, cmd->msg[i]); + cx24110_writereg(state, 0x79 + i, cmd->msg[i]); - rv = cx24110_readreg(i2c, 0x76); + rv = cx24110_readreg(state, 0x76); - cx24110_writereg(i2c, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); - for (i=500; i-- > 0 && !(cx24110_readreg(i2c,0x76)&0x40);) + cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); + for (i=500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40);) ; /* wait for LNB ready */ -} + return 0; +} -static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct cx24110_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - static int lastber=0, lastbyer=0,lastbler=0, lastesn0=0, sum_bler=0; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &cx24110_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = arg; - int sync = cx24110_readreg (i2c, 0x55); + int sync = cx24110_readreg (state, 0x55); *status = 0; @@ -520,7 +435,7 @@ static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if (sync & 0x08) *status |= FE_HAS_CARRIER; - sync = cx24110_readreg (i2c, 0x08); + sync = cx24110_readreg (state, 0x08); if (sync & 0x40) *status |= FE_HAS_VITERBI; @@ -531,87 +446,97 @@ static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if ((sync & 0x60) == 0x60) *status |= FE_HAS_LOCK; - if(cx24110_readreg(i2c,0x10)&0x40) { - /* the RS error counter has finished one counting window */ - cx24110_writereg(i2c,0x10,0x60); /* select the byer reg */ - lastbyer=cx24110_readreg(i2c,0x12)| - (cx24110_readreg(i2c,0x13)<<8)| - (cx24110_readreg(i2c,0x14)<<16); - cx24110_writereg(i2c,0x10,0x70); /* select the bler reg */ - lastbler=cx24110_readreg(i2c,0x12)| - (cx24110_readreg(i2c,0x13)<<8)| - (cx24110_readreg(i2c,0x14)<<16); - cx24110_writereg(i2c,0x10,0x20); /* start new count window */ - sum_bler += lastbler; - } - if(cx24110_readreg(i2c,0x24)&0x10) { - /* the Viterbi error counter has finished one counting window */ - cx24110_writereg(i2c,0x24,0x04); /* select the ber reg */ - lastber=cx24110_readreg(i2c,0x25)| - (cx24110_readreg(i2c,0x26)<<8); - cx24110_writereg(i2c,0x24,0x04); /* start new count window */ - cx24110_writereg(i2c,0x24,0x14); - } - if(cx24110_readreg(i2c,0x6a)&0x80) { - /* the Es/N0 error counter has finished one counting window */ - lastesn0=cx24110_readreg(i2c,0x69)| - (cx24110_readreg(i2c,0x68)<<8); - cx24110_writereg(i2c,0x6a,0x84); /* start new count window */ - } - break; + return 0; } - case FE_READ_BER: +static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - *ber = lastber; /* fixme (maybe): value range is 16 bit. Scale? */ - break; + if(cx24110_readreg(state,0x24)&0x10) { + /* the Viterbi error counter has finished one counting window */ + cx24110_writereg(state,0x24,0x04); /* select the ber reg */ + state->lastber=cx24110_readreg(state,0x25)| + (cx24110_readreg(state,0x26)<<8); + cx24110_writereg(state,0x24,0x04); /* start new count window */ + cx24110_writereg(state,0x24,0x14); + } + *ber = state->lastber; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + /* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ - u8 signal = cx24110_readreg (i2c, 0x27)+128; - *((u16*) arg) = (signal << 8) | signal; - break; + u8 signal = cx24110_readreg (state, 0x27)+128; + *signal_strength = (signal << 8) | signal; + + return 0; } - case FE_READ_SNR: +static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ - *(u16*) arg = lastesn0; - break; + if(cx24110_readreg(state,0x6a)&0x80) { + /* the Es/N0 error counter has finished one counting window */ + state->lastesn0=cx24110_readreg(state,0x69)| + (cx24110_readreg(state,0x68)<<8); + cx24110_writereg(state,0x6a,0x84); /* start new count window */ } + *snr = state->lastesn0; - case FE_READ_UNCORRECTED_BLOCKS: + return 0; + } + +static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - *(u16*) arg = sum_bler&0xffff; - sum_bler=0; - break; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + u32 lastbyer; + + if(cx24110_readreg(state,0x10)&0x40) { + /* the RS error counter has finished one counting window */ + cx24110_writereg(state,0x10,0x60); /* select the byer reg */ + lastbyer=cx24110_readreg(state,0x12)| + (cx24110_readreg(state,0x13)<<8)| + (cx24110_readreg(state,0x14)<<16); + cx24110_writereg(state,0x10,0x70); /* select the bler reg */ + state->lastbler=cx24110_readreg(state,0x12)| + (cx24110_readreg(state,0x13)<<8)| + (cx24110_readreg(state,0x14)<<16); + cx24110_writereg(state,0x10,0x20); /* start new count window */ } + *ucblocks = state->lastbler; - case FE_SET_FRONTEND: + return 0; +} + +static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - cx24108_set_tv_freq (i2c, p->frequency); - cx24110_set_inversion (i2c, p->inversion); - cx24110_set_fec (i2c, p->u.qpsk.fec_inner); - cx24110_set_symbolrate (i2c, p->u.qpsk.symbol_rate); - cx24110_writereg(i2c,0x04,0x05); /* start aquisition */ - break; + state->config->pll_set(fe, p); + cx24110_set_inversion (state, p->inversion); + cx24110_set_fec (state, p->u.qpsk.fec_inner); + cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate); + cx24110_writereg(state,0x04,0x05); /* start aquisition */ + + return 0; } - case FE_GET_FRONTEND: +static int cx24110_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; s32 afc; unsigned sclk; /* cannot read back tuner settings (freq). Need to have some private storage */ - sclk = cx24110_readreg (i2c, 0x07) & 0x03; + sclk = cx24110_readreg (state, 0x07) & 0x03; /* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. * Need 64 bit arithmetic. Is thiss possible in the kernel? */ if (sclk==0) sclk=90999000L/2L; @@ -619,152 +544,104 @@ static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) else if (sclk==2) sclk=80888000L; else sclk=90999000L; sclk>>=8; - afc = sclk*(cx24110_readreg (i2c, 0x44)&0x1f)+ - ((sclk*cx24110_readreg (i2c, 0x45))>>8)+ - ((sclk*cx24110_readreg (i2c, 0x46))>>16); + afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ + ((sclk*cx24110_readreg (state, 0x45))>>8)+ + ((sclk*cx24110_readreg (state, 0x46))>>16); p->frequency += afc; - p->inversion = (cx24110_readreg (i2c, 0x22) & 0x10) ? + p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? INVERSION_ON : INVERSION_OFF; - p->u.qpsk.fec_inner = cx24110_get_fec (i2c); - break; - } - - case FE_SLEEP: -/* cannot do this from the FE end. How to communicate this to the place where it can be done? */ - break; - case FE_INIT: - return cx24110_initfe(i2c); - - case FE_SET_TONE: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&~0x10)|((((fe_sec_tone_mode_t) arg)==SEC_TONE_ON)?0x10:0)); - case FE_SET_VOLTAGE: - return cx24110_set_voltage (i2c, (fe_sec_voltage_t) arg); - - case FE_DISEQC_SEND_MASTER_CMD: - // FIXME Status? - cx24110_send_diseqc_msg(i2c, (struct dvb_diseqc_master_cmd*) arg); - return 0; - - default: - return -EOPNOTSUPP; - }; + p->u.qpsk.fec_inner = cx24110_get_fec (state); return 0; } -static struct i2c_client client_template; - -static int attach_adapter (struct i2c_adapter *adapter) +static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - struct cx24110_state *state; - struct i2c_client *client; - int ret = 0; - u8 sig; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - sig = cx24110_readreg (adapter, 0x00); - if ( sig != 0x5a && sig != 0x69 ) - return -ENODEV; - - if ( !(state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct cx24110_state)); - state->i2c = adapter; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0x55; - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(client); +static void cx24110_release(struct dvb_frontend* fe) +{ + struct cx24110_state* state = (struct cx24110_state*) fe->demodulator_priv; kfree(state); - return ret; } - BUG_ON(!state->dvb); +static struct dvb_frontend_ops cx24110_ops; - if ((ret = dvb_register_frontend(cx24110_ioctl, state->dvb, state, - &cx24110_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; -} - - return 0; -} - -static int detach_client (struct i2c_client *client) +struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c) { - struct cx24110_state *state = i2c_get_clientdata(client); - - dvb_unregister_frontend(cx24110_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct cx24110_state *state = i2c_get_clientdata(client); - - switch(cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } + struct cx24110_state* state = NULL; + int ret; - return 0; + /* allocate memory for the internal state */ + state = (struct cx24110_state*) kmalloc(sizeof(struct cx24110_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); + state->lastber = 0; + state->lastbler = 0; + state->lastesn0 = 0; + + /* check if the demod is there */ + ret = cx24110_readreg(state, 0x00); + if ((ret != 0x5a) && (ret != 0x69)) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_CX24110, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, +static struct dvb_frontend_ops cx24110_ops = { + + .info = { + .name = "Conexant CX24110 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24110_release, + + .init = cx24110_initfe, + .set_frontend = cx24110_set_frontend, + .get_frontend = cx24110_get_frontend, + .read_status = cx24110_read_status, + .read_ber = cx24110_read_ber, + .read_signal_strength = cx24110_read_signal_strength, + .read_snr = cx24110_read_snr, + .read_ucblocks = cx24110_read_ucblocks, + + .diseqc_send_master_cmd = cx24110_send_diseqc_msg, + .set_tone = cx24110_set_tone, + .set_voltage = cx24110_set_voltage, }; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init cx24110_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit cx24110_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "cx24110: driver deregistration failed.\n"); -} - -module_init(cx24110_init); -module_exit(cx24110_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("DVB Frontend driver module for the Conexant cx24108/cx24110 chipset"); +MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); MODULE_AUTHOR("Peter Hettkamp"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(cx24110_attach); +EXPORT_SYMBOL(cx24110_pll_write); diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h new file mode 100644 index 000000000000..6b663f4744e0 --- /dev/null +++ b/drivers/media/dvb/frontends/cx24110.h @@ -0,0 +1,45 @@ +/* + cx24110 - Single Chip Satellite Channel Receiver driver module + + Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on + work + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef CX24110_H +#define CX24110_H + +#include <linux/dvb/frontend.h> + +struct cx24110_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c); + +extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data); + +#endif // CX24110_H diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c new file mode 100644 index 000000000000..3a1b8929f6fe --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000-common.c @@ -0,0 +1,145 @@ +#include "dib3000-common.h" + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +static int debug; +module_param(debug, int, 0x644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able))."); +#endif +#define deb_info(args...) dprintk(0x01,args) +#define deb_i2c(args...) dprintk(0x02,args) +#define deb_srch(args...) dprintk(0x04,args) + + +int dib3000_read_reg(struct dib3000_state *state, u16 reg) +{ + u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) + deb_i2c("i2c read error\n"); + + deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, + (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); + + return (rb[0] << 8) | rb[1]; +} + +int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) +{ + u8 b[] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } + }; + deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); + + return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; +} + +int dib3000_init_pid_list(struct dib3000_state *state, int num) +{ + int i; + if (state != NULL) { + state->pid_list = kmalloc(sizeof(struct dib3000_pid) * num,GFP_KERNEL); + if (state->pid_list == NULL) + return -ENOMEM; + + deb_info("initializing %d pids for the pid_list.\n",num); + state->pid_list_lock = SPIN_LOCK_UNLOCKED; + memset(state->pid_list,0,num*(sizeof(struct dib3000_pid))); + for (i=0; i < num; i++) { + state->pid_list[i].pid = 0; + state->pid_list[i].active = 0; + } + state->feedcount = 0; + } else + return -EINVAL; + + return 0; +} + +void dib3000_dealloc_pid_list(struct dib3000_state *state) +{ + if (state != NULL && state->pid_list != NULL) + kfree(state->pid_list); +} + +/* fetch a pid from pid_list */ +int dib3000_get_pid_index(struct dib3000_pid pid_list[], int num_pids, int pid, + spinlock_t *pid_list_lock,int onoff) +{ + int i,ret = -1; + unsigned long flags; + + spin_lock_irqsave(pid_list_lock,flags); + for (i=0; i < num_pids; i++) + if (onoff) { + if (!pid_list[i].active) { + pid_list[i].pid = pid; + pid_list[i].active = 1; + ret = i; + break; + } + } else { + if (pid_list[i].active && pid_list[i].pid == pid) { + pid_list[i].pid = 0; + pid_list[i].active = 0; + ret = i; + break; + } + } + + deb_info("setting pid: %5d %04x at index %d '%s'\n",pid,pid,ret,onoff ? "on" : "off"); + + spin_unlock_irqrestore(pid_list_lock,flags); + return ret; +} + +int dib3000_search_status(u16 irq,u16 lock) +{ + if (irq & 0x02) { + if (lock & 0x01) { + deb_srch("auto search succeeded\n"); + return 1; // auto search succeeded + } else { + deb_srch("auto search not successful\n"); + return 0; // auto search failed + } + } else if (irq & 0x01) { + deb_srch("auto search failed\n"); + return 0; // auto search failed + } + return -1; // try again +} + +/* for auto search */ +u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ + { /* fft */ + { /* gua */ + { 0, 1 }, /* 0 0 { 0,1 } */ + { 3, 9 }, /* 0 1 { 0,1 } */ + }, + { + { 2, 5 }, /* 1 0 { 0,1 } */ + { 6, 11 }, /* 1 1 { 0,1 } */ + } + }; + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de"); +MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb frontend drivers"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(dib3000_seq); + +EXPORT_SYMBOL(dib3000_read_reg); +EXPORT_SYMBOL(dib3000_write_reg); +EXPORT_SYMBOL(dib3000_init_pid_list); +EXPORT_SYMBOL(dib3000_dealloc_pid_list); +EXPORT_SYMBOL(dib3000_get_pid_index); +EXPORT_SYMBOL(dib3000_search_status); diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h new file mode 100644 index 000000000000..2537a1218783 --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000-common.h @@ -0,0 +1,153 @@ +/* + * .h-files for the common use of the frontend drivers made by DiBcom + * DiBcom 3000-MB/MC/P + * + * DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/dvb/README.dibusb for more information + * + */ + +#ifndef DIB3000_COMMON_H +#define DIB3000_COMMON_H + +#include "dvb_frontend.h" +#include "dib3000.h" + +/* info and err, taken from usb.h, if there is anything available like by default, + * please change ! + */ +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) + +/* a PID for the pid_filter list, when in use */ +struct dib3000_pid +{ + u16 pid; + int active; +}; + +/* frontend state */ +struct dib3000_state { + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + +/* configuration settings */ + struct dib3000_config config; + + spinlock_t pid_list_lock; + struct dib3000_pid *pid_list; + + int feedcount; + + struct dvb_frontend frontend; + int timing_offset; + int timing_offset_comp_done; +}; + +/* commonly used methods by the dib3000mb/mc/p frontend */ +extern int dib3000_read_reg(struct dib3000_state *state, u16 reg); +extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val); + +extern int dib3000_init_pid_list(struct dib3000_state *state, int num); +extern void dib3000_dealloc_pid_list(struct dib3000_state *state); +extern int dib3000_get_pid_index(struct dib3000_pid pid_list[], int num_pids, + int pid, spinlock_t *pid_list_lock,int onoff); + +extern int dib3000_search_status(u16 irq,u16 lock); + +/* handy shortcuts */ +#define rd(reg) dib3000_read_reg(state,reg) + +#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ + { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } + +#define wr_foreach(a,v) { int i; \ + if (sizeof(a) != sizeof(v)) \ + err("sizeof: %d %d is different",sizeof(a),sizeof(v));\ + for (i=0; i < sizeof(a)/sizeof(u16); i++) \ + wr(a[i],v[i]); \ + } + +#define set_or(reg,val) wr(reg,rd(reg) | val) + +#define set_and(reg,val) wr(reg,rd(reg) & val) + + +/* debug */ + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +#define dprintk(level,args...) \ + do { if ((debug & level)) { printk(args); } } while (0) +#else +#define dprintk(args...) do { } while (0) +#endif + +/* mask for enabling a specific pid for the pid_filter */ +#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) + +/* common values for tuning */ +#define DIB3000_ALPHA_0 ( 0) +#define DIB3000_ALPHA_1 ( 1) +#define DIB3000_ALPHA_2 ( 2) +#define DIB3000_ALPHA_4 ( 4) + +#define DIB3000_CONSTELLATION_QPSK ( 0) +#define DIB3000_CONSTELLATION_16QAM ( 1) +#define DIB3000_CONSTELLATION_64QAM ( 2) + +#define DIB3000_GUARD_TIME_1_32 ( 0) +#define DIB3000_GUARD_TIME_1_16 ( 1) +#define DIB3000_GUARD_TIME_1_8 ( 2) +#define DIB3000_GUARD_TIME_1_4 ( 3) + +#define DIB3000_TRANSMISSION_MODE_2K ( 0) +#define DIB3000_TRANSMISSION_MODE_8K ( 1) + +#define DIB3000_SELECT_LP ( 0) +#define DIB3000_SELECT_HP ( 1) + +#define DIB3000_FEC_1_2 ( 1) +#define DIB3000_FEC_2_3 ( 2) +#define DIB3000_FEC_3_4 ( 3) +#define DIB3000_FEC_5_6 ( 5) +#define DIB3000_FEC_7_8 ( 7) + +#define DIB3000_HRCH_OFF ( 0) +#define DIB3000_HRCH_ON ( 1) + +#define DIB3000_DDS_INVERSION_OFF ( 0) +#define DIB3000_DDS_INVERSION_ON ( 1) + +#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 7)) +#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 7) | (1 << 7))) + +/* for auto search */ +extern u16 dib3000_seq[2][2][2]; + +#define DIB3000_REG_MANUFACTOR_ID ( 1025) +#define DIB3000_I2C_ID_DIBCOM (0x01b3) + +#define DIB3000_REG_DEVICE_ID ( 1026) +#define DIB3000MB_DEVICE_ID (0x3000) +#define DIB3000MC_DEVICE_ID (0x3001) +#define DIB3000P_DEVICE_ID (0x3002) + +#endif // DIB3000_COMMON_H diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h new file mode 100644 index 000000000000..c6b6cae77f6e --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000.h @@ -0,0 +1,55 @@ +/* + * public header file of the frontend drivers for mobile DVB-T demodulators + * DiBcom 3000-MB and DiBcom 3000-MC/P (http://www.dibcom.fr/) + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/dvb/README.dibusb for more information + * + */ + +#ifndef DIB3000_H +#define DIB3000_H + +#include <linux/dvb/frontend.h> + +struct dib3000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* The i2c address of the PLL */ + u8 pll_addr; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend *fe); + int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params); +}; + +struct dib3000_xfer_ops +{ + /* pid and transfer handling is done in the demodulator */ + int (*pid_parse)(struct dvb_frontend *fe, int onoff); + int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); + int (*pid_ctrl)(struct dvb_frontend *fe, int pid, int onoff); +}; + +extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops); + +extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops); +#endif // DIB3000_H diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index da8fa4f9250b..37eaa7439224 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -17,8 +17,6 @@ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * - * * see Documentation/dvb/README.dibusb for more information * */ @@ -32,319 +30,42 @@ #include <linux/delay.h> #include "dvb_frontend.h" +#include "dib3000-common.h" +#include "dib3000mb_priv.h" +#include "dib3000.h" -#include "dib3000mb.h" - -/* debug */ +/* Version information */ +#define DRIVER_VERSION "0.1" +#define DRIVER_DESC "DiBcom 3000-MB DVB-T demodulator driver" +#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" #ifdef CONFIG_DVB_DIBCOM_DEBUG -#define dprintk(level,args...) \ - do { if ((debug & level)) { printk(args); } } while (0) - static int debug; module_param(debug, int, 0x644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=setfe,16=getfe (|-able))."); -#else -#define dprintk(args...) do { } while (0); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); #endif - #define deb_info(args...) dprintk(0x01,args) #define deb_xfer(args...) dprintk(0x02,args) -#define deb_alot(args...) dprintk(0x04,args) -#define deb_setf(args...) dprintk(0x08,args) -#define deb_getf(args...) dprintk(0x10,args) +#define deb_setf(args...) dprintk(0x04,args) +#define deb_getf(args...) dprintk(0x08,args) -/* Version information */ -#define DRIVER_VERSION "0.1" -#define DRIVER_DESC "DiBcom 3000-MB DVB-T frontend" -#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" - -struct dib3000mb_state { - struct i2c_client *i2c; - struct dvb_adapter *dvb; - u16 manufactor_id; - u16 device_id; -}; - -static struct dvb_frontend_info dib3000mb_info = { - .name = "DiBcom 3000-MB DVB-T", - .type = FE_OFDM, - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; - - -#define rd(reg) dib3000mb_read_reg(state->i2c,reg) -#define wr(reg,val) if (dib3000mb_write_reg(state->i2c,reg,val)) \ - { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } -#define wr_foreach(a,v) { int i; \ - deb_alot("sizeof: %d %d\n",sizeof(a),sizeof(v));\ - for (i=0; i < sizeof(a)/sizeof(u16); i++) \ - wr(a[i],v[i]); \ -} - -static u16 dib3000mb_read_reg(struct i2c_client *i2c, u16 reg) -{ - u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; - u8 rb[2]; - struct i2c_msg msg[] = { - { .addr = i2c->addr, .flags = 0, .buf = wb, .len = 2 }, - { .addr = i2c->addr, .flags = I2C_M_RD, .buf = rb, .len = 2 }, - }; - deb_alot("reading from i2c bus (reg: %d)\n",reg); - - if (i2c_transfer(i2c->adapter,msg,2) != 2) - deb_alot("i2c read error\n"); - - return (rb[0] << 8) | rb[1]; -} - -static int dib3000mb_write_reg(struct i2c_client *i2c,u16 reg, u16 val) -{ - u8 b[] = { - (reg >> 8) & 0xff, reg & 0xff, - (val >> 8) & 0xff, val & 0xff, - }; - struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .buf = b, .len = 4 } }; - deb_alot("writing to i2c bus (reg: %d, val: %d)\n",reg,val); - - return i2c_transfer(i2c->adapter,msg,1) != 1 ? -EREMOTEIO : 0 ; -} - -static int dib3000mb_tuner_thomson_cable_eu(struct dib3000mb_state *state, - u32 freq) -{ - u32 tfreq = (freq + 36125000) / 62500; - unsigned int addr; - int vu,p0,p1,p2; - - if (freq > 403250000) - vu = 1, p2 = 1, p1 = 0, p0 = 1; - else if (freq > 115750000) - vu = 0, p2 = 1, p1 = 1, p0 = 0; - else if (freq > 44250000) - vu = 0, p2 = 0, p1 = 1, p0 = 1; - else - return -EINVAL; - /* TODO better solution for i2c->addr handling */ - addr = state->i2c->addr; - state->i2c->addr = DIB3000MB_TUNER_ADDR_DEFAULT; - wr(tfreq & 0x7fff,(0x8e << 8) + ((vu << 7) | (p2 << 2) | (p1 << 1) | p0) ); - state->i2c->addr = addr; +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep); - return 0; -} - -static int dib3000mb_get_frontend(struct dib3000mb_state *state, - struct dvb_frontend_parameters *fep) -{ - struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; - fe_code_rate_t *cr; - u16 tps_val; - int inv_test1,inv_test2; - u32 dds_val, threshold = 0x800000; - - if (!rd(DIB3000MB_REG_TPS_LOCK)) - return 0; - - dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); - if (dds_val & threshold) - inv_test1 = 0; - else if (dds_val == threshold) - inv_test1 = 1; - else - inv_test1 = 2; - - dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); - if (dds_val & threshold) - inv_test2 = 0; - else if (dds_val == threshold) - inv_test2 = 1; - else - inv_test2 = 2; - - fep->inversion = - ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) - || - ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)); - - deb_getf("inversion %d %d, %d\n",inv_test2,inv_test1, fep->inversion); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { - case DIB3000MB_QAM_QPSK: - deb_getf("QPSK "); - ofdm->constellation = QPSK; - break; - case DIB3000MB_QAM_QAM16: - deb_getf("QAM16 "); - ofdm->constellation = QAM_16; - break; - case DIB3000MB_QAM_QAM64: - deb_getf("QAM64 "); - ofdm->constellation = QAM_64; - break; - default: - err("Unexpected constellation returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - if (rd(DIB3000MB_REG_TPS_HRCH)) { - deb_getf("HRCH ON\n"); - tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); - cr = &ofdm->code_rate_LP; - ofdm->code_rate_HP = FEC_NONE; - - switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { - case DIB3000MB_VIT_ALPHA_OFF: - deb_getf("HIERARCHY_NONE "); - ofdm->hierarchy_information = HIERARCHY_NONE; - break; - case DIB3000MB_VIT_ALPHA_1: - deb_getf("HIERARCHY_1 "); - ofdm->hierarchy_information = HIERARCHY_1; - break; - case DIB3000MB_VIT_ALPHA_2: - deb_getf("HIERARCHY_2 "); - ofdm->hierarchy_information = HIERARCHY_2; - break; - case DIB3000MB_VIT_ALPHA_4: - deb_getf("HIERARCHY_4 "); - ofdm->hierarchy_information = HIERARCHY_4; - break; - default: - err("Unexpected ALPHA value returned by TPS (%d)",tps_val); - } - deb_getf("TPS: %d\n",tps_val); - } else { - deb_getf("HRCH OFF\n"); - tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); - cr = &ofdm->code_rate_HP; - ofdm->code_rate_LP = FEC_NONE; - ofdm->hierarchy_information = HIERARCHY_NONE; - } - - switch (tps_val) { - case DIB3000MB_FEC_1_2: - deb_getf("FEC_1_2 "); - *cr = FEC_1_2; - break; - case DIB3000MB_FEC_2_3: - deb_getf("FEC_2_3 "); - *cr = FEC_2_3; - break; - case DIB3000MB_FEC_3_4: - deb_getf("FEC_3_4 "); - *cr = FEC_3_4; - break; - case DIB3000MB_FEC_5_6: - deb_getf("FEC_5_6 "); - *cr = FEC_4_5; - break; - case DIB3000MB_FEC_7_8: - deb_getf("FEC_7_8 "); - *cr = FEC_7_8; - break; - default: - err("Unexpected FEC returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { - case DIB3000MB_GUARD_TIME_1_32: - deb_getf("GUARD_INTERVAL_1_32 "); - ofdm->guard_interval = GUARD_INTERVAL_1_32; - break; - case DIB3000MB_GUARD_TIME_1_16: - deb_getf("GUARD_INTERVAL_1_16 "); - ofdm->guard_interval = GUARD_INTERVAL_1_16; - break; - case DIB3000MB_GUARD_TIME_1_8: - deb_getf("GUARD_INTERVAL_1_8 "); - ofdm->guard_interval = GUARD_INTERVAL_1_8; - break; - case DIB3000MB_GUARD_TIME_1_4: - deb_getf("GUARD_INTERVAL_1_4 "); - ofdm->guard_interval = GUARD_INTERVAL_1_4; - break; - default: - err("Unexpected Guard Time returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { - case DIB3000MB_FFT_2K: - deb_getf("TRANSMISSION_MODE_2K "); - ofdm->transmission_mode = TRANSMISSION_MODE_2K; - break; - case DIB3000MB_FFT_8K: - deb_getf("TRANSMISSION_MODE_8K "); - ofdm->transmission_mode = TRANSMISSION_MODE_8K; - break; - default: - err("unexpected transmission mode return by TPS (%d)",tps_val); - } - deb_getf("TPS: %d\n",tps_val); - return 0; -} - -static int dib3000mb_set_frontend(struct dib3000mb_state *state, - struct dvb_frontend_parameters *fep, int tuner); - -static int dib3000mb_fe_read_search_status(struct dib3000mb_state *state) -{ - u16 irq; - struct dvb_frontend_parameters fep; - - irq = rd(DIB3000MB_REG_AS_IRQ_PENDING); - - if (irq & 0x02) { - if (rd(DIB3000MB_REG_LOCK2_VALUE) & 0x01) { - if (dib3000mb_get_frontend(state,&fep) == 0) { - deb_setf("reading tuning data from frontend succeeded.\n"); - return dib3000mb_set_frontend(state,&fep,0) == 0; - } else { - deb_setf("reading tuning data failed -> tuning failed.\n"); - return 0; - } - } else { - deb_setf("AS IRQ was pending, but LOCK2 was not & 0x01.\n"); - return 0; - } - } else if (irq & 0x01) { - deb_setf("Autosearch failed.\n"); - return 0; - } - - return -1; -} - -static int dib3000mb_set_frontend(struct dib3000mb_state *state, +static int dib3000mb_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep, int tuner) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; fe_code_rate_t fe_cr = FEC_NONE; int search_state,seq; if (tuner) { wr(DIB3000MB_REG_TUNER, - DIB3000MB_ACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) ); - dib3000mb_tuner_thomson_cable_eu(state,fep->frequency); - - /* wait for tuner */ - msleep(1); + DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr)); + state->config.pll_set(fe, fep); wr(DIB3000MB_REG_TUNER, - DIB3000MB_DEACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) ); + DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr)); deb_setf("bandwidth: "); switch (ofdm->bandwidth) { @@ -376,15 +97,14 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, switch (ofdm->transmission_mode) { case TRANSMISSION_MODE_2K: deb_setf("2k\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_2K); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K); break; case TRANSMISSION_MODE_8K: deb_setf("8k\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_8K); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K); break; case TRANSMISSION_MODE_AUTO: deb_setf("auto\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_AUTO); break; default: return -EINVAL; @@ -394,40 +114,39 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, switch (ofdm->guard_interval) { case GUARD_INTERVAL_1_32: deb_setf("1_32\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_32); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32); break; case GUARD_INTERVAL_1_16: deb_setf("1_16\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_16); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16); break; case GUARD_INTERVAL_1_8: deb_setf("1_8\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_8); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8); break; case GUARD_INTERVAL_1_4: deb_setf("1_4\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_4); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4); break; case GUARD_INTERVAL_AUTO: deb_setf("auto\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_AUTO); break; default: return -EINVAL; } - deb_setf("invsersion: "); + deb_setf("inversion: "); switch (fep->inversion) { - case INVERSION_AUTO: - deb_setf("auto\n"); - break; case INVERSION_OFF: - deb_setf("on\n"); - wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_OFF); + deb_setf("off\n"); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF); + break; + case INVERSION_AUTO: + deb_setf("auto "); break; case INVERSION_ON: deb_setf("on\n"); - wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_ON); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON); break; default: return -EINVAL; @@ -437,15 +156,15 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, switch (ofdm->constellation) { case QPSK: deb_setf("qpsk\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QPSK); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK); break; case QAM_16: deb_setf("qam16\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM16); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM); break; case QAM_64: deb_setf("qam64\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM64); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM); break; case QAM_AUTO: break; @@ -456,22 +175,21 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, switch (ofdm->hierarchy_information) { case HIERARCHY_NONE: deb_setf("none "); - /* fall through alpha is 1, even when HIERARCHY is NONE */ + /* fall through */ case HIERARCHY_1: deb_setf("alpha=1\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_1); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1); break; case HIERARCHY_2: deb_setf("alpha=2\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_2); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2); break; case HIERARCHY_4: deb_setf("alpha=4\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_4); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4); break; case HIERARCHY_AUTO: deb_setf("alpha=auto\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO); break; default: return -EINVAL; @@ -480,39 +198,40 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, deb_setf("hierarchy: "); if (ofdm->hierarchy_information == HIERARCHY_NONE) { deb_setf("none\n"); - wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_OFF); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP); + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP); fe_cr = ofdm->code_rate_HP; } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) { deb_setf("on\n"); - wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_ON); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_LP); + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP); fe_cr = ofdm->code_rate_LP; } deb_setf("fec: "); switch (fe_cr) { case FEC_1_2: deb_setf("1_2\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2); break; case FEC_2_3: deb_setf("2_3\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_2_3); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3); break; case FEC_3_4: deb_setf("3_4\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_3_4); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4); break; case FEC_5_6: deb_setf("5_6\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_5_6); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6); break; case FEC_7_8: deb_setf("7_8\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_7_8); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8); break; case FEC_NONE: deb_setf("none "); + break; case FEC_AUTO: deb_setf("auto\n"); break; @@ -520,7 +239,7 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, return -EINVAL; } - seq = dib3000mb_seq + seq = dib3000_seq [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO] [ofdm->guard_interval == GUARD_INTERVAL_AUTO] [fep->inversion == INVERSION_AUTO]; @@ -564,6 +283,7 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, ofdm->hierarchy_information == HIERARCHY_AUTO || fe_cr == FEC_AUTO || fep->inversion == INVERSION_AUTO) { + int as_count=0; deb_setf("autosearch enabled.\n"); @@ -572,20 +292,34 @@ static int dib3000mb_set_frontend(struct dib3000mb_state *state, wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_AUTO_SEARCH); wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF); - while ((search_state = dib3000mb_fe_read_search_status(state)) < 0); - deb_info("search_state after autosearch %d\n",search_state); - return search_state ? 0 : -EINVAL; + while ((search_state = + dib3000_search_status( + rd(DIB3000MB_REG_AS_IRQ_PENDING), + rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100) + msleep(1); + + deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count); + + if (search_state == 1) { + struct dvb_frontend_parameters feps; + if (dib3000mb_get_frontend(fe, &feps) == 0) { + deb_setf("reading tuning data from frontend succeeded.\n"); + return dib3000mb_set_frontend(fe, &feps, 0); + } + } + } else { wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_CTRL); wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF); - msleep(70); } + return 0; } - -static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) +static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_UP); wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC); @@ -597,9 +331,6 @@ static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) wr(DIB3000MB_REG_ELECT_OUT_MODE,DIB3000MB_ELECT_OUT_MODE_ON); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_RESERVED); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO); - wr(DIB3000MB_REG_DDS_FREQ_MSB,DIB3000MB_DDS_FREQ_MSB); wr(DIB3000MB_REG_DDS_FREQ_LSB,DIB3000MB_DDS_FREQ_LSB); @@ -621,7 +352,7 @@ static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) wr(DIB3000MB_REG_LOCK0_MASK,DIB3000MB_LOCK0_DEFAULT); wr(DIB3000MB_REG_LOCK1_MASK,DIB3000MB_LOCK1_SEARCH_4); wr(DIB3000MB_REG_LOCK2_MASK,DIB3000MB_LOCK2_DEFAULT); - wr(DIB3000MB_REG_SEQ,dib3000mb_seq[1][1][1]); + wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]); wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_8mhz); @@ -639,8 +370,6 @@ static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) wr(DIB3000MB_REG_UNK_108,DIB3000MB_UNK_108); wr(DIB3000MB_REG_UNK_122,DIB3000MB_UNK_122); wr(DIB3000MB_REG_MOBILE_MODE_QAM,DIB3000MB_MOBILE_MODE_QAM_OFF); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP); wr(DIB3000MB_REG_BERLEN,DIB3000MB_BERLEN_DEFAULT); wr_foreach(dib3000mb_reg_filter_coeffs,dib3000mb_filter_coeffs); @@ -653,17 +382,188 @@ static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) wr(DIB3000MB_REG_FIFO_142,DIB3000MB_FIFO_142); wr(DIB3000MB_REG_MPEG2_OUT_MODE,DIB3000MB_MPEG2_OUT_MODE_188); - wr(DIB3000MB_REG_FIFO_144,DIB3000MB_FIFO_144); + wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE); wr(DIB3000MB_REG_FIFO,DIB3000MB_FIFO_INHIBIT); wr(DIB3000MB_REG_FIFO_146,DIB3000MB_FIFO_146); wr(DIB3000MB_REG_FIFO_147,DIB3000MB_FIFO_147); wr(DIB3000MB_REG_DATA_IN_DIVERSITY,DIB3000MB_DATA_DIVERSITY_IN_OFF); + + if (state->config.pll_init) { + wr(DIB3000MB_REG_TUNER, + DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr)); + state->config.pll_init(fe); + wr(DIB3000MB_REG_TUNER, + DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr)); + } + + return 0; +} + +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; + fe_code_rate_t *cr; + u16 tps_val; + int inv_test1,inv_test2; + u32 dds_val, threshold = 0x800000; + + if (!rd(DIB3000MB_REG_TPS_LOCK)) + return 0; + + dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); + if (dds_val < threshold) + inv_test1 = 0; + else if (dds_val == threshold) + inv_test1 = 1; + else + inv_test1 = 2; + + dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); + if (dds_val < threshold) + inv_test2 = 0; + else if (dds_val == threshold) + inv_test2 = 1; + else + inv_test2 = 2; + + fep->inversion = + ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || + ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? + INVERSION_ON : INVERSION_OFF; + + deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { + case DIB3000_CONSTELLATION_QPSK: + deb_getf("QPSK "); + ofdm->constellation = QPSK; + break; + case DIB3000_CONSTELLATION_16QAM: + deb_getf("QAM16 "); + ofdm->constellation = QAM_16; + break; + case DIB3000_CONSTELLATION_64QAM: + deb_getf("QAM64 "); + ofdm->constellation = QAM_64; + break; + default: + err("Unexpected constellation returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + if (rd(DIB3000MB_REG_TPS_HRCH)) { + deb_getf("HRCH ON\n"); + cr = &ofdm->code_rate_LP; + ofdm->code_rate_HP = FEC_NONE; + switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { + case DIB3000_ALPHA_0: + deb_getf("HIERARCHY_NONE "); + ofdm->hierarchy_information = HIERARCHY_NONE; + break; + case DIB3000_ALPHA_1: + deb_getf("HIERARCHY_1 "); + ofdm->hierarchy_information = HIERARCHY_1; + break; + case DIB3000_ALPHA_2: + deb_getf("HIERARCHY_2 "); + ofdm->hierarchy_information = HIERARCHY_2; + break; + case DIB3000_ALPHA_4: + deb_getf("HIERARCHY_4 "); + ofdm->hierarchy_information = HIERARCHY_4; + break; + default: + err("Unexpected ALPHA value returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); + } else { + deb_getf("HRCH OFF\n"); + cr = &ofdm->code_rate_HP; + ofdm->code_rate_LP = FEC_NONE; + ofdm->hierarchy_information = HIERARCHY_NONE; + + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); + } + + switch (tps_val) { + case DIB3000_FEC_1_2: + deb_getf("FEC_1_2 "); + *cr = FEC_1_2; + break; + case DIB3000_FEC_2_3: + deb_getf("FEC_2_3 "); + *cr = FEC_2_3; + break; + case DIB3000_FEC_3_4: + deb_getf("FEC_3_4 "); + *cr = FEC_3_4; + break; + case DIB3000_FEC_5_6: + deb_getf("FEC_5_6 "); + *cr = FEC_4_5; + break; + case DIB3000_FEC_7_8: + deb_getf("FEC_7_8 "); + *cr = FEC_7_8; + break; + default: + err("Unexpected FEC returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n",tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { + case DIB3000_GUARD_TIME_1_32: + deb_getf("GUARD_INTERVAL_1_32 "); + ofdm->guard_interval = GUARD_INTERVAL_1_32; + break; + case DIB3000_GUARD_TIME_1_16: + deb_getf("GUARD_INTERVAL_1_16 "); + ofdm->guard_interval = GUARD_INTERVAL_1_16; + break; + case DIB3000_GUARD_TIME_1_8: + deb_getf("GUARD_INTERVAL_1_8 "); + ofdm->guard_interval = GUARD_INTERVAL_1_8; + break; + case DIB3000_GUARD_TIME_1_4: + deb_getf("GUARD_INTERVAL_1_4 "); + ofdm->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + err("Unexpected Guard Time returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { + case DIB3000_TRANSMISSION_MODE_2K: + deb_getf("TRANSMISSION_MODE_2K "); + ofdm->transmission_mode = TRANSMISSION_MODE_2K; + break; + case DIB3000_TRANSMISSION_MODE_8K: + deb_getf("TRANSMISSION_MODE_8K "); + ofdm->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + err("unexpected transmission mode return by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + return 0; } -static int dib3000mb_read_status(struct dib3000mb_state *state,fe_status_t *stat) +static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + *stat = 0; if (rd(DIB3000MB_REG_AGC_LOCK)) @@ -699,8 +599,10 @@ static int dib3000mb_read_status(struct dib3000mb_state *state,fe_status_t *stat return 0; } -static int dib3000mb_read_ber(struct dib3000mb_state *state,u32 *ber) +static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB) ); return 0; } @@ -714,8 +616,10 @@ static int dib3000mb_read_ber(struct dib3000mb_state *state,u32 *ber) #define DIB3000MB_AGC_REF_dBm -14 #define DIB3000MB_GAIN_SLOPE_dBm 100 #define DIB3000MB_GAIN_DELTA_dBm -2 -static int dib3000mb_read_signal_strength(struct dib3000mb_state *state, u16 *strength) +static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + /* TODO log10 u16 sigpow = rd(DIB3000MB_REG_SIGNAL_POWER), n_agc_power = rd(DIB3000MB_REG_AGC_POWER), @@ -748,8 +652,9 @@ static int dib3000mb_read_signal_strength(struct dib3000mb_state *state, u16 *st * If SNR is above 20dB, BER should be always 0. * choose 0dB as the minimum */ -static int dib3000mb_read_snr(struct dib3000mb_state *state,u16 *snr) +static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER); int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) | rd(DIB3000MB_REG_NOISE_POWER_LSB); @@ -763,24 +668,27 @@ static int dib3000mb_read_snr(struct dib3000mb_state *state,u16 *snr) *snr = (u16) ((snr_dBm / 35) * 0xffff); */ - *snr = (sigpow<<8) / (icipow > 0 ? icipow : 1); + *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1); return 0; } -static int dib3000mb_read_unc_blocks(struct dib3000mb_state *state,u32 *unc) +static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + *unc = rd(DIB3000MB_REG_UNC); return 0; } -static int dib3000mb_sleep(struct dib3000mb_state *state) +static int dib3000mb_sleep(struct dvb_frontend* fe) { + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_DOWN); return 0; } -static int dib3000mb_fe_get_tune_settings(struct dib3000mb_state *state, - struct dvb_frontend_tune_settings *tune) +static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 800; tune->step_size = 166667; @@ -789,205 +697,137 @@ static int dib3000mb_fe_get_tune_settings(struct dib3000mb_state *state, return 0; } -static int dib3000mb_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe) { - struct dib3000mb_state *state = fe->data; - switch (cmd) { - case FE_GET_INFO: - deb_info("FE_GET_INFO\n"); - memcpy(arg, &dib3000mb_info, sizeof(struct dvb_frontend_info)); - return 0; - break; - - case FE_READ_STATUS: - deb_info("FE_READ_STATUS\n"); - return dib3000mb_read_status(state,(fe_status_t *)arg); - break; - - case FE_READ_BER: - deb_info("FE_READ_BER\n"); - return dib3000mb_read_ber(state,(u32 *)arg); - break; - - case FE_READ_SIGNAL_STRENGTH: - deb_info("FE_READ_SIG_STRENGTH\n"); - return dib3000mb_read_signal_strength(state,(u16 *) arg); - break; - - case FE_READ_SNR: - deb_info("FE_READ_SNR\n"); - return dib3000mb_read_snr(state,(u16 *) arg); - break; - - case FE_READ_UNCORRECTED_BLOCKS: - deb_info("FE_READ_UNCORRECTED_BLOCKS\n"); - return dib3000mb_read_unc_blocks(state,(u32 *) arg); - break; - - case FE_SET_FRONTEND: - deb_info("FE_SET_FRONTEND\n"); - return dib3000mb_set_frontend(state,(struct dvb_frontend_parameters *) arg,1); - break; - - case FE_GET_FRONTEND: - deb_info("FE_GET_FRONTEND\n"); - return dib3000mb_get_frontend(state,(struct dvb_frontend_parameters *) arg); - break; + return dib3000mb_fe_init(fe, 0); +} - case FE_SLEEP: - deb_info("FE_SLEEP\n"); - return dib3000mb_sleep(state); - break; +static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +{ + return dib3000mb_set_frontend(fe, fep, 1); +} - case FE_INIT: - deb_info("FE_INIT\n"); - return dib3000mb_fe_init(state,0); - break; +static void dib3000mb_release(struct dvb_frontend* fe) +{ + struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv; + kfree(state); +} - case FE_GET_TUNE_SETTINGS: - deb_info("GET_TUNE_SETTINGS"); - return dib3000mb_fe_get_tune_settings(state, (struct - dvb_frontend_tune_settings *) arg); +/* pid filter and transfer stuff */ +static int dib3000mb_pid_control(struct dvb_frontend *fe,int pid,int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + int index = dib3000_get_pid_index(state->pid_list, DIB3000MB_NUM_PIDS, pid, &state->pid_list_lock,onoff); + pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); - break; - case FE_SET_TONE: - case FE_SET_VOLTAGE: - default: - return -EOPNOTSUPP; - break; + if (index >= 0) { + wr(index+DIB3000MB_REG_FIRST_PID,pid); + } else { + err("no more pids for filtering."); + return -ENOMEM; } return 0; } -static struct i2c_client client_template; - -static int dib3000mb_attach_adapter(struct i2c_adapter *adapter) +static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff) { - struct i2c_client *client; - struct dib3000mb_state *state; - int ret = -ENOMEM; - - deb_info("i2c probe with adapter '%s'.\n",adapter->name); - - if ((state = kmalloc(sizeof(struct dib3000mb_state),GFP_KERNEL)) == NULL) - return -ENOMEM; - + struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv; - if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL) - goto i2c_kmalloc_err; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - - client->adapter = adapter; - client->addr = 0x10; - state->i2c = client; - - i2c_set_clientdata(client,state); - - state->manufactor_id = dib3000mb_read_reg(client, DIB3000MB_REG_MANUFACTOR_ID); - if (state->manufactor_id != 0x01b3) { - ret = -ENODEV; - goto probe_err; + deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling"); + if (onoff) { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE); + } else { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); } - - state->device_id = dib3000mb_read_reg(client,DIB3000MB_REG_DEVICE_ID); - if (state->device_id != 0x3000) { - ret = -ENODEV; - goto probe_err; + return 0; } - info("found a DiBCom (0x%04x) 3000-MB DVB-T frontend (ver: %x).", - state->manufactor_id, state->device_id); - - if ((ret = i2c_attach_client(client))) - goto i2c_attach_err; - - if (state->dvb == NULL) - goto i2c_attach_err; - - if ((ret = dvb_register_frontend(dib3000mb_ioctl, state->dvb, state, - &dib3000mb_info, THIS_MODULE))) - goto dvb_fe_err; - - - goto success; -dvb_fe_err: - i2c_detach_client(client); -i2c_attach_err: -probe_err: - kfree(client); -i2c_kmalloc_err: - kfree(state); - return ret; -success: +static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff) +{ + //struct dib3000_state *state = fe->demodulator_priv; + /* switch it off and on */ return 0; -} + } +static struct dvb_frontend_ops dib3000mb_ops; -static int dib3000mb_detach_client(struct i2c_client *client) +struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops) { - struct dib3000mb_state *state = i2c_get_clientdata(client); + struct dib3000_state* state = NULL; - deb_info("i2c detach\n"); + /* allocate memory for the internal state */ + state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL); + if (state == NULL) + goto error; - dvb_unregister_frontend(dib3000mb_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config,config,sizeof(struct dib3000_config)); + memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops)); - return 0; -} + /* check for the correct demod */ + if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) + goto error; -static int dib3000mb_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct dib3000mb_state *state = i2c_get_clientdata(client); - deb_info("i2c command.\n"); - switch(cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } + if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID) + goto error; - return 0; -} + if (dib3000_init_pid_list(state,DIB3000MB_NUM_PIDS)) + goto error; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "dib3000mb", - .id = I2C_DRIVERID_DVBFE_DIB3000MB, - .flags = I2C_DF_NOTIFY, - .attach_adapter = dib3000mb_attach_adapter, - .detach_client = dib3000mb_detach_client, - .command = dib3000mb_command, -}; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; -static struct i2c_client client_template = { - .name = "dib3000mb", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + /* set the xfer operations */ + xfer_ops->pid_parse = dib3000mb_pid_parse; + xfer_ops->fifo_ctrl = dib3000mb_fifo_control; + xfer_ops->pid_ctrl = dib3000mb_pid_control; -/* module stuff */ -static int __init dib3000mb_init(void) -{ - deb_info("debugging level: %d\n",debug); - return i2c_add_driver(&driver); -} + return &state->frontend; -static void __exit dib3000mb_exit(void) -{ - i2c_del_driver(&driver); -} +error: + if (state) + kfree(state); + return NULL; + } -module_init (dib3000mb_init); -module_exit (dib3000mb_exit); +static struct dvb_frontend_ops dib3000mb_ops = { + + .info = { + .name = "DiBcom 3000-MB DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib3000mb_release, + + .init = dib3000mb_fe_init_nonmobile, + .sleep = dib3000mb_sleep, + + .set_frontend = dib3000mb_set_frontend_and_tuner, + .get_frontend = dib3000mb_get_frontend, + .get_tune_settings = dib3000mb_fe_get_tune_settings, + + .read_status = dib3000mb_read_status, + .read_ber = dib3000mb_read_ber, + .read_signal_strength = dib3000mb_read_signal_strength, + .read_snr = dib3000mb_read_snr, + .read_ucblocks = dib3000mb_read_unc_blocks, +}; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(dib3000mb_attach); diff --git a/drivers/media/dvb/frontends/dib3000mb.h b/drivers/media/dvb/frontends/dib3000mb_priv.h index 7388cdc8f468..57e61aa5b07b 100644 --- a/drivers/media/dvb/frontends/dib3000mb.h +++ b/drivers/media/dvb/frontends/dib3000mb_priv.h @@ -1,5 +1,5 @@ /* - * dib3000mb.h + * dib3000mb_priv.h * * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) * @@ -7,20 +7,11 @@ * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2. * - * - * * for more information see dib3000mb.c . */ -#ifndef __DIB3000MB_H_INCLUDED__ -#define __DIB3000MB_H_INCLUDED__ - -/* info and err, taken from usb.h, if there is anything available like by default, - * please change ! - */ -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) +#ifndef __DIB3000MB_PRIV_H_INCLUDED__ +#define __DIB3000MB_PRIV_H_INCLUDED__ /* register addresses and some of their default values */ @@ -34,37 +25,18 @@ /* FFT size */ #define DIB3000MB_REG_FFT ( 1) -#define DIB3000MB_FFT_2K ( 0) -#define DIB3000MB_FFT_8K ( 1) -#define DIB3000MB_FFT_AUTO ( 1) /* Guard time */ #define DIB3000MB_REG_GUARD_TIME ( 2) -#define DIB3000MB_GUARD_TIME_1_32 ( 0) -#define DIB3000MB_GUARD_TIME_1_16 ( 1) -#define DIB3000MB_GUARD_TIME_1_8 ( 2) -#define DIB3000MB_GUARD_TIME_1_4 ( 3) -#define DIB3000MB_GUARD_TIME_AUTO ( 0) /* QAM */ #define DIB3000MB_REG_QAM ( 3) -#define DIB3000MB_QAM_QPSK ( 0) -#define DIB3000MB_QAM_QAM16 ( 1) -#define DIB3000MB_QAM_QAM64 ( 2) -#define DIB3000MB_QAM_RESERVED ( 3) /* Alpha coefficient high priority Viterbi algorithm */ #define DIB3000MB_REG_VIT_ALPHA ( 4) -#define DIB3000MB_VIT_ALPHA_OFF ( 0) -#define DIB3000MB_VIT_ALPHA_1 ( 1) -#define DIB3000MB_VIT_ALPHA_2 ( 2) -#define DIB3000MB_VIT_ALPHA_4 ( 4) -#define DIB3000MB_VIT_ALPHA_AUTO ( 7) /* spectrum inversion */ #define DIB3000MB_REG_DDS_INV ( 5) -#define DIB3000MB_DDS_INV_OFF ( 0) -#define DIB3000MB_DDS_INV_ON ( 1) /* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ #define DIB3000MB_REG_DDS_FREQ_MSB ( 6) @@ -73,12 +45,7 @@ #define DIB3000MB_DDS_FREQ_LSB ( 8990) /* timing frequency (carrier spacing) */ -#define DIB3000MB_REG_TIMING_FREQ_MSB ( 8) -#define DIB3000MB_REG_TIMING_FREQ_LSB ( 9) - -static u16 dib3000mb_reg_timing_freq[] = { - DIB3000MB_REG_TIMING_FREQ_MSB, DIB3000MB_REG_TIMING_FREQ_LSB -}; +static u16 dib3000mb_reg_timing_freq[] = { 8,9 }; static u16 dib3000mb_timing_freq[][2] = { { 126 , 48873 }, /* 6 MHz */ { 147 , 57019 }, /* 7 MHz */ @@ -86,14 +53,9 @@ static u16 dib3000mb_timing_freq[][2] = { }; /* impulse noise parameter */ -#define DIB3000MB_REG_IMPNOISE_10 ( 10) -#define DIB3000MB_REG_IMPNOISE_11 ( 11) -#define DIB3000MB_REG_IMPNOISE_12 ( 12) -#define DIB3000MB_REG_IMPNOISE_13 ( 13) -#define DIB3000MB_REG_IMPNOISE_14 ( 14) -#define DIB3000MB_REG_IMPNOISE_15 ( 15) /* 36 ??? */ -#define DIB3000MB_REG_IMPNOISE_36 ( 36) + +static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 }; enum dib3000mb_impulse_noise_type { DIB3000MB_IMPNOISE_OFF, @@ -102,12 +64,6 @@ enum dib3000mb_impulse_noise_type { DIB3000MB_IMPNOISE_DEFAULT }; -static u16 dib3000mb_reg_impulse_noise[] = { - DIB3000MB_REG_IMPNOISE_10, DIB3000MB_REG_IMPNOISE_11, - DIB3000MB_REG_IMPNOISE_12, DIB3000MB_REG_IMPNOISE_15, - DIB3000MB_REG_IMPNOISE_36 -}; - static u16 dib3000mb_impulse_noise_values[][5] = { { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ @@ -122,74 +78,26 @@ static u16 dib3000mb_impulse_noise_values[][5] = { */ /* also from 16 to 18 */ -#define DIB3000MB_REG_AGC_GAIN_19 ( 19) -#define DIB3000MB_REG_AGC_GAIN_20 ( 20) -#define DIB3000MB_REG_AGC_GAIN_21 ( 21) -#define DIB3000MB_REG_AGC_GAIN_22 ( 22) -#define DIB3000MB_REG_AGC_GAIN_23 ( 23) -#define DIB3000MB_REG_AGC_GAIN_24 ( 24) -#define DIB3000MB_REG_AGC_GAIN_25 ( 25) -#define DIB3000MB_REG_AGC_GAIN_26 ( 26) -#define DIB3000MB_REG_AGC_GAIN_27 ( 27) -#define DIB3000MB_REG_AGC_GAIN_28 ( 28) -#define DIB3000MB_REG_AGC_GAIN_29 ( 29) -#define DIB3000MB_REG_AGC_GAIN_30 ( 30) -#define DIB3000MB_REG_AGC_GAIN_31 ( 31) -#define DIB3000MB_REG_AGC_GAIN_32 ( 32) - static u16 dib3000mb_reg_agc_gain[] = { - DIB3000MB_REG_AGC_GAIN_19, DIB3000MB_REG_AGC_GAIN_20, DIB3000MB_REG_AGC_GAIN_21, - DIB3000MB_REG_AGC_GAIN_22, DIB3000MB_REG_AGC_GAIN_23, DIB3000MB_REG_AGC_GAIN_24, - DIB3000MB_REG_AGC_GAIN_25, DIB3000MB_REG_AGC_GAIN_26, DIB3000MB_REG_AGC_GAIN_27, - DIB3000MB_REG_AGC_GAIN_28, DIB3000MB_REG_AGC_GAIN_29, DIB3000MB_REG_AGC_GAIN_30, - DIB3000MB_REG_AGC_GAIN_31, DIB3000MB_REG_AGC_GAIN_32 }; + 19,20,21,22,23,24,25,26,27,28,29,30,31,32 +}; static u16 dib3000mb_default_agc_gain[] = { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ /* phase noise */ -#define DIB3000MB_REG_PHASE_NOISE_33 ( 33) -#define DIB3000MB_REG_PHASE_NOISE_34 ( 34) -#define DIB3000MB_REG_PHASE_NOISE_35 ( 35) -#define DIB3000MB_REG_PHASE_NOISE_36 ( 36) -#define DIB3000MB_REG_PHASE_NOISE_37 ( 37) -#define DIB3000MB_REG_PHASE_NOISE_38 ( 38) - -/* DIB3000MB_REG_PHASE_NOISE_36 is set when setting the impulse noise */ -static u16 dib3000mb_reg_phase_noise[] = { - DIB3000MB_REG_PHASE_NOISE_33, DIB3000MB_REG_PHASE_NOISE_34, DIB3000MB_REG_PHASE_NOISE_35, - DIB3000MB_REG_PHASE_NOISE_37, DIB3000MB_REG_PHASE_NOISE_38 -}; +/* 36 is set when setting the impulse noise */ +static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 }; static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; /* lock duration */ -#define DIB3000MB_REG_LOCK_DURATION_39 ( 39) -#define DIB3000MB_REG_LOCK_DURATION_40 ( 40) - -static u16 dib3000mb_reg_lock_duration[] = { - DIB3000MB_REG_LOCK_DURATION_39, DIB3000MB_REG_LOCK_DURATION_40 -}; - +static u16 dib3000mb_reg_lock_duration[] = { 39,40 }; static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; /* AGC loop bandwidth */ - -#define DIB3000MB_REG_AGC_BW_43 ( 43) -#define DIB3000MB_REG_AGC_BW_44 ( 44) -#define DIB3000MB_REG_AGC_BW_45 ( 45) -#define DIB3000MB_REG_AGC_BW_46 ( 46) -#define DIB3000MB_REG_AGC_BW_47 ( 47) -#define DIB3000MB_REG_AGC_BW_48 ( 48) -#define DIB3000MB_REG_AGC_BW_49 ( 49) -#define DIB3000MB_REG_AGC_BW_50 ( 50) - -static u16 dib3000mb_reg_agc_bandwidth[] = { - DIB3000MB_REG_AGC_BW_43, DIB3000MB_REG_AGC_BW_44, DIB3000MB_REG_AGC_BW_45, - DIB3000MB_REG_AGC_BW_46, DIB3000MB_REG_AGC_BW_47, DIB3000MB_REG_AGC_BW_48, - DIB3000MB_REG_AGC_BW_49, DIB3000MB_REG_AGC_BW_50 -}; +static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 }; static u16 dib3000mb_agc_bandwidth_low[] = { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; @@ -224,42 +132,8 @@ static u16 dib3000mb_agc_bandwidth_high[] = */ #define DIB3000MB_REG_SEQ ( 54) -/* all values have been set manually */ -static u16 dib3000mb_seq[2][2][2] = /* fft,gua, inv */ - { /* fft */ - { /* gua */ - { 0, 1 }, /* 0 0 { 0,1 } */ - { 3, 9 }, /* 0 1 { 0,1 } */ - }, - { - { 2, 5 }, /* 1 0 { 0,1 } */ - { 6, 11 }, /* 1 1 { 0,1 } */ - } - }; - /* bandwidth */ -#define DIB3000MB_REG_BW_55 ( 55) -#define DIB3000MB_REG_BW_56 ( 56) -#define DIB3000MB_REG_BW_57 ( 57) -#define DIB3000MB_REG_BW_58 ( 58) -#define DIB3000MB_REG_BW_59 ( 59) -#define DIB3000MB_REG_BW_60 ( 60) -#define DIB3000MB_REG_BW_61 ( 61) -#define DIB3000MB_REG_BW_62 ( 62) -#define DIB3000MB_REG_BW_63 ( 63) -#define DIB3000MB_REG_BW_64 ( 64) -#define DIB3000MB_REG_BW_65 ( 65) -#define DIB3000MB_REG_BW_66 ( 66) -#define DIB3000MB_REG_BW_67 ( 67) - -static u16 dib3000mb_reg_bandwidth[] = { - DIB3000MB_REG_BW_55, DIB3000MB_REG_BW_56, DIB3000MB_REG_BW_57, - DIB3000MB_REG_BW_58, DIB3000MB_REG_BW_59, DIB3000MB_REG_BW_60, - DIB3000MB_REG_BW_61, DIB3000MB_REG_BW_62, DIB3000MB_REG_BW_63, - DIB3000MB_REG_BW_64, DIB3000MB_REG_BW_65, DIB3000MB_REG_BW_66, - DIB3000MB_REG_BW_67 -}; - +static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; static u16 dib3000mb_bandwidth_6mhz[] = { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; @@ -309,7 +183,7 @@ static u16 dib3000mb_bandwidth_8mhz[] = /* mobile mode ??? */ #define DIB3000MB_REG_MOBILE_MODE ( 101) -#define DIB3000MB_MOBILE_MODE_ON ( 1) +#define DIB3000MB_MOBILE_MODE_ON ( 1) #define DIB3000MB_MOBILE_MODE_OFF ( 0) #define DIB3000MB_REG_UNK_106 ( 106) @@ -331,7 +205,7 @@ static u16 dib3000mb_bandwidth_8mhz[] = /* QAM for mobile mode */ #define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) -#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) +#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) #define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) #define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) @@ -345,23 +219,12 @@ static u16 dib3000mb_bandwidth_8mhz[] = /* vit hrch */ #define DIB3000MB_REG_VIT_HRCH ( 128) -#define DIB3000MB_VIT_HRCH_ON ( 1) -#define DIB3000MB_VIT_HRCH_OFF ( 0) /* vit code rate */ #define DIB3000MB_REG_VIT_CODE_RATE ( 129) -/* forward error correction code rates */ -#define DIB3000MB_FEC_1_2 ( 1) -#define DIB3000MB_FEC_2_3 ( 2) -#define DIB3000MB_FEC_3_4 ( 3) -#define DIB3000MB_FEC_5_6 ( 5) -#define DIB3000MB_FEC_7_8 ( 7) - /* vit select hp */ #define DIB3000MB_REG_VIT_HP ( 130) -#define DIB3000MB_VIT_LP ( 0) -#define DIB3000MB_VIT_HP ( 1) /* time frame for Bit-Error-Rate calculation */ #define DIB3000MB_REG_BERLEN ( 135) @@ -382,8 +245,9 @@ static u16 dib3000mb_bandwidth_8mhz[] = #define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) #define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) -#define DIB3000MB_REG_FIFO_144 ( 144) -#define DIB3000MB_FIFO_144 ( 1) +#define DIB3000MB_REG_PID_PARSE ( 144) +#define DIB3000MB_PID_PARSE_INHIBIT ( 0) +#define DIB3000MB_PID_PARSE_ACTIVATE ( 1) #define DIB3000MB_REG_FIFO ( 145) #define DIB3000MB_FIFO_INHIBIT ( 1) @@ -399,25 +263,11 @@ static u16 dib3000mb_bandwidth_8mhz[] = * pidfilter * it is not a hardware pidfilter but a filter which drops all pids * except the ones set. Necessary because of the limited USB1.1 bandwidth. + * regs 153-168 */ -#define DIB3000MB_REG_FILTER_PID_0 ( 153) -#define DIB3000MB_REG_FILTER_PID_1 ( 154) -#define DIB3000MB_REG_FILTER_PID_2 ( 155) -#define DIB3000MB_REG_FILTER_PID_3 ( 156) -#define DIB3000MB_REG_FILTER_PID_4 ( 157) -#define DIB3000MB_REG_FILTER_PID_5 ( 158) -#define DIB3000MB_REG_FILTER_PID_6 ( 159) -#define DIB3000MB_REG_FILTER_PID_7 ( 160) -#define DIB3000MB_REG_FILTER_PID_8 ( 161) -#define DIB3000MB_REG_FILTER_PID_9 ( 162) -#define DIB3000MB_REG_FILTER_PID_10 ( 163) -#define DIB3000MB_REG_FILTER_PID_11 ( 164) -#define DIB3000MB_REG_FILTER_PID_12 ( 165) -#define DIB3000MB_REG_FILTER_PID_13 ( 166) -#define DIB3000MB_REG_FILTER_PID_14 ( 167) -#define DIB3000MB_REG_FILTER_PID_15 ( 168) - -#define DIB3000MB_ACTIVATE_FILTERING (0x2000) + +#define DIB3000MB_REG_FIRST_PID ( 153) +#define DIB3000MB_NUM_PIDS ( 16) /* * output mode @@ -436,40 +286,10 @@ static u16 dib3000mb_bandwidth_8mhz[] = #define DIB3000MB_IRQ_EVENT_MASK ( 0) /* filter coefficients */ -#define DIB3000MB_REG_FILTER_COEF_171 ( 171) -#define DIB3000MB_REG_FILTER_COEF_172 ( 172) -#define DIB3000MB_REG_FILTER_COEF_173 ( 173) -#define DIB3000MB_REG_FILTER_COEF_174 ( 174) -#define DIB3000MB_REG_FILTER_COEF_175 ( 175) -#define DIB3000MB_REG_FILTER_COEF_176 ( 176) -#define DIB3000MB_REG_FILTER_COEF_177 ( 177) -#define DIB3000MB_REG_FILTER_COEF_178 ( 178) -#define DIB3000MB_REG_FILTER_COEF_179 ( 179) -#define DIB3000MB_REG_FILTER_COEF_180 ( 180) -#define DIB3000MB_REG_FILTER_COEF_181 ( 181) -#define DIB3000MB_REG_FILTER_COEF_182 ( 182) -#define DIB3000MB_REG_FILTER_COEF_183 ( 183) -#define DIB3000MB_REG_FILTER_COEF_184 ( 184) -#define DIB3000MB_REG_FILTER_COEF_185 ( 185) -#define DIB3000MB_REG_FILTER_COEF_186 ( 186) -#define DIB3000MB_REG_FILTER_COEF_187 ( 187) -#define DIB3000MB_REG_FILTER_COEF_188 ( 188) -#define DIB3000MB_REG_FILTER_COEF_189 ( 189) -#define DIB3000MB_REG_FILTER_COEF_190 ( 190) -#define DIB3000MB_REG_FILTER_COEF_191 ( 191) -#define DIB3000MB_REG_FILTER_COEF_192 ( 192) -#define DIB3000MB_REG_FILTER_COEF_193 ( 193) -#define DIB3000MB_REG_FILTER_COEF_194 ( 194) - static u16 dib3000mb_reg_filter_coeffs[] = { - DIB3000MB_REG_FILTER_COEF_171, DIB3000MB_REG_FILTER_COEF_172, DIB3000MB_REG_FILTER_COEF_173, - DIB3000MB_REG_FILTER_COEF_174, DIB3000MB_REG_FILTER_COEF_175, DIB3000MB_REG_FILTER_COEF_176, - DIB3000MB_REG_FILTER_COEF_177, DIB3000MB_REG_FILTER_COEF_178, DIB3000MB_REG_FILTER_COEF_179, - DIB3000MB_REG_FILTER_COEF_180, DIB3000MB_REG_FILTER_COEF_181, DIB3000MB_REG_FILTER_COEF_182, - DIB3000MB_REG_FILTER_COEF_183, DIB3000MB_REG_FILTER_COEF_184, DIB3000MB_REG_FILTER_COEF_185, - DIB3000MB_REG_FILTER_COEF_186, DIB3000MB_REG_FILTER_COEF_188, - DIB3000MB_REG_FILTER_COEF_189, DIB3000MB_REG_FILTER_COEF_190, DIB3000MB_REG_FILTER_COEF_191, - DIB3000MB_REG_FILTER_COEF_192, DIB3000MB_REG_FILTER_COEF_194 + 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, + 188, 189, 190, 191, 192, 194 }; static u16 dib3000mb_filter_coeffs[] = { @@ -504,13 +324,6 @@ static u16 dib3000mb_filter_coeffs[] = { #define DIB3000MB_RESET_DEVICE (0x812c) #define DIB3000MB_RESET_DEVICE_RST ( 0) -/* identification registers, manufactor an the device */ -#define DIB3000MB_REG_MANUFACTOR_ID ( 1025) -#define DIB3000MB_MANUFACTOR_ID_DIBCOM (0x01B3) - -#define DIB3000MB_REG_DEVICE_ID ( 1026) -#define DIB3000MB_DEVICE_ID (0x3000) - /* hardware clock configuration */ #define DIB3000MB_REG_CLOCK ( 1027) #define DIB3000MB_CLOCK_DEFAULT (0x9000) @@ -528,9 +341,6 @@ static u16 dib3000mb_filter_coeffs[] = { /* set the tuner i2c address */ #define DIB3000MB_REG_TUNER ( 1089) -#define DIB3000MB_TUNER_ADDR_DEFAULT ( 194) -#define DIB3000MB_ACTIVATE_TUNER_XFER(a) (0xffff & (a << 7)) -#define DIB3000MB_DEACTIVATE_TUNER_XFER(a) (0xffff & ((a << 7) + 0x80)) /* monitoring registers (read only) */ diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c new file mode 100644 index 000000000000..8015c214072c --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -0,0 +1,860 @@ +/* + * Frontend driver for mobile DVB-T demodulator DiBcom 3000-MC/P + * DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/dvb/README.dibusb for more information + * + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include "dvb_frontend.h" +#include "dib3000-common.h" +#include "dib3000mc_priv.h" +#include "dib3000.h" + +/* Version information */ +#define DRIVER_VERSION "0.1" +#define DRIVER_DESC "DiBcom 3000-MC DVB-T demodulator driver" +#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +static int debug; +module_param(debug, int, 0x644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); +#endif +#define deb_info(args...) dprintk(0x01,args) +#define deb_xfer(args...) dprintk(0x02,args) +#define deb_setf(args...) dprintk(0x04,args) +#define deb_getf(args...) dprintk(0x08,args) + + +static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode, + fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth) +{ + switch (transmission_mode) { + case TRANSMISSION_MODE_2K: + wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]); + break; + case TRANSMISSION_MODE_8K: + wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]); + break; + default: + break; + } + + switch (bandwidth) { +/* case BANDWIDTH_5_MHZ: + wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]); + break; */ + case BANDWIDTH_6_MHZ: + wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]); + break; + case BANDWIDTH_7_MHZ: + wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]); + break; + case BANDWIDTH_8_MHZ: + wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]); + break; + default: + break; + } + + switch (mode) { + case 0: /* no impulse */ /* fall through */ + wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]); + break; + case 1: /* new algo */ + wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]); + set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */ + break; + default: /* old algo */ + wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]); + break; + } + return 0; +} + +static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset, + fe_transmit_mode_t fft, fe_bandwidth_t bw) +{ + u16 timf_msb,timf_lsb; + s32 tim_offset,tim_sgn; + u64 comp1,comp2,comp=0; + + switch (bw) { + case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break; + case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break; + case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break; + default: err("unknown bandwidth (%d)",bw); break; + } + timf_msb = (comp >> 16) & 0xff; + timf_lsb = (comp & 0xffff); + + // Update the timing offset ; + if (upd_offset > 0) { + if (!state->timing_offset_comp_done) { + msleep(200); + state->timing_offset_comp_done = 1; + } + tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB); + if ((tim_offset & 0x2000) == 0x2000) + tim_offset |= 0xC000; + if (fft == TRANSMISSION_MODE_2K) + tim_offset <<= 2; + state->timing_offset += tim_offset; + } + + tim_offset = state->timing_offset; + if (tim_offset < 0) { + tim_sgn = 1; + tim_offset = -tim_offset; + } else + tim_sgn = 0; + + comp1 = (u32)tim_offset * (u32)timf_lsb ; + comp2 = (u32)tim_offset * (u32)timf_msb ; + comp = ((comp1 >> 16) + comp2) >> 7; + + if (tim_sgn == 0) + comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp; + else + comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ; + + timf_msb = (comp >> 16) & 0xff; + timf_lsb = comp & 0xffff; + + wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb); + wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb); + return 0; +} + +static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost) +{ + if (boost) { + wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON); + } else { + wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF); + } + switch (bw) { + case BANDWIDTH_8_MHZ: + wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz); + break; + case BANDWIDTH_7_MHZ: + wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz); + break; + case BANDWIDTH_6_MHZ: + wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz); + break; +/* case BANDWIDTH_5_MHZ: + wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz); + break;*/ + case BANDWIDTH_AUTO: + return -EOPNOTSUPP; + default: + err("unknown bandwidth value (%d).",bw); + return -EINVAL; + } + if (boost) { + u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) + + rd(DIB3000MC_REG_BW_TIMOUT_LSB); + timeout *= 85; timeout >>= 7; + wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff); + wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff); + } + return 0; +} + +static int dib3000mc_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep); + +static int dib3000mc_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep, int tuner) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; + fe_code_rate_t fe_cr = FEC_NONE; + int search_state, seq; + u16 val; + u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0; + + if (tuner) { + wr(DIB3000MC_REG_TUNER, + DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr)); + state->config.pll_set(fe, fep); + wr(DIB3000MC_REG_TUNER, + DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr)); + } + + dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth); + dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0); + + wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC); + wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF); + +/* Default cfg isi offset adp */ + wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]); + + wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT); + wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]); + wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133); + + wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general); + if (ofdm->bandwidth == BANDWIDTH_8_MHZ) { + wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]); + } else { + wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]); + } + + switch (ofdm->transmission_mode) { + case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break; + case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break; + case TRANSMISSION_MODE_AUTO: break; + default: return -EINVAL; + } + switch (ofdm->guard_interval) { + case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break; + case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break; + case GUARD_INTERVAL_1_8: guard = DIB3000_GUARD_TIME_1_8; break; + case GUARD_INTERVAL_1_4: guard = DIB3000_GUARD_TIME_1_4; break; + case GUARD_INTERVAL_AUTO: break; + default: return -EINVAL; + } + switch (ofdm->constellation) { + case QPSK: qam = DIB3000_CONSTELLATION_QPSK; break; + case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break; + case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break; + case QAM_AUTO: break; + default: return -EINVAL; + } + switch (ofdm->hierarchy_information) { + case HIERARCHY_NONE: /* fall through */ + case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break; + case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break; + case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break; + case HIERARCHY_AUTO: break; + default: return -EINVAL; + } + if (ofdm->hierarchy_information == HIERARCHY_NONE) { + hrch = DIB3000_HRCH_OFF; + sel_hp = DIB3000_SELECT_HP; + fe_cr = ofdm->code_rate_HP; + } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) { + hrch = DIB3000_HRCH_ON; + sel_hp = DIB3000_SELECT_LP; + fe_cr = ofdm->code_rate_LP; + } + switch (fe_cr) { + case FEC_1_2: cr = DIB3000_FEC_1_2; break; + case FEC_2_3: cr = DIB3000_FEC_2_3; break; + case FEC_3_4: cr = DIB3000_FEC_3_4; break; + case FEC_5_6: cr = DIB3000_FEC_5_6; break; + case FEC_7_8: cr = DIB3000_FEC_7_8; break; + case FEC_NONE: break; + case FEC_AUTO: break; + default: return -EINVAL; + } + + wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft)); + wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch)); + + switch (fep->inversion) { + case INVERSION_OFF: + wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF); + break; + case INVERSION_AUTO: + break; + case INVERSION_ON: + wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON); + break; + default: + return -EINVAL; + } + + seq = dib3000_seq + [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO] + [ofdm->guard_interval == GUARD_INTERVAL_AUTO] + [fep->inversion == INVERSION_AUTO]; + + deb_setf("seq? %d\n", seq); + wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1)); + + dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth); + + val = rd(DIB3000MC_REG_DEMOD_PARM); + wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON); + wr(DIB3000MC_REG_DEMOD_PARM,val); + + msleep(70); + + wr_foreach(dib3000mc_reg_agc_bandwidth, dib3000mc_agc_bandwidth); + + /* something has to be auto searched */ + if (ofdm->constellation == QAM_AUTO || + ofdm->hierarchy_information == HIERARCHY_AUTO || + ofdm->guard_interval == GUARD_INTERVAL_AUTO || + ofdm->transmission_mode == TRANSMISSION_MODE_AUTO || + fe_cr == FEC_AUTO || + fep->inversion == INVERSION_AUTO + ) { + int as_count=0; + + deb_setf("autosearch enabled.\n"); + + val = rd(DIB3000MC_REG_DEMOD_PARM); + wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON); + wr(DIB3000MC_REG_DEMOD_PARM,val); + + while ((search_state = dib3000_search_status( + rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100) + msleep(10); + + deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count); + + if (search_state == 1) { + struct dvb_frontend_parameters feps; + feps.u.ofdm.bandwidth = ofdm->bandwidth; /* bw is not auto searched */; + if (dib3000mc_get_frontend(fe, &feps) == 0) { + deb_setf("reading tuning data from frontend succeeded.\n"); + return dib3000mc_set_frontend(fe, &feps, 0); + } + } + } else { + wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE); + wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[qam]); + /* set_offset_cfg */ + wr_foreach(dib3000mc_reg_offset, + dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]); + +// dib3000mc_set_timing(1,ofdm->transmission_mode,ofdm->bandwidth); + +// wr(DIB3000MC_REG_LOCK_MASK,DIB3000MC_ACTIVATE_LOCK_MASK); /* activates some locks if needed */ + +/* set_or(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_RST_AUTO_SRCH_ON); + set_or(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF); + wr(DIB3000MC_REG_RESTART_VIT,DIB3000MC_RESTART_VIT_ON); + wr(DIB3000MC_REG_RESTART_VIT,DIB3000MC_RESTART_VIT_OFF);*/ + } + + return 0; +} + + +static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + + state->timing_offset = 0; + state->timing_offset_comp_done = 0; + + wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON); + wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK); + wr(DIB3000MC_REG_RST_I2C_ADDR, + DIB3000MC_DEMOD_ADDR(state->config.demod_address) | + DIB3000MC_DEMOD_ADDR_ON); + + wr(DIB3000MC_REG_RST_I2C_ADDR, + DIB3000MC_DEMOD_ADDR(state->config.demod_address)); + + wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG); + wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF); + + wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP); + wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE); + wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP); + wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT); + + wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF); + wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19); + + wr(33,5); + wr(36,81); + wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88); + + wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99); + wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */ + + /* mobile mode - portable reception */ + wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]); + +/* TUNER_PANASONIC_ENV57H12D5: */ + wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth); + wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general); + wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]); + + wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110); + wr(26,0x6680); + wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1); + wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2); + wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3); + wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT); + + wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general); + wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz); + + wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4); + + wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF); + wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB); + + dib3000mc_set_timing(state,0,TRANSMISSION_MODE_2K,BANDWIDTH_8_MHZ); +// wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]); + + wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120); + wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134); + wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG); + + dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ); + +/* output mode control, just the MPEG2_SLAVE */ + set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE); + wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE); + wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE); + wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE); + +/* MPEG2_PARALLEL_CONTINUOUS_CLOCK + wr(DIB3000MC_REG_OUTMODE, + DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK, + rd(DIB3000MC_REG_OUTMODE))); + + wr(DIB3000MC_REG_SMO_MODE, + DIB3000MC_SMO_MODE_DEFAULT | + DIB3000MC_SMO_MODE_188); + + wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT); + wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON); +*/ +/* diversity */ + wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT); + wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT); + + wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF); + + set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF); + + +/* if (state->config->pll_init) { + wr(DIB3000MC_REG_TUNER, + DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr)); + state->config->pll_init(fe); + wr(DIB3000MC_REG_TUNER, + DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr)); + }*/ + return 0; +} + +static int dib3000mc_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; + fe_code_rate_t *cr; + u16 tps_val,cr_val; + int inv_test1,inv_test2; + u32 dds_val, threshold = 0x1000000; + + if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507)) + return 0; + + dds_val = ((rd(DIB3000MC_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB); + if (dds_val < threshold) + inv_test1 = 0; + else if (dds_val == threshold) + inv_test1 = 1; + else + inv_test1 = 2; + + dds_val = ((rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB); + if (dds_val < threshold) + inv_test2 = 0; + else if (dds_val == threshold) + inv_test2 = 1; + else + inv_test2 = 2; + + fep->inversion = + ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || + ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? + INVERSION_ON : INVERSION_OFF; + + deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion); + + tps_val = rd(DIB3000MC_REG_TUNING_PARM); + + switch (DIB3000MC_TP_QAM(tps_val)) { + case DIB3000_CONSTELLATION_QPSK: + deb_getf("QPSK "); + ofdm->constellation = QPSK; + break; + case DIB3000_CONSTELLATION_16QAM: + deb_getf("QAM16 "); + ofdm->constellation = QAM_16; + break; + case DIB3000_CONSTELLATION_64QAM: + deb_getf("QAM64 "); + ofdm->constellation = QAM_64; + break; + default: + err("Unexpected constellation returned by TPS (%d)", tps_val); + break; + } + + if (DIB3000MC_TP_HRCH(tps_val)) { + deb_getf("HRCH ON "); + cr = &ofdm->code_rate_LP; + ofdm->code_rate_HP = FEC_NONE; + switch (DIB3000MC_TP_ALPHA(tps_val)) { + case DIB3000_ALPHA_0: + deb_getf("HIERARCHY_NONE "); + ofdm->hierarchy_information = HIERARCHY_NONE; + break; + case DIB3000_ALPHA_1: + deb_getf("HIERARCHY_1 "); + ofdm->hierarchy_information = HIERARCHY_1; + break; + case DIB3000_ALPHA_2: + deb_getf("HIERARCHY_2 "); + ofdm->hierarchy_information = HIERARCHY_2; + break; + case DIB3000_ALPHA_4: + deb_getf("HIERARCHY_4 "); + ofdm->hierarchy_information = HIERARCHY_4; + break; + default: + err("Unexpected ALPHA value returned by TPS (%d)", tps_val); + break; + } + cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val); + } else { + deb_getf("HRCH OFF "); + cr = &ofdm->code_rate_HP; + ofdm->code_rate_LP = FEC_NONE; + ofdm->hierarchy_information = HIERARCHY_NONE; + cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val); + } + + switch (cr_val) { + case DIB3000_FEC_1_2: + deb_getf("FEC_1_2 "); + *cr = FEC_1_2; + break; + case DIB3000_FEC_2_3: + deb_getf("FEC_2_3 "); + *cr = FEC_2_3; + break; + case DIB3000_FEC_3_4: + deb_getf("FEC_3_4 "); + *cr = FEC_3_4; + break; + case DIB3000_FEC_5_6: + deb_getf("FEC_5_6 "); + *cr = FEC_4_5; + break; + case DIB3000_FEC_7_8: + deb_getf("FEC_7_8 "); + *cr = FEC_7_8; + break; + default: + err("Unexpected FEC returned by TPS (%d)", tps_val); + break; + } + + switch (DIB3000MC_TP_GUARD(tps_val)) { + case DIB3000_GUARD_TIME_1_32: + deb_getf("GUARD_INTERVAL_1_32 "); + ofdm->guard_interval = GUARD_INTERVAL_1_32; + break; + case DIB3000_GUARD_TIME_1_16: + deb_getf("GUARD_INTERVAL_1_16 "); + ofdm->guard_interval = GUARD_INTERVAL_1_16; + break; + case DIB3000_GUARD_TIME_1_8: + deb_getf("GUARD_INTERVAL_1_8 "); + ofdm->guard_interval = GUARD_INTERVAL_1_8; + break; + case DIB3000_GUARD_TIME_1_4: + deb_getf("GUARD_INTERVAL_1_4 "); + ofdm->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + err("Unexpected Guard Time returned by TPS (%d)", tps_val); + break; + } + + switch (DIB3000MC_TP_FFT(tps_val)) { + case DIB3000_TRANSMISSION_MODE_2K: + deb_getf("TRANSMISSION_MODE_2K "); + ofdm->transmission_mode = TRANSMISSION_MODE_2K; + break; + case DIB3000_TRANSMISSION_MODE_8K: + deb_getf("TRANSMISSION_MODE_8K "); + ofdm->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + err("unexpected transmission mode return by TPS (%d)", tps_val); + break; + } + return 0; +} + +static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + u16 lock = rd(DIB3000MC_REG_LOCKING); + + *stat = 0; + if (DIB3000MC_AGC_LOCK(lock)) + *stat |= FE_HAS_SIGNAL; + if (DIB3000MC_CARRIER_LOCK(lock)) + *stat |= FE_HAS_CARRIER; + if (DIB3000MC_TPS_LOCK(lock)) /* VIT_LOCK ? */ + *stat |= FE_HAS_VITERBI; + if (DIB3000MC_MPEG_SYNC_LOCK(lock)) + *stat |= (FE_HAS_SYNC | FE_HAS_LOCK); + + deb_info("actual status is %2x\n",*stat); + + return 0; +} + +static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB)); + return 0; +} + +static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + + *unc = rd(DIB3000MC_REG_PACKET_ERROR_COUNT); + return 0; +} + +/* see dib3000mb.c for calculation comments */ +static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB); + *strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f); + + deb_info("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff); + return 0; +} + +/* see dib3000mb.c for calculation comments */ +static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + + u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB), + val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB); + u16 sig,noise; + + sig = (((val >> 6) & 0xff) << 8) + (val & 0x3f); + noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3); + if (noise == 0) + *snr = 0xffff; + else + *snr = (u16) sig/noise; + + deb_info("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff); + deb_info("noise: mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff); + deb_info("snr: %d\n",*snr); + return 0; +} + +static int dib3000mc_sleep(struct dvb_frontend* fe) +{ + struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv; + + set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN); + wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN); + wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN); + wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN); + return 0; +} + +static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + tune->step_size = 166667; + tune->max_drift = 166667 * 2; + + return 0; +} + +static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe) +{ + return dib3000mc_fe_init(fe, 0); +} + +static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +{ + return dib3000mc_set_frontend(fe, fep, 1); +} + +static void dib3000mc_release(struct dvb_frontend* fe) +{ + struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv; + dib3000_dealloc_pid_list(state); + kfree(state); +} + +/* pid filter and transfer stuff */ +static int dib3000mc_pid_control(struct dvb_frontend *fe,int pid,int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + int index = dib3000_get_pid_index(state->pid_list, DIB3000MC_NUM_PIDS, pid, &state->pid_list_lock,onoff); + pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); + + if (index >= 0) { + wr(index+DIB3000MC_REG_FIRST_PID,pid); + } else { + err("no more pids for filtering."); + return -ENOMEM; + } + return 0; +} + +static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff) +{ + struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv; + u16 tmp = rd(DIB3000MC_REG_SMO_MODE); + deb_xfer("%s fifo",onoff ? "enabling" : "disabling"); + if (onoff) { + wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH); + } else { + wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH); + } + return 0; +} + +static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + u16 tmp = rd(DIB3000MC_REG_SMO_MODE); + deb_xfer("%s pid parsing",onoff ? "enabling" : "disabling"); + if (onoff) { + wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE); + } else { + wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE); + } + return 0; +} + +static struct dvb_frontend_ops dib3000mc_ops; + +struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops) +{ + struct dib3000_state* state = NULL; + u16 devid; + + /* allocate memory for the internal state */ + state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config,config,sizeof(struct dib3000_config)); + memcpy(&state->ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); + + /* check for the correct demod */ + if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) + goto error; + + devid = rd(DIB3000_REG_DEVICE_ID); + if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID) + goto error; + + + switch (devid) { + case DIB3000MC_DEVICE_ID: + info("Found a DiBcom 3000-MC."); + break; + case DIB3000P_DEVICE_ID: + info("Found a DiBcom 3000-P."); + break; + } + + if (dib3000_init_pid_list(state,DIB3000MC_NUM_PIDS)) + goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + + /* set the xfer operations */ + xfer_ops->pid_parse = dib3000mc_pid_parse; + xfer_ops->fifo_ctrl = dib3000mc_fifo_control; + xfer_ops->pid_ctrl = dib3000mc_pid_control; + + return &state->frontend; + +error: + if (state) + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops dib3000mc_ops = { + + .info = { + .name = "DiBcom 3000-MC/P DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib3000mc_release, + + .init = dib3000mc_fe_init_nonmobile, + .sleep = dib3000mc_sleep, + + .set_frontend = dib3000mc_set_frontend_and_tuner, + .get_frontend = dib3000mc_get_frontend, + .get_tune_settings = dib3000mc_fe_get_tune_settings, + + .read_status = dib3000mc_read_status, + .read_ber = dib3000mc_read_ber, + .read_signal_strength = dib3000mc_read_signal_strength, + .read_snr = dib3000mc_read_snr, + .read_ucblocks = dib3000mc_read_unc_blocks, +}; + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(dib3000mc_attach); diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h new file mode 100644 index 000000000000..21ccc1399a72 --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000mc_priv.h @@ -0,0 +1,439 @@ +/* + * dib3000mc_priv.h + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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, version 2. + * + * for more information see dib3000mc.c . + */ + +#ifndef __DIB3000MC_PRIV_H__ +#define __DIB3000MC_PRIV_H__ + +/* info and err, taken from usb.h, if there is anything available like by default, + * please change ! + */ +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) + +// defines the phase noise algorithm to be used (O:Inhib, 1:CPE on) +#define DEF_PHASE_NOISE_MODE 0 + +// define Mobille algorithms +#define DEF_MOBILE_MODE Auto_Reception + +// defines the tuner type +#define DEF_TUNER_TYPE TUNER_PANASONIC_ENV57H13D5 + +// defines the impule noise algorithm to be used +#define DEF_IMPULSE_NOISE_MODE 0 + +// defines the MPEG2 data output format +#define DEF_MPEG2_OUTPUT_188 0 + +// defines the MPEG2 data output format +#define DEF_OUTPUT_MODE MPEG2_PARALLEL_CONTINUOUS_CLOCK + +/* + * Demodulator parameters + * reg: 0 1 1 1 11 11 111 + * | | | | | | + * | | | | | +-- alpha (000=0, 001=1, 010=2, 100=4) + * | | | | +----- constellation (00=QPSK, 01=16QAM, 10=64QAM) + * | | | +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4) + * | | +----------- transmission mode (0=2k, 1=8k) + * | | + * | +-------------- restart autosearch for parameters + * +---------------- restart the demodulator + * reg: 181 1 111 1 + * | | | + * | | +- FEC applies for HP or LP (0=LP, 1=HP) + * | +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8) + * +------- hierarchy on (0=no, 1=yes) + */ + +/* demodulator tuning parameter and restart options */ +#define DIB3000MC_REG_DEMOD_PARM ( 0) +#define DIB3000MC_DEMOD_PARM(a,c,g,t) ( \ + (0x7 & a) | \ + ((0x3 & c) << 3) | \ + ((0x3 & g) << 5) | \ + ((0x1 & t) << 7) ) +#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON (1 << 8) +#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF (0 << 8) +#define DIB3000MC_DEMOD_RST_DEMOD_ON (1 << 9) +#define DIB3000MC_DEMOD_RST_DEMOD_OFF (0 << 9) + +/* register for hierarchy parameters */ +#define DIB3000MC_REG_HRCH_PARM ( 181) +#define DIB3000MC_HRCH_PARM(s,f,h) ( \ + (0x1 & s) | \ + ((0x7 & f) << 1) | \ + ((0x1 & h) << 4) ) + +/* timeout ??? */ +#define DIB3000MC_REG_UNK_1 ( 1) +#define DIB3000MC_UNK_1 ( 0x04) + +/* timeout ??? */ +#define DIB3000MC_REG_UNK_2 ( 2) +#define DIB3000MC_UNK_2 ( 0x04) + +/* timeout ??? */ +#define DIB3000MC_REG_UNK_3 ( 3) +#define DIB3000MC_UNK_3 (0x1000) + +#define DIB3000MC_REG_UNK_4 ( 4) +#define DIB3000MC_UNK_4 (0x0814) + +/* timeout ??? */ +#define DIB3000MC_REG_SEQ_TPS ( 5) +#define DIB3000MC_SEQ_TPS_DEFAULT ( 1) +#define DIB3000MC_SEQ_TPS(s,t) ( \ + ((s & 0x0f) << 4) | \ + ((t & 0x01) << 8) ) +#define DIB3000MC_IS_TPS(v) ((v << 8) & 0x1) +#define DIB3000MC_IS_AS(v) ((v >> 4) & 0xf) + +/* parameters for the bandwidth */ +#define DIB3000MC_REG_BW_TIMOUT_MSB ( 6) +#define DIB3000MC_REG_BW_TIMOUT_LSB ( 7) + +static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 }; + +/*static u16 dib3000mc_bandwidth_5mhz[] = + { 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/ + +static u16 dib3000mc_bandwidth_6mhz[] = + { 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 }; + +static u16 dib3000mc_bandwidth_7mhz[] = + { 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 }; + +static u16 dib3000mc_bandwidth_8mhz[] = + { 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0b0 }; + +static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 }; +static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 }; + +/* lock mask */ +#define DIB3000MC_REG_LOCK_MASK ( 15) +#define DIB3000MC_ACTIVATE_LOCK_MASK (0x0800) + +/* reset the uncorrected packet count (??? do it 5 times) */ +#define DIB3000MC_REG_RST_UNC ( 18) +#define DIB3000MC_RST_UNC_ON ( 1) +#define DIB3000MC_RST_UNC_OFF ( 0) + +#define DIB3000MC_REG_UNK_19 ( 19) +#define DIB3000MC_UNK_19 ( 0) + +/* DDS frequency value (IF position) and inversion bit */ +#define DIB3000MC_REG_INVERSION ( 21) +#define DIB3000MC_REG_SET_DDS_FREQ_MSB ( 21) +#define DIB3000MC_DDS_FREQ_MSB_INV_OFF (0x0164) +#define DIB3000MC_DDS_FREQ_MSB_INV_ON (0x0364) + +#define DIB3000MC_REG_SET_DDS_FREQ_LSB ( 22) +#define DIB3000MC_DDS_FREQ_LSB (0x463d) + +/* timing frequencies setting */ +#define DIB3000MC_REG_TIMING_FREQ_MSB ( 23) +#define DIB3000MC_REG_TIMING_FREQ_LSB ( 24) +#define DIB3000MC_CLOCK_REF (0x151fd1) + +//static u16 dib3000mc_reg_timing_freq[] = { 23,24 }; + +//static u16 dib3000mc_timing_freq[][2] = { +// { 0x69, 0x9f18 }, /* 5 MHz */ +// { 0x7e ,0xbee9 }, /* 6 MHz */ +// { 0x93 ,0xdebb }, /* 7 MHz */ +// { 0xa8 ,0xfe8c }, /* 8 MHz */ +//}; + +/* timeout ??? */ +static u16 dib3000mc_reg_offset[] = { 26,33 }; + +static u16 dib3000mc_offset[][2] = { + { 26240, 5 }, /* default */ + { 30336, 6 }, /* 8K */ + { 38528, 8 }, /* 2K */ +}; + +#define DIB3000MC_REG_ISI ( 29) +#define DIB3000MC_ISI_DEFAULT (0x1073) +#define DIB3000MC_ISI_ACTIVATE (0x0000) +#define DIB3000MC_ISI_INHIBIT (0x0200) + +/* impulse noise control */ +static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 }; + +static u16 dib3000mc_imp_noise_ctl[][2] = { + { 0x1294, 0xfff8 }, /* mode 0 */ + { 0x1294, 0xfff8 }, /* mode 1 */ + { 0x1294, 0xfff8 }, /* mode 2 */ + { 0x1294, 0xfff8 }, /* mode 3 */ + { 0x1294, 0xfff8 }, /* mode 4 */ +}; + +/* AGC registers */ +static u16 dib3000mc_reg_agc[] = { + 36,37,38,39,42,43,44,45,46,47,48,49 +}; + +static u16 dib3000mc_agc_tuner[][12] = { + { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666, + 0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019 + }, /* TUNER_PANASONIC_ENV77H04D5, */ + + { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a, + 0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e + }, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */ + + { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff, + 0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040 + }, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */ + + { 0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29, + 0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537 + }, /* TUNER_PROVIDER_X */ + /* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */ +}; + +/* AGC loop bandwidth */ +static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 }; +static u16 dib3000mc_agc_bandwidth[] = { 0x119,0x330 }; + +static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 }; +static u16 dib3000mc_agc_bandwidth_general[] = + { 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 }; + +#define DIB3000MC_REG_IMP_NOISE_55 ( 55) +#define DIB3000MC_IMP_NEW_ALGO(w) (w | (1<<10)) + +/* Impulse noise params */ +static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 }; +static u16 dib3000mc_impluse_noise[][3] = { + { 0x489, 0x89, 0x72 }, /* 5 MHz */ + { 0x4a5, 0xa5, 0x89 }, /* 6 MHz */ + { 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */ + { 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */ +}; + +static u16 dib3000mc_reg_fft[] = { + 58,59,60,61,62,63,64,65,66,67,68,69, + 70,71,72,73,74,75,76,77,78,79,80,81, + 82,83,84,85,86 +}; + +static u16 dib3000mc_fft_modes[][29] = { + { 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, + 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d, + 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, + 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c, + 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0, 0xd + }, /* fft mode 0 */ + { 0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, + 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d, + 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, + 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c, + 0x3ffe, 0x5b3, 0x3feb, 0x0, 0x8200, 0xd + }, /* fft mode 1 */ +}; + +#define DIB3000MC_REG_UNK_88 ( 88) +#define DIB3000MC_UNK_88 (0x0410) + +static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 }; +static u16 dib3000mc_bw[][6] = { + { 0,0,0,0,0,0 }, /* 5 MHz */ + { 0,0,0,0,0,0 }, /* 6 MHz */ + { 0,0,0,0,0,0 }, /* 7 MHz */ + { 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */ +}; + + +/* phase noise control */ +#define DIB3000MC_REG_UNK_99 ( 99) +#define DIB3000MC_UNK_99 (0x0220) + +#define DIB3000MC_REG_SCAN_BOOST ( 100) +#define DIB3000MC_SCAN_BOOST_ON ((11 << 6) + 6) +#define DIB3000MC_SCAN_BOOST_OFF ((16 << 6) + 9) + +/* timeout ??? */ +#define DIB3000MC_REG_UNK_110 ( 110) +#define DIB3000MC_UNK_110 ( 3277) + +#define DIB3000MC_REG_UNK_111 ( 111) +#define DIB3000MC_UNK_111_PH_N_MODE_0 ( 0) +#define DIB3000MC_UNK_111_PH_N_MODE_1 (1 << 1) + +/* superious rm config */ +#define DIB3000MC_REG_UNK_120 ( 120) +#define DIB3000MC_UNK_120 ( 8207) + +#define DIB3000MC_REG_UNK_133 ( 133) +#define DIB3000MC_UNK_133 ( 15564) + +#define DIB3000MC_REG_UNK_134 ( 134) +#define DIB3000MC_UNK_134 ( 0) + +/* adapter config for constellation */ +static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 }; + +static u16 dib3000mc_adp_cfg[][4] = { + { 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK */ + { 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */ + { 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */ +}; + +static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 }; + +static u16 dib3000mc_mobile_mode[][5] = { + { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */ + { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */ + { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */ + { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */ +}; + +#define DIB3000MC_REG_DIVERSITY1 ( 177) +#define DIB3000MC_DIVERSITY1_DEFAULT ( 1) + +#define DIB3000MC_REG_DIVERSITY2 ( 178) +#define DIB3000MC_DIVERSITY2_DEFAULT ( 1) + +#define DIB3000MC_REG_DIVERSITY3 ( 180) +#define DIB3000MC_DIVERSITY3_IN_OFF (0xfff0) +#define DIB3000MC_DIVERSITY3_IN_ON (0xfff6) + +#define DIB3000MC_REG_FEC_CFG ( 195) +#define DIB3000MC_FEC_CFG ( 0x10) + +#define DIB3000MC_REG_SMO_MODE ( 206) +#define DIB3000MC_SMO_MODE_DEFAULT (1 << 2) +#define DIB3000MC_SMO_MODE_FIFO_FLUSH (1 << 3) +#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH ~DIB3000MC_SMO_MODE_FIFO_FLUSH +#define DIB3000MC_SMO_MODE_PID_PARSE (1 << 4) +#define DIB3000MC_SMO_MODE_NO_PID_PARSE ~DIB3000MC_SMO_MODE_PID_PARSE +#define DIB3000MC_SMO_MODE_188 (1 << 5) +#define DIB3000MC_SMO_MODE_SLAVE (DIB3000MC_SMO_MODE_DEFAULT | \ + DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1)) + +#define DIB3000MC_REG_FIFO_THRESHOLD ( 207) +#define DIB3000MC_FIFO_THRESHOLD_DEFAULT ( 1792) +#define DIB3000MC_FIFO_THRESHOLD_SLAVE ( 512) +/* + * pidfilter + * it is not a hardware pidfilter but a filter which drops all pids + * except the ones set. When connected to USB1.1 bandwidth this is important. + * DiB3000-MC/P can filter up to 32 PIDs + */ +#define DIB3000MC_REG_FIRST_PID ( 212) +#define DIB3000MC_NUM_PIDS ( 32) + +#define DIB3000MC_REG_OUTMODE ( 244) +#define DIB3000MC_OM_PARALLEL_GATED_CLK ( 0) +#define DIB3000MC_OM_PAR_CONT_CLK (1 << 11) +#define DIB3000MC_OM_SERIAL (2 << 11) +#define DIB3000MC_OM_DIVOUT_ON (4 << 11) +#define DIB3000MC_OM_SLAVE (DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK) + +#define DIB3000MC_REG_RF_POWER ( 392) + +#define DIB3000MC_REG_FFT_POSITION ( 407) + +#define DIB3000MC_REG_DDS_FREQ_MSB ( 414) +#define DIB3000MC_REG_DDS_FREQ_LSB ( 415) + +#define DIB3000MC_REG_TIMING_OFFS_MSB ( 416) +#define DIB3000MC_REG_TIMING_OFFS_LSB ( 417) + +#define DIB3000MC_REG_TUNING_PARM ( 458) +#define DIB3000MC_TP_QAM(v) ((v >> 13) & 0x03) +#define DIB3000MC_TP_HRCH(v) ((v >> 12) & 0x01) +#define DIB3000MC_TP_ALPHA(v) ((v >> 9) & 0x07) +#define DIB3000MC_TP_FFT(v) ((v >> 8) & 0x01) +#define DIB3000MC_TP_FEC_CR_HP(v) ((v >> 5) & 0x07) +#define DIB3000MC_TP_FEC_CR_LP(v) ((v >> 2) & 0x07) +#define DIB3000MC_TP_GUARD(v) (v & 0x03) + +#define DIB3000MC_REG_SIGNAL_NOISE_MSB ( 483) +#define DIB3000MC_REG_SIGNAL_NOISE_LSB ( 484) + +#define DIB3000MC_REG_MER ( 485) + +#define DIB3000MC_REG_BER_MSB ( 500) +#define DIB3000MC_REG_BER_LSB ( 501) + +#define DIB3000MC_REG_PACKET_ERRORS ( 503) + +#define DIB3000MC_REG_PACKET_ERROR_COUNT ( 506) + +#define DIB3000MC_REG_LOCK_507 ( 507) +#define DIB3000MC_LOCK_507 (0x0002) // ? name correct ? + +#define DIB3000MC_REG_LOCKING ( 509) +#define DIB3000MC_AGC_LOCK(v) (v & 0x8000) +#define DIB3000MC_CARRIER_LOCK(v) (v & 0x2000) +#define DIB3000MC_MPEG_SYNC_LOCK(v) (v & 0x0080) +#define DIB3000MC_MPEG_DATA_LOCK(v) (v & 0x0040) +#define DIB3000MC_TPS_LOCK(v) (v & 0x0004) + +#define DIB3000MC_REG_AS_IRQ ( 511) +#define DIB3000MC_AS_IRQ_SUCCESS (1 << 1) +#define DIB3000MC_AS_IRQ_FAIL ( 1) + +#define DIB3000MC_REG_TUNER ( 769) + +#define DIB3000MC_REG_RST_I2C_ADDR ( 1024) +#define DIB3000MC_DEMOD_ADDR_ON ( 1) +#define DIB3000MC_DEMOD_ADDR(a) ((a << 3) & 0x03F0) + +#define DIB3000MC_REG_RESTART ( 1027) +#define DIB3000MC_RESTART_OFF (0x0000) +#define DIB3000MC_RESTART_AGC (0x0800) +#define DIB3000MC_RESTART_CONFIG (0x8000) + +#define DIB3000MC_REG_RESTART_VIT ( 1028) +#define DIB3000MC_RESTART_VIT_OFF ( 0) +#define DIB3000MC_RESTART_VIT_ON ( 1) + +#define DIB3000MC_REG_CLK_CFG_1 ( 1031) +#define DIB3000MC_CLK_CFG_1_POWER_UP ( 0) +#define DIB3000MC_CLK_CFG_1_POWER_DOWN (0xffff) + +#define DIB3000MC_REG_CLK_CFG_2 ( 1032) +#define DIB3000MC_CLK_CFG_2_PUP_FIXED (0x012c) +#define DIB3000MC_CLK_CFG_2_PUP_PORT (0x0104) +#define DIB3000MC_CLK_CFG_2_PUP_MOBILE (0x0000) +#define DIB3000MC_CLK_CFG_2_POWER_DOWN (0xffff) + +#define DIB3000MC_REG_CLK_CFG_3 ( 1033) +#define DIB3000MC_CLK_CFG_3_POWER_UP ( 0) +#define DIB3000MC_CLK_CFG_3_POWER_DOWN (0xfff5) + +#define DIB3000MC_REG_CLK_CFG_7 ( 1037) +#define DIB3000MC_CLK_CFG_7_INIT ( 12592) +#define DIB3000MC_CLK_CFG_7_POWER_UP (~0x0003) +#define DIB3000MC_CLK_CFG_7_PWR_DOWN (0x0003) +#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF (1 << 8) + +/* was commented out ??? */ +#define DIB3000MC_REG_CLK_CFG_8 ( 1038) +#define DIB3000MC_CLK_CFG_8_POWER_UP (0x160c) + +#define DIB3000MC_REG_CLK_CFG_9 ( 1039) +#define DIB3000MC_CLK_CFG_9_POWER_UP ( 0) + +/* also clock ??? */ +#define DIB3000MC_REG_ELEC_OUT ( 1040) +#define DIB3000MC_ELEC_OUT_HIGH_Z ( 0) +#define DIB3000MC_ELEC_OUT_DIV_OUT_ON ( 1) +#define DIB3000MC_ELEC_OUT_SLAVE ( 3) + +#endif diff --git a/drivers/media/dvb/frontends/dst.c b/drivers/media/dvb/frontends/dst.c deleted file mode 100644 index 3370fcb43b53..000000000000 --- a/drivers/media/dvb/frontends/dst.c +++ /dev/null @@ -1,1256 +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. - -*/ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/delay.h> -#include <asm/div64.h> - -#include "dvb_frontend.h" -#include "dst-bt878.h" - -unsigned int dst_verbose = 0; -MODULE_PARM(dst_verbose, "i"); -MODULE_PARM_DESC(dst_verbose, "verbose startup messages, default is 1 (yes)"); -unsigned int dst_debug = 0; -MODULE_PARM(dst_debug, "i"); -MODULE_PARM_DESC(dst_debug, "debug messages, default is 0 (no)"); - -#define dprintk if (dst_debug) printk - -#define DST_I2C_ADDR 0x55 - -#define DST_TYPE_IS_SAT 0 -#define DST_TYPE_IS_TERR 1 -#define DST_TYPE_IS_CABLE 2 - -#define DST_TYPE_HAS_NEWTUNE 1 -#define DST_TYPE_HAS_TS204 2 -#define DST_TYPE_HAS_SYMDIV 4 - -#define HAS_LOCK 1 -#define ATTEMPT_TUNE 2 -#define HAS_POWER 4 - -struct dst_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; - - struct bt878 *bt; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - -static struct dvb_frontend_info dst_info_sat = { - .name = "DST SAT", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, -/* . symbol_rate_tolerance = ???,*/ - .notifier_delay = 50, /* 1/20 s */ - .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK -}; - -static struct dvb_frontend_info dst_info_cable = { - .name = "DST CABLE", - .type = FE_QAM, - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, -/* . symbol_rate_tolerance = ???,*/ - .notifier_delay = 50, /* 1/20 s */ - .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO -}; - -static struct dvb_frontend_info dst_info_terr = { - .name = "DST TERR", - .type = FE_OFDM, - .frequency_min = 137000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, - .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO -}; - -static void dst_packsize(struct dst_data *dst, int psize) -{ - union dst_gpio_packet bits; - - bits.psize = psize; - bt878_device_control(dst->bt, DST_IG_TS, &bits); -} - -static int dst_gpio_outb(struct dst_data *dst, u32 mask, u32 enbb, u32 outhigh) -{ - union dst_gpio_packet enb; - union dst_gpio_packet bits; - int err; - - enb.enb.mask = mask; - enb.enb.enable = enbb; - if ((err = bt878_device_control(dst->bt, DST_IG_ENABLE, &enb)) < 0) { - dprintk("%s: dst_gpio_enb error (err == %i, mask == 0x%02x, enb == 0x%02x)\n", __FUNCTION__, err, mask, enbb); - return -EREMOTEIO; - } - - /* because complete disabling means no output, no need to do output packet */ - if (enbb == 0) - return 0; - - bits.outp.mask = enbb; - bits.outp.highvals = outhigh; - - if ((err = bt878_device_control(dst->bt, DST_IG_WRITE, &bits)) < 0) { - dprintk("%s: dst_gpio_outb error (err == %i, enbb == 0x%02x, outhigh == 0x%02x)\n", __FUNCTION__, err, enbb, outhigh); - return -EREMOTEIO; - } - return 0; -} - -static int dst_gpio_inb(struct dst_data *dst, u8 * result) -{ - union dst_gpio_packet rd_packet; - int err; - - *result = 0; - - if ((err = bt878_device_control(dst->bt, DST_IG_READ, &rd_packet)) < 0) { - dprintk("%s: dst_gpio_inb error (err == %i)\n", __FUNCTION__, err); - return -EREMOTEIO; - } - *result = (u8) rd_packet.rd.value; - return 0; -} - -#define DST_I2C_ENABLE 1 -#define DST_8820 2 - -static int dst_reset8820(struct dst_data *dst) -{ - int retval; - /* pull 8820 gpio pin low, wait, high, wait, then low */ - // dprintk ("%s: reset 8820\n", __FUNCTION__); - retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0); - if (retval < 0) - return retval; - msleep(10); - retval = dst_gpio_outb(dst, DST_8820, DST_8820, DST_8820); - if (retval < 0) - return retval; - /* wait for more feedback on what works here * - msleep(10); - retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0); - if (retval < 0) - return retval; - */ - return 0; -} - -static int dst_i2c_enable(struct dst_data *dst) -{ - int retval; - /* pull I2C enable gpio pin low, wait */ - // dprintk ("%s: i2c enable\n", __FUNCTION__); - retval = dst_gpio_outb(dst, ~0, DST_I2C_ENABLE, 0); - if (retval < 0) - return retval; - // dprintk ("%s: i2c enable delay\n", __FUNCTION__); - msleep(33); - return 0; -} - -static int dst_i2c_disable(struct dst_data *dst) -{ - int retval; - /* release I2C enable gpio pin, wait */ - // dprintk ("%s: i2c disable\n", __FUNCTION__); - retval = dst_gpio_outb(dst, ~0, 0, 0); - if (retval < 0) - return retval; - // dprintk ("%s: i2c disable delay\n", __FUNCTION__); - msleep(33); - return 0; -} - -static int dst_wait_dst_ready(struct dst_data *dst) -{ - u8 reply; - int retval; - int i; - for (i = 0; i < 200; i++) { - retval = dst_gpio_inb(dst, &reply); - if (retval < 0) - return retval; - if ((reply & DST_I2C_ENABLE) == 0) { - dprintk("%s: dst wait ready after %d\n", __FUNCTION__, i); - return 1; - } - msleep(5); - } - dprintk("%s: dst wait NOT ready after %d\n", __FUNCTION__, i); - return 0; -} - -static int write_dst(struct dst_data *dst, u8 * data, u8 len) -{ - struct i2c_msg msg = { - .addr = DST_I2C_ADDR,.flags = 0,.buf = data,.len = len - }; - int err; - int cnt; - - if (dst_debug && dst_verbose) { - u8 i; - dprintk("%s writing", __FUNCTION__); - for (i = 0; i < len; i++) { - dprintk(" 0x%02x", data[i]); - } - dprintk("\n"); - } - msleep(30); - for (cnt = 0; cnt < 4; cnt++) { - if ((err = i2c_transfer(dst->i2c, &msg, 1)) < 0) { - dprintk("%s: write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, data[0]); - dst_i2c_disable(dst); - msleep(500); - dst_i2c_enable(dst); - msleep(500); - continue; - } else - break; - } - if (cnt >= 4) - return -EREMOTEIO; - return 0; -} - -static int read_dst(struct dst_data *dst, u8 * ret, u8 len) -{ - struct i2c_msg msg = {.addr = DST_I2C_ADDR,.flags = I2C_M_RD,.buf = ret,.len = len }; - int err; - int cnt; - - for (cnt = 0; cnt < 4; cnt++) { - if ((err = i2c_transfer(dst->i2c, &msg, 1)) < 0) { - dprintk("%s: read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, ret[0]); - dst_i2c_disable(dst); - dst_i2c_enable(dst); - continue; - } else - break; - } - if (cnt >= 4) - return -EREMOTEIO; - dprintk("%s reply is 0x%x\n", __FUNCTION__, ret[0]); - if (dst_debug && dst_verbose) { - for (err = 1; err < len; err++) - dprintk(" 0x%x", ret[err]); - if (err > 1) - dprintk("\n"); - } - return 0; -} - -static int dst_set_freq(struct dst_data *dst, u32 freq) -{ - u8 *val; - - dst->frequency = freq; - - // dprintk("%s: set frequency %u\n", __FUNCTION__, freq); - if (dst->dst_type == DST_TYPE_IS_SAT) { - freq = freq / 1000; - if (freq < 950 || freq > 2150) - return -EINVAL; - val = &dst->tx_tuna[0]; - val[2] = (freq >> 8) & 0x7f; - val[3] = (u8) freq; - val[4] = 1; - val[8] &= ~4; - if (freq < 1531) - val[8] |= 4; - } else if (dst->dst_type == DST_TYPE_IS_TERR) { - freq = freq / 1000; - if (freq < 137000 || freq > 858000) - return -EINVAL; - val = &dst->tx_tuna[0]; - val[2] = (freq >> 16) & 0xff; - val[3] = (freq >> 8) & 0xff; - val[4] = (u8) freq; - val[5] = 0; - switch (dst->bandwidth) { - case BANDWIDTH_6_MHZ: - val[6] = 6; - break; - - case BANDWIDTH_7_MHZ: - case BANDWIDTH_AUTO: - val[6] = 7; - break; - - case BANDWIDTH_8_MHZ: - val[6] = 8; - break; - } - - val[7] = 0; - val[8] = 0; - } else if (dst->dst_type == DST_TYPE_IS_CABLE) { - /* guess till will get one */ - freq = freq / 1000; - val = &dst->tx_tuna[0]; - val[2] = (freq >> 16) & 0xff; - val[3] = (freq >> 8) & 0xff; - val[4] = (u8) freq; - } else - return -EINVAL; - return 0; -} - -static int dst_set_bandwidth(struct dst_data *dst, fe_bandwidth_t bandwidth) -{ - u8 *val; - - dst->bandwidth = bandwidth; - - if (dst->dst_type != DST_TYPE_IS_TERR) - return 0; - - val = &dst->tx_tuna[0]; - switch (bandwidth) { - case BANDWIDTH_6_MHZ: - val[6] = 6; - break; - - case BANDWIDTH_7_MHZ: - val[6] = 7; - break; - - case BANDWIDTH_8_MHZ: - val[6] = 8; - break; - - default: - return -EINVAL; - } - return 0; -} - -static int dst_set_inversion(struct dst_data *dst, fe_spectral_inversion_t inversion) -{ - u8 *val; - - dst->inversion = inversion; - - val = &dst->tx_tuna[0]; - - val[8] &= ~0x80; - - switch (inversion) { - case INVERSION_OFF: - break; - case INVERSION_ON: - val[8] |= 0x80; - break; - default: - return -EINVAL; - } - return 0; -} - - -static int dst_set_fec(struct dst_data *dst, fe_code_rate_t fec) -{ - dst->fec = fec; - return 0; -} - -static fe_code_rate_t dst_get_fec(struct dst_data *dst) -{ - return dst->fec; -} - -static int dst_set_symbolrate(struct dst_data *dst, u32 srate) -{ - u8 *val; - u32 symcalc; - u64 sval; - - dst->symbol_rate = srate; - - if (dst->dst_type == DST_TYPE_IS_TERR) { - return 0; - } - // dprintk("%s: set srate %u\n", __FUNCTION__, srate); - srate /= 1000; - val = &dst->tx_tuna[0]; - - if (dst->type_flags & DST_TYPE_HAS_SYMDIV) { - sval = srate; - sval <<= 20; - do_div(sval, 88000); - symcalc = (u32) sval; - // dprintk("%s: set symcalc %u\n", __FUNCTION__, symcalc); - val[5] = (u8) (symcalc >> 12); - val[6] = (u8) (symcalc >> 4); - val[7] = (u8) (symcalc << 4); - } else { - val[5] = (u8) (srate >> 16) & 0x7f; - val[6] = (u8) (srate >> 8); - val[7] = (u8) srate; - } - val[8] &= ~0x20; - if (srate > 8000) - val[8] |= 0x20; - return 0; -} - - -static u8 dst_check_sum(u8 * buf, u32 len) -{ - u32 i; - u8 val = 0; - if (!len) - return 0; - for (i = 0; i < len; i++) { - val += buf[i]; - } - return ((~val) + 1); -} - -typedef struct dst_types { - char *mstr; - int offs; - u8 dst_type; - u32 type_flags; -} DST_TYPES; - -struct dst_types dst_tlist[] = { - {"DST-020", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV}, - {"DST-030", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE}, - {"DST-03T", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204}, - {"DST-MOT", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV}, - {"DST-CI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE}, - {"DSTMCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE}, - {"DSTFCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE}, - {"DCTNEW", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE}, - {"DCT_CI", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE | DST_TYPE_HAS_TS204}, - {"DTTDIG", 1, DST_TYPE_IS_TERR, 0} -}; - -/* DCTNEW and DCT-CI are guesses */ - -static void dst_type_flags_print(u32 type_flags) -{ - printk("DST type flags :"); - if (type_flags & DST_TYPE_HAS_NEWTUNE) - printk(" 0x%x newtuner", DST_TYPE_HAS_NEWTUNE); - if (type_flags & DST_TYPE_HAS_TS204) - printk(" 0x%x ts204", DST_TYPE_HAS_TS204); - if (type_flags & DST_TYPE_HAS_SYMDIV) - printk(" 0x%x symdiv", DST_TYPE_HAS_SYMDIV); - printk("\n"); -} - -static int dst_type_print(u8 type) -{ - char *otype; - switch (type) { - case DST_TYPE_IS_SAT: - otype = "satellite"; - break; - case DST_TYPE_IS_TERR: - otype = "terrestial TV"; - break; - case DST_TYPE_IS_CABLE: - otype = "terrestial TV"; - break; - default: - printk("%s: invalid dst type %d\n", __FUNCTION__, type); - return -EINVAL; - } - printk("DST type : %s\n", otype); - return 0; -} - -static int dst_check_ci(struct dst_data *dst) -{ - u8 txbuf[8]; - u8 rxbuf[8]; - int retval; - int i; - struct dst_types *dsp; - u8 use_dst_type; - u32 use_type_flags; - - memset(txbuf, 0, sizeof(txbuf)); - txbuf[1] = 6; - txbuf[7] = dst_check_sum(txbuf, 7); - - dst_i2c_enable(dst); - dst_reset8820(dst); - retval = write_dst(dst, txbuf, 8); - if (retval < 0) { - dst_i2c_disable(dst); - dprintk("%s: write not successful, maybe no card?\n", __FUNCTION__); - return retval; - } - msleep(3); - retval = read_dst(dst, rxbuf, 1); - dst_i2c_disable(dst); - if (retval < 0) { - dprintk("%s: read not successful, maybe no card?\n", __FUNCTION__); - return retval; - } - if (rxbuf[0] != 0xff) { - dprintk("%s: write reply not 0xff, not ci (%02x)\n", __FUNCTION__, rxbuf[0]); - return retval; - } - if (!dst_wait_dst_ready(dst)) - return 0; - // dst_i2c_enable(i2c); Dimitri - retval = read_dst(dst, rxbuf, 8); - dst_i2c_disable(dst); - if (retval < 0) { - dprintk("%s: read not successful\n", __FUNCTION__); - return retval; - } - if (rxbuf[7] != dst_check_sum(rxbuf, 7)) { - dprintk("%s: checksum failure\n", __FUNCTION__); - return retval; - } - rxbuf[7] = '\0'; - for (i = 0, dsp = &dst_tlist[0]; i < sizeof(dst_tlist) / sizeof(dst_tlist[0]); i++, dsp++) { - if (!strncmp(&rxbuf[dsp->offs], dsp->mstr, strlen(dsp->mstr))) { - use_type_flags = dsp->type_flags; - use_dst_type = dsp->dst_type; - printk("%s: recognize %s\n", __FUNCTION__, dsp->mstr); - break; - } - } - if (i >= sizeof(dst_tlist) / sizeof(dst_tlist[0])) { - printk("%s: unable to recognize %s or %s\n", __FUNCTION__, &rxbuf[0], &rxbuf[1]); - printk("%s please email linux-dvb@linuxtv.org with this type in\n", __FUNCTION__); - use_dst_type = DST_TYPE_IS_SAT; - use_type_flags = DST_TYPE_HAS_SYMDIV; - } - dst_type_print(use_dst_type); - - dst->type_flags = use_type_flags; - dst->dst_type = use_dst_type; - dst_type_flags_print(dst->type_flags); - - if (dst->type_flags & DST_TYPE_HAS_TS204) { - dst_packsize(dst, 204); - } - return 0; -} - -static int dst_command(struct dst_data *dst, u8 * data, u8 len) -{ - int retval; - u8 reply; - - dst_i2c_enable(dst); - dst_reset8820(dst); - retval = write_dst(dst, data, len); - if (retval < 0) { - dst_i2c_disable(dst); - dprintk("%s: write not successful\n", __FUNCTION__); - return retval; - } - msleep(33); - retval = read_dst(dst, &reply, 1); - dst_i2c_disable(dst); - if (retval < 0) { - dprintk("%s: read verify not successful\n", __FUNCTION__); - return retval; - } - if (reply != 0xff) { - dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply); - return 0; - } - if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) - return 0; - if (!dst_wait_dst_ready(dst)) - return 0; - // dst_i2c_enable(i2c); Per dimitri - retval = read_dst(dst, dst->rxbuffer, 8); - dst_i2c_disable(dst); - if (retval < 0) { - dprintk("%s: read not successful\n", __FUNCTION__); - return 0; - } - if (dst->rxbuffer[7] != dst_check_sum(dst->rxbuffer, 7)) { - dprintk("%s: checksum failure\n", __FUNCTION__); - return 0; - } - return 0; -} - -static int dst_get_signal(struct dst_data *dst) -{ - int retval; - u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; - - if ((dst->diseq_flags & ATTEMPT_TUNE) == 0) { - dst->decode_lock = dst->decode_strength = dst->decode_snr = 0; - return 0; - } - if (0 == (dst->diseq_flags & HAS_LOCK)) { - dst->decode_lock = dst->decode_strength = dst->decode_snr = 0; - return 0; - } - if (time_after_eq(jiffies, dst->cur_jiff + (HZ / 5))) { - retval = dst_command(dst, get_signal, 8); - if (retval < 0) - return retval; - if (dst->dst_type == DST_TYPE_IS_SAT) { - dst->decode_lock = ((dst->rxbuffer[6] & 0x10) == 0) ? 1 : 0; - dst->decode_strength = dst->rxbuffer[5] << 8; - dst->decode_snr = dst->rxbuffer[2] << 8 | dst->rxbuffer[3]; - } else if ((dst->dst_type == DST_TYPE_IS_TERR) || (dst->dst_type == DST_TYPE_IS_CABLE)) { - dst->decode_lock = (dst->rxbuffer[1]) ? 1 : 0; - dst->decode_strength = dst->rxbuffer[4] << 8; - dst->decode_snr = dst->rxbuffer[3] << 8; - } - dst->cur_jiff = jiffies; - } - return 0; -} - -/* - * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 - * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 - * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 - * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 - * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 - * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 - * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 - * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec - * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 - * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 - * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 - */ - -static int dst_set_diseqc(struct dst_data *dst, u8 * cmd, u8 len) -{ - u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; - - if (dst->dst_type == DST_TYPE_IS_TERR) - return 0; - - if (len == 0 || len > 4) - return -EINVAL; - memcpy(&paket[3], cmd, len); - paket[7] = dst_check_sum(&paket[0], 7); - dst_command(dst, paket, 8); - return 0; -} - -static int dst_tone_power_cmd(struct dst_data *dst) -{ - u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; - - if (dst->dst_type == DST_TYPE_IS_TERR) - return 0; - - if (dst->voltage == SEC_VOLTAGE_OFF) - paket[4] = 0; - else - paket[4] = 1; - if (dst->tone == SEC_TONE_ON) - paket[2] = dst->k22; - else - paket[2] = 0; - paket[7] = dst_check_sum(&paket[0], 7); - dst_command(dst, paket, 8); - return 0; -} - -static int dst_set_voltage(struct dst_data *dst, fe_sec_voltage_t voltage) -{ - u8 *val; - int need_cmd; - - dst->voltage = voltage; - - if (dst->dst_type == DST_TYPE_IS_TERR) - return 0; - - need_cmd = 0; - val = &dst->tx_tuna[0]; - val[8] &= ~0x40; - switch (voltage) { - case SEC_VOLTAGE_13: - if ((dst->diseq_flags & HAS_POWER) == 0) - need_cmd = 1; - dst->diseq_flags |= HAS_POWER; - break; - case SEC_VOLTAGE_18: - if ((dst->diseq_flags & HAS_POWER) == 0) - need_cmd = 1; - dst->diseq_flags |= HAS_POWER; - val[8] |= 0x40; - break; - case SEC_VOLTAGE_OFF: - need_cmd = 1; - dst->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); - break; - default: - return -EINVAL; - } - if (need_cmd) { - dst_tone_power_cmd(dst); - } - return 0; -} - - -static int dst_set_tone(struct dst_data *dst, fe_sec_tone_mode_t tone) -{ - u8 *val; - - dst->tone = tone; - - if (dst->dst_type == DST_TYPE_IS_TERR) - return 0; - - val = &dst->tx_tuna[0]; - - val[8] &= ~0x1; - - switch (tone) { - case SEC_TONE_OFF: - break; - case SEC_TONE_ON: - val[8] |= 1; - break; - default: - return -EINVAL; - } - dst_tone_power_cmd(dst); - return 0; -} - -static int dst_get_tuna(struct dst_data *dst) -{ - int retval; - if ((dst->diseq_flags & ATTEMPT_TUNE) == 0) - return 0; - dst->diseq_flags &= ~(HAS_LOCK); - if (!dst_wait_dst_ready(dst)) - return 0; - if (dst->type_flags & DST_TYPE_HAS_NEWTUNE) { - /* how to get variable length reply ???? */ - retval = read_dst(dst, dst->rx_tuna, 10); - } else { - retval = read_dst(dst, &dst->rx_tuna[2], 8); - } - if (retval < 0) { - dprintk("%s: read not successful\n", __FUNCTION__); - return 0; - } - if (dst->type_flags & DST_TYPE_HAS_NEWTUNE) { - if (dst->rx_tuna[9] != dst_check_sum(&dst->rx_tuna[0], 9)) { - dprintk("%s: checksum failure?\n", __FUNCTION__); - return 0; - } - } else { - if (dst->rx_tuna[9] != dst_check_sum(&dst->rx_tuna[2], 7)) { - dprintk("%s: checksum failure?\n", __FUNCTION__); - return 0; - } - } - if (dst->rx_tuna[2] == 0 && dst->rx_tuna[3] == 0) - return 0; - dst->decode_freq = ((dst->rx_tuna[2] & 0x7f) << 8) + dst->rx_tuna[3]; - - dst->decode_lock = 1; - /* - dst->decode_n1 = (dst->rx_tuna[4] << 8) + - (dst->rx_tuna[5]); - - dst->decode_n2 = (dst->rx_tuna[8] << 8) + - (dst->rx_tuna[7]); - */ - dst->diseq_flags |= HAS_LOCK; - /* dst->cur_jiff = jiffies; */ - return 1; -} - -static int dst_write_tuna(struct dst_data *dst) -{ - int retval; - u8 reply; - - dprintk("%s: type_flags 0x%x \n", __FUNCTION__, dst->type_flags); - dst->decode_freq = 0; - dst->decode_lock = dst->decode_strength = dst->decode_snr = 0; - if (dst->dst_type == DST_TYPE_IS_SAT) { - if (!(dst->diseq_flags & HAS_POWER)) - dst_set_voltage(dst, SEC_VOLTAGE_13); - } - dst->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); - dst_i2c_enable(dst); - if (dst->type_flags & DST_TYPE_HAS_NEWTUNE) { - dst_reset8820(dst); - dst->tx_tuna[9] = dst_check_sum(&dst->tx_tuna[0], 9); - retval = write_dst(dst, &dst->tx_tuna[0], 10); - } else { - dst->tx_tuna[9] = dst_check_sum(&dst->tx_tuna[2], 7); - retval = write_dst(dst, &dst->tx_tuna[2], 8); - } - if (retval < 0) { - dst_i2c_disable(dst); - dprintk("%s: write not successful\n", __FUNCTION__); - return retval; - } - msleep(3); - retval = read_dst(dst, &reply, 1); - dst_i2c_disable(dst); - if (retval < 0) { - dprintk("%s: read verify not successful\n", __FUNCTION__); - return retval; - } - if (reply != 0xff) { - dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply); - return 0; - } - dst->diseq_flags |= ATTEMPT_TUNE; - return dst_get_tuna(dst); -} - -static void dst_init(struct dst_data *dst) -{ - static u8 ini_satci_tuna[] = { 9, 0, 3, 0xb6, 1, 0, 0x73, 0x21, 0, 0 }; - static u8 ini_satfta_tuna[] = { 0, 0, 3, 0xb6, 1, 0x55, 0xbd, 0x50, 0, 0 }; - static u8 ini_tvfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; - static u8 ini_tvci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; - static u8 ini_cabfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; - static u8 ini_cabci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 }; - dst->inversion = INVERSION_ON; - dst->voltage = SEC_VOLTAGE_13; - dst->tone = SEC_TONE_OFF; - dst->symbol_rate = 29473000; - dst->fec = FEC_AUTO; - dst->diseq_flags = 0; - dst->k22 = 0x02; - dst->bandwidth = BANDWIDTH_7_MHZ; - dst->cur_jiff = jiffies; - if (dst->dst_type == DST_TYPE_IS_SAT) { - dst->frequency = 950000; - memcpy(dst->tx_tuna, ((dst->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_satci_tuna : ini_satfta_tuna), sizeof(ini_satfta_tuna)); - } else if (dst->dst_type == DST_TYPE_IS_TERR) { - dst->frequency = 137000000; - memcpy(dst->tx_tuna, ((dst->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_tvci_tuna : ini_tvfta_tuna), sizeof(ini_tvfta_tuna)); - } else if (dst->dst_type == DST_TYPE_IS_CABLE) { - dst->frequency = 51000000; - memcpy(dst->tx_tuna, ((dst->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_cabci_tuna : ini_cabfta_tuna), sizeof(ini_cabfta_tuna)); - } -} - -struct lkup { - unsigned int cmd; - char *desc; -} looker[] = { - { - FE_GET_INFO, "FE_GET_INFO:"}, { - FE_READ_STATUS, "FE_READ_STATUS:"}, { - FE_READ_BER, "FE_READ_BER:"}, { - FE_READ_SIGNAL_STRENGTH, "FE_READ_SIGNAL_STRENGTH:"}, { - FE_READ_SNR, "FE_READ_SNR:"}, { - FE_READ_UNCORRECTED_BLOCKS, "FE_READ_UNCORRECTED_BLOCKS:"}, { - FE_SET_FRONTEND, "FE_SET_FRONTEND:"}, { - FE_GET_FRONTEND, "FE_GET_FRONTEND:"}, { - FE_SLEEP, "FE_SLEEP:"}, { - FE_INIT, "FE_INIT:"}, { - FE_SET_TONE, "FE_SET_TONE:"}, { -FE_SET_VOLTAGE, "FE_SET_VOLTAGE:"},}; - -static int dst_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct dst_data *dst = fe->data; - int retval; - /* - char *cc; - - cc = "FE_UNSUPP:"; - for(retval = 0; retval < sizeof(looker) / sizeof(looker[0]); retval++) { - if (looker[retval].cmd == cmd) { - cc = looker[retval].desc; - break; - } - } - dprintk("%s cmd %s (0x%x)\n",__FUNCTION__, cc, cmd); - */ - // printk("%s: dst %8.8x bt %8.8x i2c %8.8x\n", __FUNCTION__, dst, dst->bt, dst->i2c); - /* should be set by attach, but just in case */ - - switch (cmd) { - case FE_GET_INFO: - { - struct dvb_frontend_info *info; - info = &dst_info_sat; - if (dst->dst_type == DST_TYPE_IS_TERR) - info = &dst_info_terr; - else if (dst->dst_type == DST_TYPE_IS_CABLE) - info = &dst_info_cable; - memcpy(arg, info, sizeof(struct dvb_frontend_info)); - break; - } - case FE_READ_STATUS: - { - fe_status_t *status = arg; - - *status = 0; - if (dst->diseq_flags & HAS_LOCK) { - dst_get_signal(dst); - if (dst->decode_lock) - *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; - } - break; - } - - case FE_READ_BER: - { - /* guess */ - // *(u32*) arg = dst->decode_n1; - *(u32 *) arg = 0; - return -EOPNOTSUPP; - } - - case FE_READ_SIGNAL_STRENGTH: - { - dst_get_signal(dst); - *((u16 *) arg) = dst->decode_strength; - break; - } - - case FE_READ_SNR: - { - dst_get_signal(dst); - *((u16 *) arg) = dst->decode_snr; - break; - } - - case FE_READ_UNCORRECTED_BLOCKS: - { - *((u32 *) arg) = 0; /* the stv0299 can't measure BER and */ - return -EOPNOTSUPP; /* errors at the same time.... */ - } - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - dst_set_freq(dst, p->frequency); - dst_set_inversion(dst, p->inversion); - if (dst->dst_type == DST_TYPE_IS_SAT) { - dst_set_fec(dst, p->u.qpsk.fec_inner); - dst_set_symbolrate(dst, p->u.qpsk.symbol_rate); - } else if (dst->dst_type == DST_TYPE_IS_TERR) { - dst_set_bandwidth(dst, p->u.ofdm.bandwidth); - } else if (dst->dst_type == DST_TYPE_IS_CABLE) { - dst_set_fec(dst, p->u.qam.fec_inner); - dst_set_symbolrate(dst, p->u.qam.symbol_rate); - } - dst_write_tuna(dst); - - break; - } - - case FE_GET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - - p->frequency = dst->decode_freq; - p->inversion = dst->inversion; - if (dst->dst_type == DST_TYPE_IS_SAT) { - p->u.qpsk.symbol_rate = dst->symbol_rate; - p->u.qpsk.fec_inner = dst_get_fec(dst); - } else if (dst->dst_type == DST_TYPE_IS_TERR) { - p->u.ofdm.bandwidth = dst->bandwidth; - } else if (dst->dst_type == DST_TYPE_IS_CABLE) { - p->u.qam.symbol_rate = dst->symbol_rate; - p->u.qam.fec_inner = dst_get_fec(dst); - p->u.qam.modulation = QAM_AUTO; - } - break; - } - - case FE_SLEEP: - return 0; - - case FE_INIT: - dst_init(dst); - break; - - case FE_DISEQC_SEND_MASTER_CMD: - { - struct dvb_diseqc_master_cmd *cmd = (struct dvb_diseqc_master_cmd *) arg; - retval = dst_set_diseqc(dst, cmd->msg, cmd->msg_len); - if (retval < 0) - return retval; - break; - } - case FE_SET_TONE: - retval = dst_set_tone(dst, (fe_sec_tone_mode_t) arg); - if (retval < 0) - return retval; - break; - case FE_SET_VOLTAGE: - retval = dst_set_voltage(dst, (fe_sec_voltage_t) arg); - if (retval < 0) - return retval; - break; - default: - return -EOPNOTSUPP; - }; - - return 0; -} - -static ssize_t attr_read_type(struct device *dev, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dst_data *dst = (struct dst_data *) i2c_get_clientdata(client); - return sprintf(buf, "0x%02x\n", dst->dst_type); -} - -static ssize_t attr_write_type(struct device *dev, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dst_data *dst = (struct dst_data *) i2c_get_clientdata(client); - unsigned long type; - type = simple_strtoul(buf, NULL, 0); - dst->dst_type = type & 0xff; - return strlen(buf) + 1; -} - -/* dst_type, "Type of DST card, 0 Satellite, 1 terrestial, 2 Cable, default driver determined"); */ -static struct device_attribute dev_attr_client_type = { - .attr = {.name = "type",.mode = S_IRUGO | S_IWUGO,.owner = THIS_MODULE}, - .show = &attr_read_type, - .store = &attr_write_type, -}; - -static ssize_t attr_read_flags(struct device *dev, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dst_data *dst = (struct dst_data *) i2c_get_clientdata(client); - return sprintf(buf, "0x%02x\n", dst->type_flags); -} - -static ssize_t attr_write_flags(struct device *dev, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dst_data *dst = (struct dst_data *) i2c_get_clientdata(client); - unsigned long flags; - flags = simple_strtoul(buf, NULL, 0); - dst->type_flags = flags & 0xffffffff; - return strlen(buf) + 1; -} - -/* dst_type_flags, "Type flags of DST card, bitfield 1=10 byte tuner, 2=TS is 204, 4=symdiv"); */ -static struct device_attribute dev_attr_client_flags = { - .attr = {.name = "flags",.mode = S_IRUGO | S_IWUGO,.owner = THIS_MODULE}, - .show = &attr_read_flags, - .store = &attr_write_flags, -}; - -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) -{ - struct i2c_client *client; - struct dst_data *dst; - struct bt878 *bt; - struct dvb_frontend_info *info; - int ret; - - bt = bt878_find_by_i2c_adap(adapter); - if (!bt) - return -ENODEV; - - dst = kmalloc(sizeof(struct dst_data), GFP_KERNEL); - if (dst == NULL) { - printk(KERN_INFO "%s: Out of memory.\n", __FUNCTION__); - return -ENOMEM; - } - - memset(dst, 0, sizeof(*dst)); - dst->bt = bt; - dst->i2c = adapter; - if (dst_check_ci(dst) < 0) { - kfree(dst); - return -ENODEV; - } - dst_init(dst); - - dprintk("%s: register dst %8.8x bt %8.8x i2c %8.8x\n", __FUNCTION__, (u32) dst, (u32) (dst->bt), (u32) (dst->i2c)); - - switch (dst->dst_type) { - case DST_TYPE_IS_TERR: - info = &dst_info_terr; - break; - case DST_TYPE_IS_CABLE: - info = &dst_info_cable; - break; - case DST_TYPE_IS_SAT: - info = &dst_info_sat; - break; - default: - printk("dst: unknown frontend type. please report to the LinuxTV.org DVB mailinglist.\n"); - kfree(dst); - return -ENODEV; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(dst); - return -ENOMEM; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = DST_I2C_ADDR; - - i2c_set_clientdata(client, (void *) dst); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(dst); - return -EFAULT; - } - - BUG_ON(!dst->dvb); - - device_create_file(&client->dev, &dev_attr_client_type); - device_create_file(&client->dev, &dev_attr_client_flags); - - ret = dvb_register_frontend(dst_ioctl, dst->dvb, dst, info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(dst); - return -EFAULT; - } - - return 0; -} - -static int detach_client(struct i2c_client *client) -{ - struct dst_data *state = (struct dst_data *) i2c_get_clientdata(client); - - dvb_unregister_frontend(dst_ioctl, state->dvb); - - device_remove_file(&client->dev, &dev_attr_client_type); - device_remove_file(&client->dev, &dev_attr_client_flags); - - i2c_detach_client(client); - BUG_ON(state->dvb); - - kfree(client); - kfree(state); - - return 0; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct dst_data *state = (struct dst_data *) i2c_get_clientdata(client); - - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter *) arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "dst", - .id = I2C_DRIVERID_DVBFE_DST, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - I2C_DEVNAME("dst"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_dst(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_dst(void) -{ - if (i2c_del_driver(&driver)) - printk("dst: driver deregistration failed\n"); -} - -module_init(init_dst); -module_exit(exit_dst); - -MODULE_DESCRIPTION("DST DVB-S Frontend"); -MODULE_AUTHOR("Jamie Honan"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c index f9538ffa6745..b17a19526b4e 100644 --- a/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -24,244 +24,258 @@ #include <linux/init.h> #include "dvb_frontend.h" +#include "dvb_dummy_fe.h" -#define FRONTEND_NAME "dvbfe_dummy" - -static int frontend_type; -module_param(frontend_type, int, 0444); -MODULE_PARM_DESC(frontend_type, "0 == DVB-S, 1 == DVB-C, 2 == DVB-T"); - -/* depending on module parameter sct deliver different infos - */ - -static struct dvb_frontend_info dvb_s_dummyfe_info = { - .name = "DVB-S dummy frontend", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 250, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, -/* .symbol_rate_tolerance = ???,*/ - .notifier_delay = 50, /* 1/20 s */ - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK -}; - -static struct dvb_frontend_info dvb_c_dummyfe_info = { - .name = "DVB-C dummy frontend", - .type = FE_QAM, - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ -#if 0 - .frequency_tolerance = ???, - .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ - .notifier_delay = ?, -#endif - .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | - FE_CAN_QAM_128 | FE_CAN_QAM_256 | - FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO -}; -static struct dvb_frontend_info dvb_t_dummyfe_info = { - .name = "DVB-T dummy frontend", - .type = FE_OFDM, - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, - /*.frequency_tolerance = */ /* FIXME: 12% of SR */ - .symbol_rate_min = 0, /* FIXME */ - .symbol_rate_max = 9360000, /* FIXME */ - .symbol_rate_tolerance = 4000, - .notifier_delay = 0, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; -struct dvb_frontend_info *frontend_info(void) -{ - switch(frontend_type) { - case 2: - return &dvb_t_dummyfe_info; - case 1: - return &dvb_c_dummyfe_info; - case 0: - default: - return &dvb_s_dummyfe_info; - } -} +struct dvb_dummy_fe_state { + struct dvb_frontend_ops ops; -static int dvbdummyfe_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, frontend_info(), - sizeof(struct dvb_frontend_info)); - break; + struct dvb_frontend frontend; +}; - case FE_READ_STATUS: +static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) { - fe_status_t *status = arg; *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - break; + + return 0; } - case FE_READ_BER: +static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; *ber = 0; - break; + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u8 signal = 0xff; - *((u16*) arg) = (signal << 8) | signal; - break; + *strength = 0; + return 0; } - case FE_READ_SNR: +static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr) { - u8 snr = 0xf0; - *(u16*) arg = (snr << 8) | snr; - break; + *snr = 0; + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: - *(u32*) arg = 0; - break; - - case FE_SET_FRONTEND: - break; - - case FE_GET_FRONTEND: - break; - - case FE_SLEEP: +static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + *ucblocks = 0; return 0; +} - case FE_INIT: +static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ return 0; +} - case FE_SET_TONE: - return -EOPNOTSUPP; +static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + return 0; +} - case FE_SET_VOLTAGE: +static int dvb_dummy_fe_sleep(struct dvb_frontend* fe) +{ return 0; +} - default: - return -EOPNOTSUPP; +static int dvb_dummy_fe_init(struct dvb_frontend* fe) +{ + return 0; } + +static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ return 0; } -static struct i2c_client client_template; +static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + return 0; +} -static int dvbdummyfe_attach_adapter(struct i2c_adapter *adapter) +static void dvb_dummy_fe_release(struct dvb_frontend* fe) { - struct dvb_adapter *dvb; - struct i2c_client *client; - int ret; + struct dvb_dummy_fe_state* state = (struct dvb_dummy_fe_state*) fe->demodulator_priv; + kfree(state); +} - if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL) - return -ENOMEM; +static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops; - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; +struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) +{ + struct dvb_dummy_fe_state* state = NULL; - if ((ret = i2c_attach_client(client))) { - kfree(client); - return ret; -} + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; - dvb = i2c_get_clientdata(client); - BUG_ON(!dvb); + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops)); - if ((ret = dvb_register_frontend(dvbdummyfe_ioctl, dvb, NULL, - frontend_info(), THIS_MODULE))) { - kfree(client); - return ret; - } + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - return 0; +error: + if (state) kfree(state); + return NULL; } +static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; -static int dvbdummyfe_detach_client(struct i2c_client *client) +struct dvb_frontend* dvb_dummy_fe_qpsk_attach() { - struct dvb_adapter *dvb = i2c_get_clientdata(client); + struct dvb_dummy_fe_state* state = NULL; - dvb_unregister_frontend(dvbdummyfe_ioctl, dvb); - i2c_detach_client(client); - kfree(client); - return 0; -} + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; -static int dvbdummyfe_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - switch(cmd) { - case FE_REGISTER: - i2c_set_clientdata(client, arg); - break; - case FE_UNREGISTER: - break; - default: - return -EOPNOTSUPP; - } + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops)); - return 0; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_DUMMY, - .flags = I2C_DF_NOTIFY, - .attach_adapter = dvbdummyfe_attach_adapter, - .detach_client = dvbdummyfe_detach_client, - .command = dvbdummyfe_command, -}; +static struct dvb_frontend_ops dvb_dummy_fe_qam_ops; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_dvbdummyfe (void) +struct dvb_frontend* dvb_dummy_fe_qam_attach() { - return i2c_add_driver(&driver); -} + struct dvb_dummy_fe_state* state = NULL; -static void __exit exit_dvbdummyfe (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "dummyfe: driver deregistration failed.\n"); + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } +static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { + + .info = { + .name = "Dummy DVB-T", + .type = FE_OFDM, + .frequency_min = 0, + .frequency_max = 863250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, +}; -module_init(init_dvbdummyfe); -module_exit(exit_dvbdummyfe); +static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { + + .info = { + .name = "Dummy DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ + .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, +}; +static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { + + .info = { + .name = "Dummy DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 250, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, + + .set_voltage = dvb_dummy_fe_set_voltage, + .set_tone = dvb_dummy_fe_set_tone, +}; MODULE_DESCRIPTION("DVB DUMMY Frontend"); MODULE_AUTHOR("Emard"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach); +EXPORT_SYMBOL(dvb_dummy_fe_qam_attach); +EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach); diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h new file mode 100644 index 000000000000..8210f19d56ce --- /dev/null +++ b/drivers/media/dvb/frontends/dvb_dummy_fe.h @@ -0,0 +1,32 @@ +/* + * Driver for Dummy Frontend + * + * Written by Emard <emard@softhome.net> + * + * 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 DVB_DUMMY_FE_H +#define DVB_DUMMY_FE_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void); +extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void); +extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void); + +#endif // DVB_DUMMY_FE_H diff --git a/drivers/media/dvb/frontends/grundig_29504-401.c b/drivers/media/dvb/frontends/grundig_29504-401.c deleted file mode 100644 index db1b9e56030e..000000000000 --- a/drivers/media/dvb/frontends/grundig_29504-401.c +++ /dev/null @@ -1,749 +0,0 @@ -/* - driver for Grundig 29504-401 DVB-T Frontends based on - LSI L64781 COFDM demodulator as used in Technotrend DVB-T cards - - Copyright (C) 2001 Holger Waechtler <holger@convergence.de> - for Convergence Integrated Media GmbH - Marko Kohtala <marko.kohtala@luukku.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/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/string.h> -#include <linux/slab.h> - -#include "dvb_frontend.h" - -#define FRONTEND_NAME "dvbfe_l64781" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; -static int old_set_tv_freq; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -module_param(old_set_tv_freq, int, 0644); -MODULE_PARM_DESC(old_set_tv_freq, "Use old tsa5060_set_tv_freq calculations."); - -struct l64781_state { - int first:1; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - -struct dvb_frontend_info l64781_info = { - .name = "Grundig 29504-401 (LSI L64781 Based)", - .type = FE_OFDM, -/* .frequency_min = ???,*/ -/* .frequency_max = ???,*/ - .frequency_stepsize = 166666, -/* .frequency_tolerance = ???,*/ -/* .symbol_rate_tolerance = ???,*/ - .notifier_delay = 0, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | - FE_CAN_MUTE_TS -}; - - -static int l64781_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) -{ - int ret; - u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x55, .flags = 0, .buf = buf, .len = 2 }; - - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) - dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", - __FUNCTION__, reg, ret); - - return (ret != 1) ? -1 : 0; -} - - -static u8 l64781_readreg (struct i2c_adapter *i2c, u8 reg) -{ - int ret; - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - - -static int tsa5060_write (struct i2c_adapter *i2c, u8 data [4]) -{ - int ret; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 }; - - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) - dprintk ("%s: write_reg error == %02x!\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 166666 Hz. - * frequency offset is 36125000 Hz. - */ -static int tsa5060_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u32 div; - u8 buf [4]; - u8 cfg, cpump, band_select; - - if (old_set_tv_freq) - div = (36000000 + freq) / 166666; - else - div = (36125000 + freq) / 166666; - - cfg = 0x88; - - cpump = freq < 175000000 ? 2 : freq < 390000000 ? 1 : - freq < 470000000 ? 2 : freq < 750000000 ? 1 : 3; - - band_select = freq < 175000000 ? 0x0e : freq < 470000000 ? 0x05 : 0x03; - - buf [0] = (div >> 8) & 0x7f; - buf [1] = div & 0xff; - buf [2] = ((div >> 10) & 0x60) | cfg; - - if (old_set_tv_freq) - buf [3] = 0xc0; - else - buf [3] = (cpump << 6) | band_select; - - return tsa5060_write (i2c, buf); -} - - - -static void apply_tps (struct i2c_adapter *i2c) -{ - l64781_writereg (i2c, 0x2a, 0x00); - l64781_writereg (i2c, 0x2a, 0x01); - - /* This here is a little bit questionable because it enables - the automatic update of TPS registers. I think we'd need to - handle the IRQ from FE to update some other registers as - well, or at least implement some magic to tuning to correct - to the TPS received from transmission. */ - l64781_writereg (i2c, 0x2a, 0x02); -} - - -static void reset_afc (struct i2c_adapter *i2c) -{ - /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for - timing offset */ - l64781_writereg (i2c, 0x07, 0x9e); /* stall AFC */ - l64781_writereg (i2c, 0x08, 0); /* AFC INIT FREQ */ - l64781_writereg (i2c, 0x09, 0); - l64781_writereg (i2c, 0x0a, 0); - l64781_writereg (i2c, 0x07, 0x8e); - l64781_writereg (i2c, 0x0e, 0); /* AGC gain to zero in beginning */ - l64781_writereg (i2c, 0x11, 0x80); /* stall TIM */ - l64781_writereg (i2c, 0x10, 0); /* TIM_OFFSET_LSB */ - l64781_writereg (i2c, 0x12, 0); - l64781_writereg (i2c, 0x13, 0); - l64781_writereg (i2c, 0x11, 0x00); -} - - -static int apply_frontend_param (struct i2c_adapter *i2c, - struct dvb_frontend_parameters *param) -{ - /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ - static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; - /* QPSK, QAM_16, QAM_64 */ - static const u8 qam_tab [] = { 2, 4, 0, 6 }; - static const u8 bw_tab [] = { 8, 7, 6 }; /* 8Mhz, 7MHz, 6MHz */ - static const u8 guard_tab [] = { 1, 2, 4, 8 }; - /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ - static const u32 ppm = 8000; - struct dvb_ofdm_parameters *p = ¶m->u.ofdm; - u32 ddfs_offset_fixed; -/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ -/* bw_tab[p->bandWidth]<<10)/15625; */ - u32 init_freq; - u32 spi_bias; - u8 val0x04; - u8 val0x05; - u8 val0x06; - int bw = p->bandwidth - BANDWIDTH_8_MHZ; - - if (param->inversion != INVERSION_ON && - param->inversion != INVERSION_OFF) - return -EINVAL; - - if (bw < 0 || bw > 2) - return -EINVAL; - - if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && - p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && - p->code_rate_HP != FEC_7_8) - return -EINVAL; - - if (p->hierarchy_information != HIERARCHY_NONE && - (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && - p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && - p->code_rate_LP != FEC_7_8)) - return -EINVAL; - - if (p->constellation != QPSK && p->constellation != QAM_16 && - p->constellation != QAM_64) - return -EINVAL; - - if (p->transmission_mode != TRANSMISSION_MODE_2K && - p->transmission_mode != TRANSMISSION_MODE_8K) - return -EINVAL; - - if (p->guard_interval < GUARD_INTERVAL_1_32 || - p->guard_interval > GUARD_INTERVAL_1_4) - return -EINVAL; - - if (p->hierarchy_information < HIERARCHY_NONE || - p->hierarchy_information > HIERARCHY_4) - return -EINVAL; - - ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000; - - /* This works up to 20000 ppm, it overflows if too large ppm! */ - init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / - bw_tab[p->bandwidth] & 0xFFFFFF); - - /* SPI bias calculation is slightly modified to fit in 32bit */ - /* will work for high ppm only... */ - spi_bias = 378 * (1 << 10); - spi_bias *= 16; - spi_bias *= bw_tab[p->bandwidth]; - spi_bias *= qam_tab[p->constellation]; - spi_bias /= p->code_rate_HP + 1; - spi_bias /= (guard_tab[p->guard_interval] + 32); - spi_bias *= 1000ULL; - spi_bias /= 1000ULL + ppm/1000; - spi_bias *= p->code_rate_HP; - - val0x04 = (p->transmission_mode << 2) | p->guard_interval; - val0x05 = fec_tab[p->code_rate_HP]; - - if (p->hierarchy_information != HIERARCHY_NONE) - val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; - - val0x06 = (p->hierarchy_information << 2) | p->constellation; - - l64781_writereg (i2c, 0x04, val0x04); - l64781_writereg (i2c, 0x05, val0x05); - l64781_writereg (i2c, 0x06, val0x06); - - reset_afc (i2c); - - /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ - l64781_writereg (i2c, 0x15, - p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); - l64781_writereg (i2c, 0x16, init_freq & 0xff); - l64781_writereg (i2c, 0x17, (init_freq >> 8) & 0xff); - l64781_writereg (i2c, 0x18, (init_freq >> 16) & 0xff); - - l64781_writereg (i2c, 0x1b, spi_bias & 0xff); - l64781_writereg (i2c, 0x1c, (spi_bias >> 8) & 0xff); - l64781_writereg (i2c, 0x1d, ((spi_bias >> 16) & 0x7f) | - (param->inversion == INVERSION_ON ? 0x80 : 0x00)); - - l64781_writereg (i2c, 0x22, ddfs_offset_fixed & 0xff); - l64781_writereg (i2c, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); - - l64781_readreg (i2c, 0x00); /* clear interrupt registers... */ - l64781_readreg (i2c, 0x01); /* dto. */ - - apply_tps (i2c); - - return 0; -} - - -static int reset_and_configure (struct i2c_adapter *i2c) -{ - u8 buf [] = { 0x06 }; - struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; - - return (i2c_transfer(i2c, &msg, 1) == 1) ? 0 : -ENODEV; -} - - -static int get_frontend(struct i2c_adapter* i2c, struct dvb_frontend_parameters* param) -{ - int tmp; - - - tmp = l64781_readreg(i2c, 0x04); - switch(tmp & 3) { - case 0: - param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; - break; - case 1: - param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; - break; - case 2: - param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; - break; - case 3: - param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; - break; - } - switch((tmp >> 2) & 3) { - case 0: - param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; - break; - case 1: - param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; - break; - default: - printk("Unexpected value for transmission_mode\n"); - } - - - - tmp = l64781_readreg(i2c, 0x05); - switch(tmp & 7) { - case 0: - param->u.ofdm.code_rate_HP = FEC_1_2; - break; - case 1: - param->u.ofdm.code_rate_HP = FEC_2_3; - break; - case 2: - param->u.ofdm.code_rate_HP = FEC_3_4; - break; - case 3: - param->u.ofdm.code_rate_HP = FEC_5_6; - break; - case 4: - param->u.ofdm.code_rate_HP = FEC_7_8; - break; - default: - printk("Unexpected value for code_rate_HP\n"); - } - switch((tmp >> 3) & 7) { - case 0: - param->u.ofdm.code_rate_LP = FEC_1_2; - break; - case 1: - param->u.ofdm.code_rate_LP = FEC_2_3; - break; - case 2: - param->u.ofdm.code_rate_LP = FEC_3_4; - break; - case 3: - param->u.ofdm.code_rate_LP = FEC_5_6; - break; - case 4: - param->u.ofdm.code_rate_LP = FEC_7_8; - break; - default: - printk("Unexpected value for code_rate_LP\n"); - } - - - tmp = l64781_readreg(i2c, 0x06); - switch(tmp & 3) { - case 0: - param->u.ofdm.constellation = QPSK; - break; - case 1: - param->u.ofdm.constellation = QAM_16; - break; - case 2: - param->u.ofdm.constellation = QAM_64; - break; - default: - printk("Unexpected value for constellation\n"); - } - switch((tmp >> 2) & 7) { - case 0: - param->u.ofdm.hierarchy_information = HIERARCHY_NONE; - break; - case 1: - param->u.ofdm.hierarchy_information = HIERARCHY_1; - break; - case 2: - param->u.ofdm.hierarchy_information = HIERARCHY_2; - break; - case 3: - param->u.ofdm.hierarchy_information = HIERARCHY_4; - break; - default: - printk("Unexpected value for hierarchy\n"); - } - - - tmp = l64781_readreg (i2c, 0x1d); - param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; - - tmp = (int) (l64781_readreg (i2c, 0x08) | - (l64781_readreg (i2c, 0x09) << 8) | - (l64781_readreg (i2c, 0x0a) << 16)); - param->frequency += tmp; - - return 0; -} - - -static int init (struct i2c_adapter *i2c) -{ - reset_and_configure (i2c); - - /* Power up */ - l64781_writereg (i2c, 0x3e, 0xa5); - - /* Reset hard */ - l64781_writereg (i2c, 0x2a, 0x04); - l64781_writereg (i2c, 0x2a, 0x00); - - /* Set tuner specific things */ - /* AFC_POL, set also in reset_afc */ - l64781_writereg (i2c, 0x07, 0x8e); - - /* Use internal ADC */ - l64781_writereg (i2c, 0x0b, 0x81); - - /* AGC loop gain, and polarity is positive */ - l64781_writereg (i2c, 0x0c, 0x84); - - /* Internal ADC outputs two's complement */ - l64781_writereg (i2c, 0x0d, 0x8c); - - /* With ppm=8000, it seems the DTR_SENSITIVITY will result in - value of 2 with all possible bandwidths and guard - intervals, which is the initial value anyway. */ - /*l64781_writereg (i2c, 0x19, 0x92);*/ - - /* Everything is two's complement, soft bit and CSI_OUT too */ - l64781_writereg (i2c, 0x1e, 0x09); - - return 0; -} - - -static -int l64781_ioctl (struct dvb_frontend *fe, - unsigned int cmd, void *arg) -{ - struct l64781_state* state = fe->data; - struct i2c_adapter *i2c = state->i2c; - int res; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &l64781_info, - sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; - int sync = l64781_readreg (i2c, 0x32); - int gain = l64781_readreg (i2c, 0x0e); - - l64781_readreg (i2c, 0x00); /* clear interrupt registers... */ - l64781_readreg (i2c, 0x01); /* dto. */ - - *status = 0; - - if (gain > 5) - *status |= FE_HAS_SIGNAL; - - if (sync & 0x02) /* VCXO locked, this criteria should be ok */ - *status |= FE_HAS_CARRIER; - - if (sync & 0x20) - *status |= FE_HAS_VITERBI; - - if (sync & 0x40) - *status |= FE_HAS_SYNC; - - if (sync == 0x7f) - *status |= FE_HAS_LOCK; - - break; - } - - case FE_READ_BER: - { - /* XXX FIXME: set up counting period (reg 0x26...0x28) - */ - u32 *ber = (u32 *) arg; - *ber = l64781_readreg (i2c, 0x39) - | (l64781_readreg (i2c, 0x3a) << 8); - break; - } - - case FE_READ_SIGNAL_STRENGTH: - { - u8 gain = l64781_readreg (i2c, 0x0e); - *(u16 *) arg = (gain << 8) | gain; - break; - } - - case FE_READ_SNR: - { - u16 *snr = (u16 *) arg; - u8 avg_quality = 0xff - l64781_readreg (i2c, 0x33); - *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ - break; - } - - case FE_READ_UNCORRECTED_BLOCKS: - { - u32 *ub = (u32 *) arg; - *ub = l64781_readreg (i2c, 0x37) - | (l64781_readreg (i2c, 0x38) << 8); - break; - } - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - tsa5060_set_tv_freq (i2c, p->frequency); - return apply_frontend_param (i2c, p); - } - - case FE_GET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - return get_frontend(i2c, p); - } - - case FE_SLEEP: - /* Power down */ - return l64781_writereg (i2c, 0x3e, 0x5a); - - case FE_INIT: - res = init (i2c); - if ((res == 0) && (state->first)) { - state->first = 0; - msleep(200); - } - return res; - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - fesettings->min_delay_ms = 200; - fesettings->step_size = 166667; - fesettings->max_drift = 166667*2; - return 0; - } - - default: - dprintk ("%s: unknown command !!!\n", __FUNCTION__); - return -EINVAL; - }; - - return 0; -} - -static int l64781_probe(struct i2c_adapter *i2c) -{ - u8 reg0x3e; - u8 b0 [] = { 0x1a }; - u8 b1 [] = { 0x00 }; - struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - - /** - * the L64781 won't show up before we send the reset_and_configure() - * broadcast. If nothing responds there is no L64781 on the bus... - */ - if (reset_and_configure(i2c) < 0) { - dprintk("No response to reset and configure broadcast...\n"); - return -ENODEV; - } - - /* The chip always responds to reads */ - if (i2c_transfer(i2c, msg, 2) != 2) { - dprintk("No response to read on I2C bus\n"); - return -ENODEV; - } - - /* Save current register contents for bailout */ - reg0x3e = l64781_readreg(i2c, 0x3e); - - /* Reading the POWER_DOWN register always returns 0 */ - if (reg0x3e != 0) { - dprintk("Device doesn't look like L64781\n"); - return -ENODEV; - } - - /* Turn the chip off */ - l64781_writereg (i2c, 0x3e, 0x5a); - - /* Responds to all reads with 0 */ - if (l64781_readreg(i2c, 0x1a) != 0) { - dprintk("Read 1 returned unexpcted value\n"); - goto out; - } - - /* Turn the chip on */ - l64781_writereg (i2c, 0x3e, 0xa5); - - /* Responds with register default value */ - if (l64781_readreg(i2c, 0x1a) != 0xa1) { - dprintk("Read 2 returned unexpcted value\n"); - goto out; - } - - return 0; -out: - l64781_writereg (i2c, 0x3e, reg0x3e); /* restore reg 0x3e */ - return -ENODEV; - } - -static struct i2c_client client_template; - -static int l64781_attach_adapter(struct i2c_adapter *adapter) -{ - struct l64781_state *state; - struct i2c_client *client; - int ret; - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if ((ret = l64781_probe(adapter))) - return ret; - - if ( !(state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct l64781_state)); - state->i2c = adapter; - state->first = 1; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0; //XXX - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(state); - kfree(client); - return ret; - } - - BUG_ON(!state->dvb); - - if ((ret = dvb_register_frontend(l64781_ioctl, state->dvb, state, - &l64781_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(state); - kfree(client); - return ret; - } - - return 0; -} - -static int l64781_detach_client(struct i2c_client *client) -{ - struct l64781_state *state = i2c_get_clientdata(client); - - dvb_unregister_frontend(l64781_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int l64781_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct l64781_state *data = i2c_get_clientdata(client); - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: { - data->dvb = arg; - break; - } - case FE_UNREGISTER: { - data->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_L64781, - .flags = I2C_DF_NOTIFY, - .attach_adapter = l64781_attach_adapter, - .detach_client = l64781_detach_client, - .command = l64781_command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_l64781 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_l64781 (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "l64781: driver deregistration failed\n"); -} - -module_init(init_l64781); -module_exit(exit_l64781); - -MODULE_DESCRIPTION("Grundig 29504-401 DVB-T Frontend (LSI L64781 Based)"); -MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/media/dvb/frontends/grundig_29504-491.c b/drivers/media/dvb/frontends/grundig_29504-491.c deleted file mode 100644 index 64a6c512f9d9..000000000000 --- a/drivers/media/dvb/frontends/grundig_29504-491.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend - - Copyright (C) 2001 Convergence Integrated Media GmbH - - written by Ralph Metzler <ralph@convergence.de> - - adoption to the new DVB frontend API and diagnostic ioctl's - by Holger Waechtler <holger@convergence.de> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/string.h> -#include <linux/slab.h> - -#include "dvb_frontend.h" - -#define FRONTEND_NAME "dvbfe_tda8083" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - - -struct tda8083_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - -static struct dvb_frontend_info tda8083_info = { - .name = "Grundig 29504-491, (TDA8083 based)", - .type = FE_QPSK, - .frequency_min = 950000, /* FIXME: guessed! */ - .frequency_max = 1400000, /* FIXME: guessed! */ - .frequency_stepsize = 125, /* kHz for QPSK frontends */ -/* .frequency_tolerance = ???,*/ - .symbol_rate_min = 1000000, /* FIXME: guessed! */ - .symbol_rate_max = 45000000, /* FIXME: guessed! */ -/* .symbol_rate_tolerance = ???,*/ - .notifier_delay = 0, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_MUTE_TS -}; - - - -static u8 tda8083_init_tab [] = { - 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, - 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, - 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, - 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, - 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - - -static int tda8083_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) -{ - int ret; - u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x68, .flags = 0, .buf = buf, .len = 2 }; - - ret = i2c_transfer(i2c, &msg, 1); - - if (ret != 1) - dprintk ("%s: writereg error (reg %02x, ret == %i)\n", - __FUNCTION__, reg, ret); - - return (ret != 1) ? -1 : 0; -} - - -static int tda8083_readregs (struct i2c_adapter *i2c, u8 reg1, u8 *b, u8 len) -{ - int ret; - struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = ®1, .len = 1 }, - { .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - dprintk ("%s: readreg error (reg %02x, ret == %i)\n", - __FUNCTION__, reg1, ret); - - return ret == 2 ? 0 : -1; -} - - -static inline u8 tda8083_readreg (struct i2c_adapter *i2c, u8 reg) -{ - u8 val; - - tda8083_readregs (i2c, reg, &val, 1); - - return val; -} - - -static int tsa5522_write (struct i2c_adapter *i2c, u8 data [4]) -{ - int ret; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 }; - - ret = i2c_transfer(i2c, &msg, 1); - - if (ret != 1) - dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int tsa5522_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u32 div = freq / 125; - u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x8e, 0x00 }; - - return tsa5522_write (i2c, buf); -} - - -static int tda8083_init (struct i2c_adapter *i2c) -{ - int i; - - dprintk("%s: init TDA8083\n", __FILE__); - - for (i=0; i<44; i++) - tda8083_writereg (i2c, i, tda8083_init_tab[i]); - - return 0; -} - - -static int tda8083_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion) -{ - /* XXX FIXME: implement other modes than FEC_AUTO */ - if (inversion == INVERSION_AUTO) - return 0; - - return -EINVAL; -} - - -static int tda8083_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) -{ - if (fec == FEC_AUTO) - return tda8083_writereg (i2c, 0x07, 0xff); - - if (fec >= FEC_1_2 && fec <= FEC_8_9) - return tda8083_writereg (i2c, 0x07, 1 << (FEC_8_9 - fec)); - - return -EINVAL; -} - - -static fe_code_rate_t tda8083_get_fec (struct i2c_adapter *i2c) -{ - u8 index; - static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, - FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; - - index = tda8083_readreg(i2c, 0x0e) & 0x07; - - return fec_tab [index]; -} - - -static int tda8083_set_symbolrate (struct i2c_adapter *i2c, u32 srate) -{ - u32 ratio; - u32 tmp; - u8 filter; - - if (srate > 32000000) - srate = 32000000; - if (srate < 500000) - srate = 500000; - - filter = 0; - if (srate < 24000000) - filter = 2; - if (srate < 16000000) - filter = 3; - - tmp = 31250 << 16; - ratio = tmp / srate; - - tmp = (tmp % srate) << 8; - ratio = (ratio << 8) + tmp / srate; - - tmp = (tmp % srate) << 8; - ratio = (ratio << 8) + tmp / srate; - - dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); - - tda8083_writereg (i2c, 0x05, filter); - tda8083_writereg (i2c, 0x02, (ratio >> 16) & 0xff); - tda8083_writereg (i2c, 0x03, (ratio >> 8) & 0xff); - tda8083_writereg (i2c, 0x04, (ratio ) & 0xff); - - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - - return 1; -} - - -static void tda8083_wait_diseqc_fifo (struct i2c_adapter *i2c, int timeout) -{ - unsigned long start = jiffies; - - while (jiffies - start < timeout && - !(tda8083_readreg(i2c, 0x02) & 0x80)) - { - msleep(50); - }; -} - - -static int tda8083_send_diseqc_msg (struct i2c_adapter *i2c, - struct dvb_diseqc_master_cmd *m) -{ - int i; - - tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ - - for (i=0; i<m->msg_len; i++) - tda8083_writereg (i2c, 0x23 + i, m->msg[i]); - - tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ - - tda8083_wait_diseqc_fifo (i2c, 100); - - return 0; -} - - -static int tda8083_send_diseqc_burst (struct i2c_adapter *i2c, fe_sec_mini_cmd_t burst) -{ - switch (burst) { - case SEC_MINI_A: - tda8083_writereg (i2c, 0x29, (5 << 2)); /* send burst A */ - break; - case SEC_MINI_B: - tda8083_writereg (i2c, 0x29, (7 << 2)); /* send B */ - break; - default: - return -EINVAL; - }; - - tda8083_wait_diseqc_fifo (i2c, 100); - - return 0; -} - - -static int tda8083_set_tone (struct i2c_adapter *i2c, fe_sec_tone_mode_t tone) -{ - tda8083_writereg (i2c, 0x26, 0xf1); - - switch (tone) { - case SEC_TONE_OFF: - return tda8083_writereg (i2c, 0x29, 0x00); - case SEC_TONE_ON: - return tda8083_writereg (i2c, 0x29, 0x80); - default: - return -EINVAL; - }; -} - - -static int tda8083_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage) -{ - switch (voltage) { - case SEC_VOLTAGE_13: - return tda8083_writereg (i2c, 0x20, 0x00); - case SEC_VOLTAGE_18: - return tda8083_writereg (i2c, 0x20, 0x11); - default: - return -EINVAL; - }; -} - - -static int tda8083_ioctl(struct dvb_frontend *fe, unsigned int cmd, - void *arg) -{ - struct tda8083_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &tda8083_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status=(fe_status_t *) arg; - u8 signal = ~tda8083_readreg (i2c, 0x01); - u8 sync = tda8083_readreg (i2c, 0x02); - - *status = 0; - - if (signal > 10) - *status |= FE_HAS_SIGNAL; - - if (sync & 0x01) - *status |= FE_HAS_CARRIER; - - if (sync & 0x02) - *status |= FE_HAS_VITERBI; - - if (sync & 0x10) - *status |= FE_HAS_SYNC; - - if ((sync & 0x1f) == 0x1f) - *status |= FE_HAS_LOCK; - - break; - } - - case FE_READ_BER: - *((u32*) arg) = 0; /* XXX FIXME: implement me!!! */ - return -EOPNOTSUPP; - - case FE_READ_SIGNAL_STRENGTH: - { - u8 signal = ~tda8083_readreg (i2c, 0x01); - *((u16*) arg) = (signal << 8) | signal; - break; - } - case FE_READ_SNR: - { - u8 snr = tda8083_readreg (i2c, 0x08); - *((u16*) arg) = (snr << 8) | snr; - break; - } - case FE_READ_UNCORRECTED_BLOCKS: - *((u32*) arg) = 0; /* XXX FIXME: implement me!!! */ - return -EOPNOTSUPP; - - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - tsa5522_set_tv_freq (i2c, p->frequency); - tda8083_set_inversion (i2c, p->inversion); - tda8083_set_fec (i2c, p->u.qpsk.fec_inner); - tda8083_set_symbolrate (i2c, p->u.qpsk.symbol_rate); - - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - - break; - } - - case FE_GET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - - /* FIXME: get symbolrate & frequency offset...*/ - /*p->frequency = ???;*/ - p->inversion = (tda8083_readreg (i2c, 0x0e) & 0x80) ? - INVERSION_ON : INVERSION_OFF; - p->u.qpsk.fec_inner = tda8083_get_fec (i2c); - /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (i2c);*/ - break; - } - - case FE_SLEEP: - tda8083_writereg (i2c, 0x00, 0x02); - break; - - case FE_INIT: - tda8083_init (i2c); - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - break; - - case FE_DISEQC_SEND_MASTER_CMD: - return tda8083_send_diseqc_msg (i2c, arg); - - case FE_DISEQC_SEND_BURST: - tda8083_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg); - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - - break; - - case FE_SET_TONE: - tda8083_set_tone (i2c, (fe_sec_tone_mode_t) arg); - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - break; - - case FE_SET_VOLTAGE: - tda8083_set_voltage (i2c, (fe_sec_voltage_t) arg); - tda8083_writereg (i2c, 0x00, 0x3c); - tda8083_writereg (i2c, 0x00, 0x04); - break; - - default: - return -EOPNOTSUPP; - }; - - return 0; -} - -static struct i2c_client client_template; - -static int tda8083_attach_adapter(struct i2c_adapter *adapter) -{ - struct tda8083_state *state; - struct i2c_client *client; - int ret; - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if ((tda8083_readreg (adapter, 0x00)) != 0x05) - return -ENODEV; - - if ( !(state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL)) ) - return -ENOMEM; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; - } - - memset(state, 0, sizeof(struct tda8083_state)); - state->i2c = adapter; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0; //XXX - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(state); - kfree(client); - return ret; -} - - BUG_ON(!state->dvb); - - if ((ret = dvb_register_frontend(tda8083_ioctl, state->dvb, state, - &tda8083_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(state); - kfree(client); - return ret; - } - - return 0; -} - -static int tda8083_detach_client(struct i2c_client *client) -{ - struct tda8083_state *state = i2c_get_clientdata(client); - - dvb_unregister_frontend (tda8083_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int tda8083_command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct tda8083_state *data = i2c_get_clientdata(client); - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: { - data->dvb = arg; - break; - } - case FE_UNREGISTER: { - data->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_TDA8083, - .flags = I2C_DF_NOTIFY, - .attach_adapter = tda8083_attach_adapter, - .detach_client = tda8083_detach_client, - .command = tda8083_command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_tda8083 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_tda8083 (void) -{ - if (i2c_del_driver(&driver)) - printk("grundig_29504_401: driver deregistration failed\n"); -} - -module_init(init_tda8083); -module_exit(exit_tda8083); - -MODULE_DESCRIPTION("Grundig 29504-491 DVB frontend driver (TDA8083 Based)"); -MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c new file mode 100644 index 000000000000..af4cb09b03a2 --- /dev/null +++ b/drivers/media/dvb/frontends/l64781.c @@ -0,0 +1,607 @@ +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler <holger@convergence.de> + for Convergence Integrated Media GmbH + Marko Kohtala <marko.kohtala@nokia.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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "dvb_frontend.h" +#include "l64781.h" + + +struct l64781_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct l64781_config* config; + + struct dvb_frontend frontend; + + /* private demodulator data */ + int first:1; +}; + +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "l64781: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + + +static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", + __FUNCTION__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + +static int l64781_readreg (struct l64781_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) return ret; + + return b1[0]; +} + +static void apply_tps (struct l64781_state* state) +{ + l64781_writereg (state, 0x2a, 0x00); + l64781_writereg (state, 0x2a, 0x01); + + /* This here is a little bit questionable because it enables + the automatic update of TPS registers. I think we'd need to + handle the IRQ from FE to update some other registers as + well, or at least implement some magic to tuning to correct + to the TPS received from transmission. */ + l64781_writereg (state, 0x2a, 0x02); +} + + +static void reset_afc (struct l64781_state* state) +{ + /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for + timing offset */ + l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ + l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ + l64781_writereg (state, 0x09, 0); + l64781_writereg (state, 0x0a, 0); + l64781_writereg (state, 0x07, 0x8e); + l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ + l64781_writereg (state, 0x11, 0x80); /* stall TIM */ + l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ + l64781_writereg (state, 0x12, 0); + l64781_writereg (state, 0x13, 0); + l64781_writereg (state, 0x11, 0x00); +} + +static int reset_and_configure (struct l64781_state* state) +{ + u8 buf [] = { 0x06 }; + struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; + // NOTE: this is correct in writing to address 0x00 + + return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; +} + +static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ + static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; + /* QPSK, QAM_16, QAM_64 */ + static const u8 qam_tab [] = { 2, 4, 0, 6 }; + static const u8 bw_tab [] = { 8, 7, 6 }; /* 8Mhz, 7MHz, 6MHz */ + static const u8 guard_tab [] = { 1, 2, 4, 8 }; + /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ + static const u32 ppm = 8000; + struct dvb_ofdm_parameters *p = ¶m->u.ofdm; + u32 ddfs_offset_fixed; +/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ +/* bw_tab[p->bandWidth]<<10)/15625; */ + u32 init_freq; + u32 spi_bias; + u8 val0x04; + u8 val0x05; + u8 val0x06; + int bw = p->bandwidth - BANDWIDTH_8_MHZ; + + state->config->pll_set(fe, param); + + if (param->inversion != INVERSION_ON && + param->inversion != INVERSION_OFF) + return -EINVAL; + + if (bw < 0 || bw > 2) + return -EINVAL; + + if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && + p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && + p->code_rate_HP != FEC_7_8) + return -EINVAL; + + if (p->hierarchy_information != HIERARCHY_NONE && + (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && + p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && + p->code_rate_LP != FEC_7_8)) + return -EINVAL; + + if (p->constellation != QPSK && p->constellation != QAM_16 && + p->constellation != QAM_64) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if (p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if (p->hierarchy_information < HIERARCHY_NONE || + p->hierarchy_information > HIERARCHY_4) + return -EINVAL; + + ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000; + + /* This works up to 20000 ppm, it overflows if too large ppm! */ + init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / + bw_tab[p->bandwidth] & 0xFFFFFF); + + /* SPI bias calculation is slightly modified to fit in 32bit */ + /* will work for high ppm only... */ + spi_bias = 378 * (1 << 10); + spi_bias *= 16; + spi_bias *= bw_tab[p->bandwidth]; + spi_bias *= qam_tab[p->constellation]; + spi_bias /= p->code_rate_HP + 1; + spi_bias /= (guard_tab[p->guard_interval] + 32); + spi_bias *= 1000ULL; + spi_bias /= 1000ULL + ppm/1000; + spi_bias *= p->code_rate_HP; + + val0x04 = (p->transmission_mode << 2) | p->guard_interval; + val0x05 = fec_tab[p->code_rate_HP]; + + if (p->hierarchy_information != HIERARCHY_NONE) + val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; + + val0x06 = (p->hierarchy_information << 2) | p->constellation; + + l64781_writereg (state, 0x04, val0x04); + l64781_writereg (state, 0x05, val0x05); + l64781_writereg (state, 0x06, val0x06); + + reset_afc (state); + + /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ + l64781_writereg (state, 0x15, + p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); + l64781_writereg (state, 0x16, init_freq & 0xff); + l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); + l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); + + l64781_writereg (state, 0x1b, spi_bias & 0xff); + l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); + l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | + (param->inversion == INVERSION_ON ? 0x80 : 0x00)); + + l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); + l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + apply_tps (state); + + return 0; +} + +static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + int tmp; + + + tmp = l64781_readreg(state, 0x04); + switch(tmp & 3) { + case 0: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + break; + } + switch((tmp >> 2) & 3) { + case 0: + param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + printk("Unexpected value for transmission_mode\n"); + } + + + + tmp = l64781_readreg(state, 0x05); + switch(tmp & 7) { + case 0: + param->u.ofdm.code_rate_HP = FEC_1_2; + break; + case 1: + param->u.ofdm.code_rate_HP = FEC_2_3; + break; + case 2: + param->u.ofdm.code_rate_HP = FEC_3_4; + break; + case 3: + param->u.ofdm.code_rate_HP = FEC_5_6; + break; + case 4: + param->u.ofdm.code_rate_HP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_HP\n"); + } + switch((tmp >> 3) & 7) { + case 0: + param->u.ofdm.code_rate_LP = FEC_1_2; + break; + case 1: + param->u.ofdm.code_rate_LP = FEC_2_3; + break; + case 2: + param->u.ofdm.code_rate_LP = FEC_3_4; + break; + case 3: + param->u.ofdm.code_rate_LP = FEC_5_6; + break; + case 4: + param->u.ofdm.code_rate_LP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_LP\n"); + } + + + tmp = l64781_readreg(state, 0x06); + switch(tmp & 3) { + case 0: + param->u.ofdm.constellation = QPSK; + break; + case 1: + param->u.ofdm.constellation = QAM_16; + break; + case 2: + param->u.ofdm.constellation = QAM_64; + break; + default: + printk("Unexpected value for constellation\n"); + } + switch((tmp >> 2) & 7) { + case 0: + param->u.ofdm.hierarchy_information = HIERARCHY_NONE; + break; + case 1: + param->u.ofdm.hierarchy_information = HIERARCHY_1; + break; + case 2: + param->u.ofdm.hierarchy_information = HIERARCHY_2; + break; + case 3: + param->u.ofdm.hierarchy_information = HIERARCHY_4; + break; + default: + printk("Unexpected value for hierarchy\n"); + } + + + tmp = l64781_readreg (state, 0x1d); + param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; + + tmp = (int) (l64781_readreg (state, 0x08) | + (l64781_readreg (state, 0x09) << 8) | + (l64781_readreg (state, 0x0a) << 16)); + param->frequency += tmp; + + return 0; +} + +static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + int sync = l64781_readreg (state, 0x32); + int gain = l64781_readreg (state, 0x0e); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + *status = 0; + + if (gain > 5) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x02) /* VCXO locked, this criteria should be ok */ + *status |= FE_HAS_CARRIER; + + if (sync & 0x20) + *status |= FE_HAS_VITERBI; + + if (sync & 0x40) + *status |= FE_HAS_SYNC; + + if (sync == 0x7f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int l64781_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + /* XXX FIXME: set up counting period (reg 0x26...0x28) + */ + *ber = l64781_readreg (state, 0x39) + | (l64781_readreg (state, 0x3a) << 8); + + return 0; +} + +static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + u8 gain = l64781_readreg (state, 0x0e); + *signal_strength = (gain << 8) | gain; + + return 0; +} + +static int l64781_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + u8 avg_quality = 0xff - l64781_readreg (state, 0x33); + *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ + + return 0; +} + +static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + *ucblocks = l64781_readreg (state, 0x37) + | (l64781_readreg (state, 0x38) << 8); + + return 0; +} + +static int l64781_sleep(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + /* Power down */ + return l64781_writereg (state, 0x3e, 0x5a); +} + +static int l64781_init(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + reset_and_configure (state); + + /* Power up */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Reset hard */ + l64781_writereg (state, 0x2a, 0x04); + l64781_writereg (state, 0x2a, 0x00); + + /* Set tuner specific things */ + /* AFC_POL, set also in reset_afc */ + l64781_writereg (state, 0x07, 0x8e); + + /* Use internal ADC */ + l64781_writereg (state, 0x0b, 0x81); + + /* AGC loop gain, and polarity is positive */ + l64781_writereg (state, 0x0c, 0x84); + + /* Internal ADC outputs two's complement */ + l64781_writereg (state, 0x0d, 0x8c); + + /* With ppm=8000, it seems the DTR_SENSITIVITY will result in + value of 2 with all possible bandwidths and guard + intervals, which is the initial value anyway. */ + /*l64781_writereg (state, 0x19, 0x92);*/ + + /* Everything is two's complement, soft bit and CSI_OUT too */ + l64781_writereg (state, 0x1e, 0x09); + + if (state->config->pll_init) state->config->pll_init(fe); + + /* delay a bit after first init attempt */ + if (state->first) { + state->first = 0; + msleep(200); + } + + return 0; +} + +static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 200; + fesettings->step_size = 166667; + fesettings->max_drift = 166667*2; + return 0; +} + +static void l64781_release(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops l64781_ops; + +struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c) +{ + struct l64781_state* state = NULL; + int reg0x3e = -1; + u8 b0 [] = { 0x1a }; + u8 b1 [] = { 0x00 }; + struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + /* allocate memory for the internal state */ + state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); + state->first = 1; + + /** + * the L64781 won't show up before we send the reset_and_configure() + * broadcast. If nothing responds there is no L64781 on the bus... + */ + if (reset_and_configure(state) < 0) { + dprintk("No response to reset and configure broadcast...\n"); + goto error; + } + + /* The chip always responds to reads */ + if (i2c_transfer(state->i2c, msg, 2) != 2) { + dprintk("No response to read on I2C bus\n"); + goto error; + } + + /* Save current register contents for bailout */ + reg0x3e = l64781_readreg(state, 0x3e); + + /* Reading the POWER_DOWN register always returns 0 */ + if (reg0x3e != 0) { + dprintk("Device doesn't look like L64781\n"); + goto error; + } + + /* Turn the chip off */ + l64781_writereg (state, 0x3e, 0x5a); + + /* Responds to all reads with 0 */ + if (l64781_readreg(state, 0x1a) != 0) { + dprintk("Read 1 returned unexpcted value\n"); + goto error; + } + + /* Turn the chip on */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Responds with register default value */ + if (l64781_readreg(state, 0x1a) != 0xa1) { + dprintk("Read 2 returned unexpcted value\n"); + goto error; + } + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops l64781_ops = { + + .info = { + .name = "LSI L64781 DVB-T", + .type = FE_OFDM, + /* .frequency_min = ???,*/ + /* .frequency_max = ???,*/ + .frequency_stepsize = 166666, + /* .frequency_tolerance = ???,*/ + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_MUTE_TS + }, + + .release = l64781_release, + + .init = l64781_init, + .sleep = l64781_sleep, + + .set_frontend = apply_frontend_param, + .get_frontend = get_frontend, + .get_tune_settings = l64781_get_tune_settings, + + .read_status = l64781_read_status, + .read_ber = l64781_read_ber, + .read_signal_strength = l64781_read_signal_strength, + .read_snr = l64781_read_snr, + .read_ucblocks = l64781_read_ucblocks, +}; + +MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(l64781_attach); diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h new file mode 100644 index 000000000000..259dfffcc867 --- /dev/null +++ b/drivers/media/dvb/frontends/l64781.h @@ -0,0 +1,43 @@ +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler <holger@convergence.de> + for Convergence Integrated Media GmbH + Marko Kohtala <marko.kohtala@nokia.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 L64781_H +#define L64781_H + +#include <linux/dvb/frontend.h> + +struct l64781_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + + +extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c); + +#endif // L64781_H diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c index db003e12d908..a5dcc8766313 100644 --- a/drivers/media/dvb/frontends/mt312.c +++ b/drivers/media/dvb/frontends/mt312.c @@ -1,5 +1,5 @@ /* - Driver for Zarlink MT312 Satellite Channel Decoder + Driver for Zarlink VP310/MT312 Satellite Channel Decoder Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> @@ -31,71 +31,52 @@ #include <linux/moduleparam.h> #include "dvb_frontend.h" +#include "mt312_priv.h" #include "mt312.h" -#define FRONTEND_NAME "dvbfe_mt312" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct mt312_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct mt312_config* config; + struct dvb_frontend frontend; -#define I2C_ADDR_MT312 0x0e -#define I2C_ADDR_SL1935 0x61 -#define I2C_ADDR_TSA5059 0x61 + u8 id; + u8 frequency; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "mt312: " args); \ + } while (0) #define MT312_SYS_CLK 90000000UL /* 90 MHz */ #define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */ #define MT312_PLL_CLK 10000000UL /* 10 MHz */ -static struct dvb_frontend_info mt312_info = { - .name = "Zarlink MT312", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, - /*.frequency_tolerance = 29500, FIXME: binary compatibility waste? */ - .symbol_rate_min = MT312_SYS_CLK / 128, - .symbol_rate_max = MT312_SYS_CLK / 2, - /*.symbol_rate_tolerance = 500, FIXME: binary compatibility waste? 2% */ - .notifier_delay = 0, - .caps = - FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | - FE_CAN_RECOVER -}; - -struct mt312_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - int id; -}; - -static int mt312_read(struct i2c_adapter *i2c, - const enum mt312_reg_addr reg, void *buf, - const size_t count) +static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg, + void *buf, const size_t count) { int ret; struct i2c_msg msg[2]; u8 regbuf[1] = { reg }; - msg[0].addr = I2C_ADDR_MT312; + msg[0].addr = state->config->demod_address; msg[0].flags = 0; msg[0].buf = regbuf; msg[0].len = 1; - msg[1].addr = I2C_ADDR_MT312; + msg[1].addr = state->config->demod_address; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = count; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { printk(KERN_ERR "%s: ret == %d\n", __FUNCTION__, ret); @@ -113,9 +94,8 @@ static int mt312_read(struct i2c_adapter *i2c, return 0; } -static int mt312_write(struct i2c_adapter *i2c, - const enum mt312_reg_addr reg, const void *src, - const size_t count) +static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg, + const void *src, const size_t count) { int ret; u8 buf[count + 1]; @@ -132,12 +112,12 @@ static int mt312_write(struct i2c_adapter *i2c, buf[0] = reg; memcpy(&buf[1], src, count); - msg.addr = I2C_ADDR_MT312; + msg.addr = state->config->demod_address; msg.flags = 0; msg.buf = buf; msg.len = count + 1; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) { dprintk("%s: ret == %d\n", __FUNCTION__, ret); @@ -147,123 +127,136 @@ static int mt312_write(struct i2c_adapter *i2c, return 0; } -static inline int mt312_readreg(struct i2c_adapter *i2c, +static inline int mt312_readreg(struct mt312_state* state, const enum mt312_reg_addr reg, u8 * val) { - return mt312_read(i2c, reg, val, 1); + return mt312_read(state, reg, val, 1); } -static inline int mt312_writereg(struct i2c_adapter *i2c, +static inline int mt312_writereg(struct mt312_state* state, const enum mt312_reg_addr reg, const u8 val) { - return mt312_write(i2c, reg, &val, 1); + return mt312_write(state, reg, &val, 1); } -static int mt312_pll_write(struct i2c_adapter *i2c, const u8 addr, - u8 * buf, const u8 len) +static inline u32 mt312_div(u32 a, u32 b) +{ + return (a + (b / 2)) / b; +} + +static int mt312_reset(struct mt312_state* state, const u8 full) +{ + return mt312_writereg(state, RESET, full ? 0x80 : 0x40); +} + +static int mt312_get_inversion(struct mt312_state* state, + fe_spectral_inversion_t *i) { int ret; - struct i2c_msg msg; + u8 vit_mode; - msg.addr = addr; - msg.flags = 0; - msg.buf = buf; - msg.len = len; + if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0) + return ret; + + if (vit_mode & 0x80) /* auto inversion was used */ + *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; + + return 0; +} + +static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr) +{ + int ret; + u8 sym_rate_h; + u8 dec_ratio; + u16 sym_rat_op; + u16 monitor; + u8 buf[2]; - if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x40)) < 0) + if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0) return ret; - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) - printk(KERN_ERR "%s: i/o error (ret == %d)\n", __FUNCTION__, ret); + if (sym_rate_h & 0x80) { /* symbol rate search was used */ + if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0) + return ret; - if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x00)) < 0) + if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0) return ret; - return 0; + monitor = (buf[0] << 8) | buf[1]; + + dprintk(KERN_DEBUG "sr(auto) = %u\n", + mt312_div(monitor * 15625, 4)); + } else { + if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0) + return ret; + + if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0) + return ret; + + dec_ratio = ((buf[0] >> 5) & 0x07) * 32; + + if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0) + return ret; + + sym_rat_op = (buf[0] << 8) | buf[1]; + + dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n", + sym_rat_op, dec_ratio); + dprintk(KERN_DEBUG "*sr(manual) = %lu\n", + (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) * + 2) - dec_ratio); } -static inline u32 mt312_div(u32 a, u32 b) -{ - return (a + (b / 2)) / b; + return 0; } -static int sl1935_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr) +static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr) { - /* 155 uA, Baseband Path B */ - u8 buf[4] = { 0x00, 0x00, 0x80, 0x00 }; - - u8 exp; - u32 ref; - u32 div; - - if (sr < 10000000) { /* 1-10 MSym/s: ratio 2 ^ 3 */ - exp = 3; - buf[2] |= 0x40; /* 690 uA */ - } else if (sr < 15000000) { /* 10-15 MSym/s: ratio 2 ^ 4 */ - exp = 4; - buf[2] |= 0x20; /* 330 uA */ - } else { /* 15-45 MSym/s: ratio 2 ^ 7 */ - exp = 7; - buf[3] |= 0x08; /* Baseband Path A */ + const fe_code_rate_t fec_tab[8] = + { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, + FEC_AUTO, FEC_AUTO }; + + int ret; + u8 fec_status; + + if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0) + return ret; + + *cr = fec_tab[(fec_status >> 4) & 0x07]; + + return 0; } - div = mt312_div(MT312_PLL_CLK, 1 << exp); - ref = mt312_div(freq * 1000, div); - mt312_info.frequency_stepsize = mt312_div(div, 1000); - buf[0] = (ref >> 8) & 0x7f; - buf[1] = (ref >> 0) & 0xff; - buf[2] |= (exp - 1); - if (freq < 1550000) - buf[3] |= 0x10; - dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0], - buf[1], buf[2], buf[3]); - return mt312_pll_write(i2c, I2C_ADDR_SL1935, buf, sizeof(buf)); -} -static int tsa5059_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr) -{ - u8 buf[4]; - u32 ref = mt312_div(freq, 125); - buf[0] = (ref >> 8) & 0x7f; - buf[1] = (ref >> 0) & 0xff; - buf[2] = 0x84 | ((ref >> 10) & 0x60); - buf[3] = 0x80; - if (freq < 1550000) - buf[3] |= 0x02; - dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0], - buf[1], buf[2], buf[3]); - return mt312_pll_write(i2c, I2C_ADDR_TSA5059, buf, sizeof(buf)); -} -static int mt312_reset(struct i2c_adapter *i2c, const u8 full) -{ - return mt312_writereg(i2c, RESET, full ? 0x80 : 0x40); -} -static int mt312_initfe(struct mt312_state *state, u8 pll) + + +static int mt312_initfe(struct dvb_frontend* fe) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; /* wake up */ - if ((ret = mt312_writereg(i2c, CONFIG, (pll == 60 ? 0x88 : 0x8c))) < 0) + if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0) return ret; /* wait at least 150 usec */ udelay(150); /* full reset */ - if ((ret = mt312_reset(i2c, 1)) < 0) + if ((ret = mt312_reset(state, 1)) < 0) return ret; // Per datasheet, write correct values. 09/28/03 ACCJr. @@ -271,56 +264,63 @@ static int mt312_initfe(struct mt312_state *state, u8 pll) { u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00}; - if ((ret = mt312_write(i2c, VIT_SETUP, buf_def, sizeof(buf_def))) < 0) + if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0) return ret; } /* SYS_CLK */ - buf[0] = mt312_div((pll == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000); + buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000); /* DISEQC_RATIO */ buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4); - if ((ret = mt312_write(i2c, SYS_CLK, buf, sizeof(buf))) < 0) + if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0) return ret; - if ((ret = mt312_writereg(i2c, SNR_THS_HIGH, 0x32)) < 0) + if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0) return ret; - if ((ret = mt312_writereg(i2c, OP_CTRL, 0x53)) < 0) + if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0) return ret; /* TS_SW_LIM */ buf[0] = 0x8c; buf[1] = 0x98; - if ((ret = mt312_write(i2c, TS_SW_LIM_L, buf, sizeof(buf))) < 0) + if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0) return ret; - if ((ret = mt312_writereg(i2c, CS_SW_LIM, 0x69)) < 0) + if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0) return ret; + if (state->config->pll_init) { + mt312_writereg(state, GPP_CTRL, 0x40); + state->config->pll_init(fe); + mt312_writereg(state, GPP_CTRL, 0x00); + } + return 0; } -static int mt312_send_master_cmd(struct i2c_adapter *i2c, - const struct dvb_diseqc_master_cmd *c) +static int mt312_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *c) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 diseqc_mode; if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_write(i2c, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0) + mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3) | 0x04)) < 0) return ret; @@ -328,21 +328,15 @@ static int mt312_send_master_cmd(struct i2c_adapter *i2c, /* set DISEQC_MODE[2:0] to zero if a return message is expected */ if (c->msg[0] & 0x02) if ((ret = - mt312_writereg(i2c, DISEQC_MODE, (diseqc_mode & 0x40))) < 0) + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0) return ret; return 0; } -static int mt312_recv_slave_reply(struct i2c_adapter *i2c, - struct dvb_diseqc_slave_reply *r) -{ - /* TODO */ - return -EOPNOTSUPP; -} - -static int mt312_send_burst(struct i2c_adapter *i2c, const fe_sec_mini_cmd_t c) +static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 mini_tab[2] = { 0x02, 0x03 }; int ret; @@ -351,19 +345,20 @@ static int mt312_send_burst(struct i2c_adapter *i2c, const fe_sec_mini_cmd_t c) if (c > SEC_MINI_B) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | mini_tab[c])) < 0) return ret; return 0; } -static int mt312_set_tone(struct i2c_adapter *i2c, const fe_sec_tone_mode_t t) +static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 tone_tab[2] = { 0x01, 0x00 }; int ret; @@ -372,36 +367,37 @@ static int mt312_set_tone(struct i2c_adapter *i2c, const fe_sec_tone_mode_t t) if (t > SEC_TONE_OFF) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | tone_tab[t])) < 0) return ret; return 0; } -static int mt312_set_voltage(struct i2c_adapter *i2c, const fe_sec_voltage_t v) +static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; if (v > SEC_VOLTAGE_OFF) return -EINVAL; - return mt312_writereg(i2c, DISEQC_MODE, volt_tab[v]); + return mt312_writereg(state, DISEQC_MODE, volt_tab[v]); } -static int mt312_read_status(struct mt312_state *state, fe_status_t *s) +static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; - u8 status[3], vit_mode; + u8 status[3]; *s = 0; - if ((ret = mt312_read(i2c, QPSK_STAT_H, status, sizeof(status))) < 0) + if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0) return ret; dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]); @@ -417,26 +413,16 @@ static int mt312_read_status(struct mt312_state *state, fe_status_t *s) if (status[0] & 0x01) *s |= FE_HAS_LOCK; /* qpsk lock */ - // VP310 doesn't have AUTO, so we "implement it here" ACCJr - if ((state->id == ID_VP310) && !(status[0] & 0x01)) { - if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0) - return ret; - vit_mode ^= 0x40; - if ((ret = mt312_writereg(i2c, VIT_MODE, vit_mode)) < 0) - return ret; - if ((ret = mt312_writereg(i2c, GO, 0x01)) < 0) - return ret; - } - return 0; } -static int mt312_read_bercnt(struct i2c_adapter *i2c, u32 *ber) +static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[3]; - if ((ret = mt312_read(i2c, RS_BERCNT_H, buf, 3)) < 0) + if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0) return ret; *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64; @@ -444,14 +430,15 @@ static int mt312_read_bercnt(struct i2c_adapter *i2c, u32 *ber) return 0; } -static int mt312_read_agc(struct i2c_adapter *i2c, u16 *signal_strength) +static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[3]; u16 agc; s16 err_db; - if ((ret = mt312_read(i2c, AGC_H, buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0) return ret; agc = (buf[0] << 6) | (buf[1] >> 2); @@ -464,12 +451,13 @@ static int mt312_read_agc(struct i2c_adapter *i2c, u16 *signal_strength) return 0; } -static int mt312_read_snr(struct i2c_adapter *i2c, u16 *snr) +static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; - if ((ret = mt312_read(i2c, M_SNR_H, &buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0) return ret; *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1); @@ -477,12 +465,13 @@ static int mt312_read_snr(struct i2c_adapter *i2c, u16 *snr) return 0; } -static int mt312_read_ubc(struct i2c_adapter *i2c, u32 *ubc) +static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; - if ((ret = mt312_read(i2c, RS_UBC_H, &buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0) return ret; *ubc = (buf[0] << 8) | buf[1]; @@ -490,10 +479,10 @@ static int mt312_read_ubc(struct i2c_adapter *i2c, u32 *ubc) return 0; } -static int mt312_set_frontend(struct mt312_state *state, - const struct dvb_frontend_parameters *p) +static int mt312_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[5], config_val; u16 sr; @@ -502,20 +491,18 @@ static int mt312_set_frontend(struct mt312_state *state, { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f }; const u8 inv_tab[3] = { 0x00, 0x40, 0x80 }; - int (*set_tv_freq)(struct i2c_adapter *i2c, u32 freq, u32 sr); - dprintk("%s: Freq %d\n", __FUNCTION__, p->frequency); - if ((p->frequency < mt312_info.frequency_min) - || (p->frequency > mt312_info.frequency_max)) + if ((p->frequency < fe->ops->info.frequency_min) + || (p->frequency > fe->ops->info.frequency_max)) return -EINVAL; if ((p->inversion < INVERSION_OFF) - || (p->inversion > INVERSION_AUTO)) + || (p->inversion > INVERSION_ON)) return -EINVAL; - if ((p->u.qpsk.symbol_rate < mt312_info.symbol_rate_min) - || (p->u.qpsk.symbol_rate > mt312_info.symbol_rate_max)) + if ((p->u.qpsk.symbol_rate < fe->ops->info.symbol_rate_min) + || (p->u.qpsk.symbol_rate > fe->ops->info.symbol_rate_max)) return -EINVAL; if ((p->u.qpsk.fec_inner < FEC_NONE) @@ -530,31 +517,36 @@ static int mt312_set_frontend(struct mt312_state *state, case ID_VP310: // For now we will do this only for the VP310. // It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03 - if ((ret = mt312_readreg(i2c, CONFIG, &config_val) < 0)) + if ((ret = mt312_readreg(state, CONFIG, &config_val) < 0)) return ret; if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz { - if ((config_val & 0x0c) == 0x08) //We are running 60MHz - if ((ret = mt312_initfe(state, (u8) 90)) < 0) + if ((config_val & 0x0c) == 0x08) { //We are running 60MHz + state->frequency = 90; + if ((ret = mt312_initfe(fe)) < 0) return ret; } + } else { - if ((config_val & 0x0c) == 0x0C) //We are running 90MHz - if ((ret = mt312_initfe(state, (u8) 60)) < 0) + if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz + state->frequency = 60; + if ((ret = mt312_initfe(fe)) < 0) return ret; } - set_tv_freq = tsa5059_set_tv_freq; + } break; + case ID_MT312: - set_tv_freq = sl1935_set_tv_freq; break; + default: return -EINVAL; } - if ((ret = set_tv_freq(i2c, p->frequency, p->u.qpsk.symbol_rate)) < 0) - return ret; + mt312_writereg(state, GPP_CTRL, 0x40); + state->config->pll_set(fe, p); + mt312_writereg(state, GPP_CTRL, 0x00); /* sr = (u16)(sr * 256.0 / 1000000.0) */ sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625); @@ -575,333 +567,182 @@ static int mt312_set_frontend(struct mt312_state *state, /* GO */ buf[4] = 0x01; - if ((ret = mt312_write(i2c, SYM_RATE_H, buf, sizeof(buf))) < 0) + if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0) return ret; - mt312_reset(i2c, 0); + mt312_reset(state, 0); return 0; } -static int mt312_get_inversion(struct i2c_adapter *i2c, - fe_spectral_inversion_t * i) -{ - int ret; - u8 vit_mode; - - if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0) - return ret; - - if (vit_mode & 0x80) /* auto inversion was used */ - *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; - - return 0; -} - -static int mt312_get_symbol_rate(struct i2c_adapter *i2c, u32 *sr) -{ - int ret; - u8 sym_rate_h; - u8 dec_ratio; - u16 sym_rat_op; - u16 monitor; - u8 buf[2]; - - if ((ret = mt312_readreg(i2c, SYM_RATE_H, &sym_rate_h)) < 0) - return ret; - - if (sym_rate_h & 0x80) { /* symbol rate search was used */ - if ((ret = mt312_writereg(i2c, MON_CTRL, 0x03)) < 0) - return ret; - - if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0) - return ret; - - monitor = (buf[0] << 8) | buf[1]; - - dprintk(KERN_DEBUG "sr(auto) = %u\n", - mt312_div(monitor * 15625, 4)); - } else { - if ((ret = mt312_writereg(i2c, MON_CTRL, 0x05)) < 0) - return ret; - - if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0) - return ret; - - dec_ratio = ((buf[0] >> 5) & 0x07) * 32; - - if ((ret = mt312_read(i2c, SYM_RAT_OP_H, buf, sizeof(buf))) < 0) - return ret; - - sym_rat_op = (buf[0] << 8) | buf[1]; - - dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n", - sym_rat_op, dec_ratio); - dprintk(KERN_DEBUG "*sr(manual) = %lu\n", - (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) * - 2) - dec_ratio); - } - - return 0; -} - -static int mt312_get_code_rate(struct i2c_adapter *i2c, fe_code_rate_t *cr) -{ - const fe_code_rate_t fec_tab[8] = - { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, - FEC_AUTO, FEC_AUTO }; - - int ret; - u8 fec_status; - - if ((ret = mt312_readreg(i2c, FEC_STATUS, &fec_status)) < 0) - return ret; - - *cr = fec_tab[(fec_status >> 4) & 0x07]; - - return 0; -} - -static int mt312_get_frontend(struct i2c_adapter *i2c, +static int mt312_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; - if ((ret = mt312_get_inversion(i2c, &p->inversion)) < 0) + if ((ret = mt312_get_inversion(state, &p->inversion)) < 0) return ret; - if ((ret = mt312_get_symbol_rate(i2c, &p->u.qpsk.symbol_rate)) < 0) + if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0) return ret; - if ((ret = mt312_get_code_rate(i2c, &p->u.qpsk.fec_inner)) < 0) + if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0) return ret; return 0; } -static int mt312_sleep(struct i2c_adapter *i2c) +static int mt312_sleep(struct dvb_frontend* fe) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 config; /* reset all registers to defaults */ - if ((ret = mt312_reset(i2c, 1)) < 0) + if ((ret = mt312_reset(state, 1)) < 0) return ret; - if ((ret = mt312_readreg(i2c, CONFIG, &config)) < 0) + if ((ret = mt312_readreg(state, CONFIG, &config)) < 0) return ret; /* enter standby */ - if ((ret = mt312_writereg(i2c, CONFIG, config & 0x7f)) < 0) + if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0) return ret; return 0; } -static int mt312_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct mt312_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &mt312_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_DISEQC_RESET_OVERLOAD: - return -EOPNOTSUPP; - - case FE_DISEQC_SEND_MASTER_CMD: - return mt312_send_master_cmd(i2c, arg); - - case FE_DISEQC_RECV_SLAVE_REPLY: - if (state->id == ID_MT312) - return mt312_recv_slave_reply(i2c, arg); - else - return -EOPNOTSUPP; - - case FE_DISEQC_SEND_BURST: - return mt312_send_burst(i2c, (fe_sec_mini_cmd_t) arg); - - case FE_SET_TONE: - return mt312_set_tone(i2c, (fe_sec_tone_mode_t) arg); - - case FE_SET_VOLTAGE: - return mt312_set_voltage(i2c, (fe_sec_voltage_t) arg); - - case FE_ENABLE_HIGH_LNB_VOLTAGE: - return -EOPNOTSUPP; - - case FE_READ_STATUS: - return mt312_read_status(state, arg); - - case FE_READ_BER: - return mt312_read_bercnt(i2c, arg); - - case FE_READ_SIGNAL_STRENGTH: - return mt312_read_agc(i2c, arg); - - case FE_READ_SNR: - return mt312_read_snr(i2c, arg); - - case FE_READ_UNCORRECTED_BLOCKS: - return mt312_read_ubc(i2c, arg); - - case FE_SET_FRONTEND: - return mt312_set_frontend(state, arg); - - case FE_GET_FRONTEND: - return mt312_get_frontend(i2c, arg); - - case FE_GET_EVENT: - return -EOPNOTSUPP; - - case FE_SLEEP: - return mt312_sleep(i2c); - - case FE_INIT: - /* For the VP310 we should run at 60MHz when ever possible. - * It should be better to run the mt312 ar lower speed when - * ever possible, but tunning will be slower. ACCJr 09/29/03 - */ - if (state->id == ID_MT312) - return mt312_initfe(state, (u8) 90); - else - return mt312_initfe(state, (u8) 60); - - case FE_GET_TUNE_SETTINGS: +static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; fesettings->min_delay_ms = 50; fesettings->step_size = 0; fesettings->max_drift = 0; return 0; } - default: - return -ENOIOCTLCMD; - } - - return 0; +static void mt312_release(struct dvb_frontend* fe) +{ + struct mt312_state* state = (struct mt312_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_client client_template; +static struct dvb_frontend_ops vp310_mt312_ops; -static int mt312_attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* vp310_attach(const struct mt312_config* config, + struct i2c_adapter* i2c) { - struct mt312_state *state; - struct i2c_client *client; - int ret; - u8 id; - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if (mt312_readreg(adapter, ID, &id) < 0) - return -ENODEV; - - if ((id != ID_VP310) && (id != ID_MT312)) - return -ENODEV; - - if ( !(state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct mt312_state)); - state->i2c = adapter; - state->id = id; + struct mt312_state* state = NULL; - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; - } + /* allocate memory for the internal state */ + state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + if (state == NULL) + goto error; - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = I2C_ADDR_MT312; - i2c_set_clientdata(client, state); + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops)); + strcpy(state->ops.info.name, "Zarlink VP310 DVB-S"); - if ((ret = i2c_attach_client(client))) { - kfree(client); - kfree(state); - return ret; + /* check if the demod is there */ + if (mt312_readreg(state, ID, &state->id) < 0) + goto error; + if (state->id != ID_VP310) { + goto error; } - BUG_ON(!state->dvb); + /* create dvb_frontend */ + state->frequency = 90; + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - if ((ret = dvb_register_frontend(mt312_ioctl, state->dvb, state, - &mt312_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(client); +error: + if (state) kfree(state); - return ret; - } - - return 0; + return NULL; } -static int mt312_detach_client(struct i2c_client *client) +struct dvb_frontend* mt312_attach(const struct mt312_config* config, + struct i2c_adapter* i2c) { - struct mt312_state *state = i2c_get_clientdata(client); + struct mt312_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + if (state == NULL) + goto error; - dvb_unregister_frontend (mt312_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops)); + strcpy(state->ops.info.name, "Zarlink MT312 DVB-S"); -static int mt312_command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct mt312_state *state = i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; + /* check if the demod is there */ + if (mt312_readreg(state, ID, &state->id) < 0) + goto error; + if (state->id != ID_MT312) { + goto error; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_MT312, - .flags = I2C_DF_NOTIFY, - .attach_adapter = mt312_attach_adapter, - .detach_client = mt312_detach_client, - .command = mt312_command, -}; + /* create dvb_frontend */ + state->frequency = 60; + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, +error: + if (state) + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops vp310_mt312_ops = { + + .info = { + .name = "Zarlink ???? DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, + .symbol_rate_min = MT312_SYS_CLK / 128, + .symbol_rate_max = MT312_SYS_CLK / 2, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | + FE_CAN_RECOVER + }, + + .release = mt312_release, + + .init = mt312_initfe, + .sleep = mt312_sleep, + + .set_frontend = mt312_set_frontend, + .get_frontend = mt312_get_frontend, + .get_tune_settings = mt312_get_tune_settings, + + .read_status = mt312_read_status, + .read_ber = mt312_read_ber, + .read_signal_strength = mt312_read_signal_strength, + .read_snr = mt312_read_snr, + .read_ucblocks = mt312_read_ucblocks, + + .diseqc_send_master_cmd = mt312_send_master_cmd, + .diseqc_send_burst = mt312_send_burst, + .set_tone = mt312_set_tone, + .set_voltage = mt312_set_voltage, }; -static int __init mt312_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit mt312_module_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "mt312: driver deregistration failed.\n"); -} - -module_init(mt312_module_init); -module_exit(mt312_module_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("MT312 Satellite Channel Decoder Driver"); +MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver"); MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(mt312_attach); +EXPORT_SYMBOL(vp310_attach); diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h index 222c6ecbd0cc..1f58f0c9d110 100644 --- a/drivers/media/dvb/frontends/mt312.h +++ b/drivers/media/dvb/frontends/mt312.h @@ -1,5 +1,5 @@ /* - Driver for Zarlink MT312 QPSK Frontend + Driver for Zarlink MT312 Satellite Channel Decoder Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> @@ -18,145 +18,30 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + References: + http://products.zarlink.com/product_profiles/MT312.htm + http://products.zarlink.com/product_profiles/SL1935.htm */ -#ifndef _DVB_FRONTENDS_MT312 -#define _DVB_FRONTENDS_MT312 +#ifndef MT312_H +#define MT312_H -enum mt312_reg_addr { - QPSK_INT_H = 0, - QPSK_INT_M = 1, - QPSK_INT_L = 2, - FEC_INT = 3, - QPSK_STAT_H = 4, - QPSK_STAT_L = 5, - FEC_STATUS = 6, - LNB_FREQ_H = 7, - LNB_FREQ_L = 8, - M_SNR_H = 9, - M_SNR_L = 10, - VIT_ERRCNT_H = 11, - VIT_ERRCNT_M = 12, - VIT_ERRCNT_L = 13, - RS_BERCNT_H = 14, - RS_BERCNT_M = 15, - RS_BERCNT_L = 16, - RS_UBC_H = 17, - RS_UBC_L = 18, - SIG_LEVEL = 19, - GPP_CTRL = 20, - RESET = 21, - DISEQC_MODE = 22, - SYM_RATE_H = 23, - SYM_RATE_L = 24, - VIT_MODE = 25, - QPSK_CTRL = 26, - GO = 27, - IE_QPSK_H = 28, - IE_QPSK_M = 29, - IE_QPSK_L = 30, - IE_FEC = 31, - QPSK_STAT_EN = 32, - FEC_STAT_EN = 33, - SYS_CLK = 34, - DISEQC_RATIO = 35, - DISEQC_INSTR = 36, - FR_LIM = 37, - FR_OFF = 38, - AGC_CTRL = 39, - AGC_INIT = 40, - AGC_REF = 41, - AGC_MAX = 42, - AGC_MIN = 43, - AGC_LK_TH = 44, - TS_AGC_LK_TH = 45, - AGC_PWR_SET = 46, - QPSK_MISC = 47, - SNR_THS_LOW = 48, - SNR_THS_HIGH = 49, - TS_SW_RATE = 50, - TS_SW_LIM_L = 51, - TS_SW_LIM_H = 52, - CS_SW_RATE_1 = 53, - CS_SW_RATE_2 = 54, - CS_SW_RATE_3 = 55, - CS_SW_RATE_4 = 56, - CS_SW_LIM = 57, - TS_LPK = 58, - TS_LPK_M = 59, - TS_LPK_L = 60, - CS_KPROP_H = 61, - CS_KPROP_L = 62, - CS_KINT_H = 63, - CS_KINT_L = 64, - QPSK_SCALE = 65, - TLD_OUTCLK_TH = 66, - TLD_INCLK_TH = 67, - FLD_TH = 68, - PLD_OUTLK3 = 69, - PLD_OUTLK2 = 70, - PLD_OUTLK1 = 71, - PLD_OUTLK0 = 72, - PLD_INLK3 = 73, - PLD_INLK2 = 74, - PLD_INLK1 = 75, - PLD_INLK0 = 76, - PLD_ACC_TIME = 77, - SWEEP_PAR = 78, - STARTUP_TIME = 79, - LOSSLOCK_TH = 80, - FEC_LOCK_TM = 81, - LOSSLOCK_TM = 82, - VIT_ERRPER_H = 83, - VIT_ERRPER_M = 84, - VIT_ERRPER_L = 85, - VIT_SETUP = 86, - VIT_REF0 = 87, - VIT_REF1 = 88, - VIT_REF2 = 89, - VIT_REF3 = 90, - VIT_REF4 = 91, - VIT_REF5 = 92, - VIT_REF6 = 93, - VIT_MAXERR = 94, - BA_SETUPT = 95, - OP_CTRL = 96, - FEC_SETUP = 97, - PROG_SYNC = 98, - AFC_SEAR_TH = 99, - CSACC_DIF_TH = 100, - QPSK_LK_CT = 101, - QPSK_ST_CT = 102, - MON_CTRL = 103, - QPSK_RESET = 104, - QPSK_TST_CT = 105, - QPSK_TST_ST = 106, - TEST_R = 107, - AGC_H = 108, - AGC_M = 109, - AGC_L = 110, - FREQ_ERR1_H = 111, - FREQ_ERR1_M = 112, - FREQ_ERR1_L = 113, - FREQ_ERR2_H = 114, - FREQ_ERR2_L = 115, - SYM_RAT_OP_H = 116, - SYM_RAT_OP_L = 117, - DESEQC2_INT = 118, - DISEQC2_STAT = 119, - DISEQC2_FIFO = 120, - DISEQC2_CTRL1 = 121, - DISEQC2_CTRL2 = 122, - MONITOR_H = 123, - MONITOR_L = 124, - TEST_MODE = 125, - ID = 126, - CONFIG = 127 -}; +#include <linux/dvb/frontend.h> + +struct mt312_config +{ + /* the demodulator's i2c address */ + u8 demod_address; -enum mt312_model_id { - ID_VP310 = 1, - ID_MT312 = 3 + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; -#endif /* DVB_FRONTENDS_MT312 */ +extern struct dvb_frontend* mt312_attach(const struct mt312_config* config, + struct i2c_adapter* i2c); + +extern struct dvb_frontend* vp310_attach(const struct mt312_config* config, + struct i2c_adapter* i2c); + +#endif // MT312_H diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb/frontends/mt312_priv.h new file mode 100644 index 000000000000..5e0b95b5337b --- /dev/null +++ b/drivers/media/dvb/frontends/mt312_priv.h @@ -0,0 +1,162 @@ +/* + Driver for Zarlink MT312 QPSK Frontend + + Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> + + 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 _DVB_FRONTENDS_MT312_PRIV +#define _DVB_FRONTENDS_MT312_PRIV + +enum mt312_reg_addr { + QPSK_INT_H = 0, + QPSK_INT_M = 1, + QPSK_INT_L = 2, + FEC_INT = 3, + QPSK_STAT_H = 4, + QPSK_STAT_L = 5, + FEC_STATUS = 6, + LNB_FREQ_H = 7, + LNB_FREQ_L = 8, + M_SNR_H = 9, + M_SNR_L = 10, + VIT_ERRCNT_H = 11, + VIT_ERRCNT_M = 12, + VIT_ERRCNT_L = 13, + RS_BERCNT_H = 14, + RS_BERCNT_M = 15, + RS_BERCNT_L = 16, + RS_UBC_H = 17, + RS_UBC_L = 18, + SIG_LEVEL = 19, + GPP_CTRL = 20, + RESET = 21, + DISEQC_MODE = 22, + SYM_RATE_H = 23, + SYM_RATE_L = 24, + VIT_MODE = 25, + QPSK_CTRL = 26, + GO = 27, + IE_QPSK_H = 28, + IE_QPSK_M = 29, + IE_QPSK_L = 30, + IE_FEC = 31, + QPSK_STAT_EN = 32, + FEC_STAT_EN = 33, + SYS_CLK = 34, + DISEQC_RATIO = 35, + DISEQC_INSTR = 36, + FR_LIM = 37, + FR_OFF = 38, + AGC_CTRL = 39, + AGC_INIT = 40, + AGC_REF = 41, + AGC_MAX = 42, + AGC_MIN = 43, + AGC_LK_TH = 44, + TS_AGC_LK_TH = 45, + AGC_PWR_SET = 46, + QPSK_MISC = 47, + SNR_THS_LOW = 48, + SNR_THS_HIGH = 49, + TS_SW_RATE = 50, + TS_SW_LIM_L = 51, + TS_SW_LIM_H = 52, + CS_SW_RATE_1 = 53, + CS_SW_RATE_2 = 54, + CS_SW_RATE_3 = 55, + CS_SW_RATE_4 = 56, + CS_SW_LIM = 57, + TS_LPK = 58, + TS_LPK_M = 59, + TS_LPK_L = 60, + CS_KPROP_H = 61, + CS_KPROP_L = 62, + CS_KINT_H = 63, + CS_KINT_L = 64, + QPSK_SCALE = 65, + TLD_OUTCLK_TH = 66, + TLD_INCLK_TH = 67, + FLD_TH = 68, + PLD_OUTLK3 = 69, + PLD_OUTLK2 = 70, + PLD_OUTLK1 = 71, + PLD_OUTLK0 = 72, + PLD_INLK3 = 73, + PLD_INLK2 = 74, + PLD_INLK1 = 75, + PLD_INLK0 = 76, + PLD_ACC_TIME = 77, + SWEEP_PAR = 78, + STARTUP_TIME = 79, + LOSSLOCK_TH = 80, + FEC_LOCK_TM = 81, + LOSSLOCK_TM = 82, + VIT_ERRPER_H = 83, + VIT_ERRPER_M = 84, + VIT_ERRPER_L = 85, + VIT_SETUP = 86, + VIT_REF0 = 87, + VIT_REF1 = 88, + VIT_REF2 = 89, + VIT_REF3 = 90, + VIT_REF4 = 91, + VIT_REF5 = 92, + VIT_REF6 = 93, + VIT_MAXERR = 94, + BA_SETUPT = 95, + OP_CTRL = 96, + FEC_SETUP = 97, + PROG_SYNC = 98, + AFC_SEAR_TH = 99, + CSACC_DIF_TH = 100, + QPSK_LK_CT = 101, + QPSK_ST_CT = 102, + MON_CTRL = 103, + QPSK_RESET = 104, + QPSK_TST_CT = 105, + QPSK_TST_ST = 106, + TEST_R = 107, + AGC_H = 108, + AGC_M = 109, + AGC_L = 110, + FREQ_ERR1_H = 111, + FREQ_ERR1_M = 112, + FREQ_ERR1_L = 113, + FREQ_ERR2_H = 114, + FREQ_ERR2_L = 115, + SYM_RAT_OP_H = 116, + SYM_RAT_OP_L = 117, + DESEQC2_INT = 118, + DISEQC2_STAT = 119, + DISEQC2_FIFO = 120, + DISEQC2_CTRL1 = 121, + DISEQC2_CTRL2 = 122, + MONITOR_H = 123, + MONITOR_L = 124, + TEST_MODE = 125, + ID = 126, + CONFIG = 127 +}; + +enum mt312_model_id { + ID_VP310 = 1, + ID_MT312 = 3 +}; + +#endif /* DVB_FRONTENDS_MT312_PRIV */ diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c index 2bb0db18eaac..86a4627372df 100644 --- a/drivers/media/dvb/frontends/mt352.c +++ b/drivers/media/dvb/frontends/mt352.c @@ -37,438 +37,88 @@ #include <linux/delay.h> #include "dvb_frontend.h" +#include "mt352_priv.h" #include "mt352.h" -#define FRONTEND_NAME "dvbfe_mt352" +struct mt352_state { -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) + struct i2c_adapter* i2c; -static int debug; -#define MAX_CARDS 4 -static int force_card[MAX_CARDS] = { -1, -1, -1, -1 }; -static int force_card_count = 0; + struct dvb_frontend_ops ops; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -module_param_array(force_card, int, &force_card_count, 0444); -MODULE_PARM_DESC(force_card, "Forces the type of each attached mt352 frontend.\n\t" - "If your card is not autodetected, then you must specify its type here.\n\t" - "Valid card types are: 0 == AVDVBT771, 1 == TUA6034, 2 == TDTC9251DH01C,\n\t" - "3 == DVICO FusionHDTV DVB-T1, 4 == DVICO FusionHDTV DVB-T Lite."); + /* configuration settings */ + const struct mt352_config* config; -struct mt352_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - struct dvb_frontend_info fe_info; - int card_type; + struct dvb_frontend frontend; }; -#define mt352_write(ibuf, ilen) \ +static int debug; +#define dprintk(args...) \ do { \ - struct i2c_msg msg = { .addr = I2C_MT352_ADDR, .flags = 0, \ - .buf = ibuf, .len = ilen }; \ - int err = i2c_transfer(i2c, &msg, 1); \ - if (err != 1) { \ - printk(KERN_WARNING \ - "mt352_write() failed (err = %d)!\n", err); \ - return err; \ - } \ + if (debug) printk(KERN_DEBUG "mt352: " args); \ } while (0) -static struct _tuner_info tuner_info [] = { - { - .fe_name = "AverMedia DVB-T 771", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_AVERMEDIA771, - .mt352_charge_pump = mt352_cp_AVERMEDIA771, - .mt352_band_select = mt352_bs_AVERMEDIA771 - }, - { - .fe_name = "Zarlink MT352 + TUA6034 DVB-T", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_TUA6034, - .mt352_charge_pump = mt352_cp_TUA6034, - .mt352_band_select = mt352_bs_TUA6034 - }, - { - .fe_name = "Zarlink MT352 + Samsung TDTC9251DH01C DVB-T", - .fe_frequency_min = 474000000, - .fe_frequency_max = 858000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_TDTC9251DH01C, - .mt352_charge_pump = mt352_cp_TDTC9251DH01C, - .mt352_band_select = mt352_bs_TDTC9251DH01C - }, - { - .fe_name = "DVICO FusionHDTV DVB-T1", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_DVICODVBT1, - .mt352_charge_pump = mt352_cp_DVICODVBT1, - .mt352_band_select = mt352_bs_DVICODVBT1, - }, - { - .fe_name = "DVICO FusionHDTV DVB-T Lite", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc0, - .mt352_init = mt352_init_DVICODVBTLITE, - .mt352_charge_pump = mt352_cp_DVICODVBTLITE, - .mt352_band_select = mt352_bs_DVICODVBTLITE, - } -}; - -static struct dvb_frontend_info mt352_info_template = { - .name = "DVB-T Zarlink MT352 demodulator driver", - .type = FE_OFDM, -/* - .frequency_min = 0, - .frequency_max = 0, - .frequency_stepsize = 0, - .frequency_tolerance = 0, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .symbol_rate_tolerance = ???, -*/ - .notifier_delay = 0, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | - FE_CAN_MUTE_TS -}; - -static u8 mt352_reset [] = { RESET, 0x80 }; -static u8 mt352_adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; -static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 }; - -static int mt352_init_TUA6034(struct i2c_adapter *i2c) +int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen) { - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x19, 0xa0 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x10, 0x23, 0x00, 0xFF, 0xFF, - 0x00, 0xFF, 0x00, 0x40, 0x40 }; - static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; - static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg,sizeof(mt352_agc_cfg)); - udelay(2000); - mt352_write(mt352_av771_extra,sizeof(mt352_av771_extra)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x10, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0xa1 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, + .buf = ibuf, .len = ilen }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + dprintk("mt352_write() failed (err = %d)!\n", err); + return err; } -static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; - static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(200); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - return 0; } -static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c) +static u8 mt352_read_register(struct mt352_state* state, u8 reg) { - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x38 }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(200); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static unsigned char mt352_cp_TUA6034(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 542000000) - cp = 0xbe; - else if (freq < 830000000) - cp = 0xf6; - else - cp = 0xfe; - - return cp; -} - -static unsigned char mt352_cp_AVERMEDIA771(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 150000000) - cp = 0xB4; - else if (freq < 173000000) - cp = 0xBC; - else if (freq < 250000000) - cp = 0xB4; - else if (freq < 400000000) - cp = 0xBC; - else if (freq < 420000000) - cp = 0xF4; - else if (freq < 470000000) - cp = 0xFC; - else if (freq < 600000000) - cp = 0xBC; - else if (freq < 730000000) - cp = 0xF4; - else - cp = 0xFC; - - return cp; -} - -static unsigned char mt352_cp_TDTC9251DH01C(u32 freq) -{ - return(0xcc); -} - -static unsigned char mt352_cp_DVICODVBT1(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 542000000) - cp = 0xbc; - else if (freq < 830000000) - cp = 0xf4; - else - cp = 0xfc; - - return cp; -} + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, + .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; -static unsigned char mt352_cp_DVICODVBTLITE(u32 freq) -{ - unsigned char cp = 0; + ret = i2c_transfer(state->i2c, msg, 2); - if (freq < 542000000) - cp = 0xb4; - else if (freq < 771000000) - cp = 0xbc; - else - cp = 0xf4; + if (ret != 2) + dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - return cp; + return b1[0]; } -static unsigned char mt352_bs_TUA6034(u32 freq) -{ - unsigned char bs = 0; - if (freq < 250000000) - bs = 0x01; - else - bs = 0x08; - return bs; -} -static unsigned char mt352_bs_AVERMEDIA771(u32 freq) -{ - unsigned char bs = 0; - - if (freq < 150000000) - bs = 0x01; - else if (freq < 173000000) - bs = 0x01; - else if (freq < 250000000) - bs = 0x02; - else if (freq < 400000000) - bs = 0x02; - else if (freq < 420000000) - bs = 0x02; - else if (freq < 470000000) - bs = 0x02; - else if (freq < 600000000) - bs = 0x08; - else if (freq < 730000000) - bs = 0x08; - else - bs = 0x08; - return bs; -} -static unsigned char mt352_bs_TDTC9251DH01C(u32 freq) -{ - unsigned char bs = 0; - if (freq >= 48000000 && freq <= 154000000) /* low band */ - bs = 0x09; - if (freq >= 161000000 && freq <= 439000000) /* medium band */ - bs = 0x0a; - if (freq >= 447000000 && freq <= 863000000) /* high band */ - bs = 0x08; - return bs; -} -static unsigned char mt352_bs_DVICODVBT1(u32 freq) -{ - unsigned char bs = 0; - - if (freq == 0) /* power down PLL */ - bs = 0x03; - else if (freq < 157500000) /* low band */ - bs = 0x01; - else if (freq < 443250000) /* mid band */ - bs = 0x02; - else /* high band */ - bs = 0x04; - - return bs; -} -static unsigned char mt352_bs_DVICODVBTLITE(u32 freq) -{ - unsigned char bs = 0; - - if (freq == 0) /* power down PLL */ - bs = 0x03; - else if (freq < 443250000) /* mid band */ - bs = 0x02; - else /* high band */ - bs = 0x08; - - return bs; -} - -static u32 mt352_read_eeprom_dword(struct i2c_adapter *i2c, int dword_base) - { - int i; - u32 dword = 0; - u8 reg, val; - struct i2c_msg msg[2] = { - { - .addr = 0x50, - .flags = 0, - .buf = ®, - .len = 1 - }, - { - .addr = 0x50, - .flags = I2C_M_RD, - .buf = &val, - .len = 1 - } - }; - - for (i = 0; i < 4; i++) { - reg = dword_base + i; - if (i2c_transfer(i2c,msg,2) != 2) - return 0; - dword = (dword << 8) | val; - } - - return dword; -} - -static int mt352_init(struct i2c_adapter *i2c, int card_type) -{ - /** - * all register write sequence have the register address of the - * first register in the first byte, thenafter the value to write - * into this and the following registers. - * - * - * We only write non-default settings, all default settings are - * restored by the full mt352_reset sequence. - * - * - * The optimal AGC target value and slope might vary from tuner - * type to tuner type, so check whether you need to adjust this one... - **/ - - return(MT352_INIT(i2c)); -} - -static int mt352_sleep(struct i2c_adapter *i2c) +static int mt352_sleep(struct dvb_frontend* fe) { static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; - mt352_write(mt352_softdown, sizeof(mt352_softdown)); + mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); return 0; } -static int mt352_set_parameters(struct i2c_adapter *i2c, - struct dvb_frontend_parameters *param, - int card_type) +static int mt352_set_parameters(struct dvb_frontend* fe, + struct dvb_frontend_parameters *param) { + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; unsigned char buf[14]; unsigned int tps = 0; struct dvb_ofdm_parameters *op = ¶m->u.ofdm; - uint16_t tmp; int i; switch (op->code_rate_HP) { @@ -600,22 +250,7 @@ static int mt352_set_parameters(struct i2c_adapter *i2c, buf[6] = 0x31; /* INPUT_FREQ_(1|0), 20.48MHz clock, 36.166667MHz IF */ buf[7] = 0x05; /* see MT352 Design Manual page 32 for details */ - buf[8] = PLL_I2C_ADDR; - - /** - * All the following settings are tuner module dependent, - * check the datasheet... - */ - - /* here we assume 1/6MHz == 166.66kHz stepsize */ - #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ - tmp = (((param->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - - buf[9] = msb(tmp); /* CHAN_START_(1|0) */ - buf[10] = lsb(tmp); - - buf[11] = MT352_CHARGE_PUMP(param->frequency); - buf[12] = MT352_BAND_SELECT(param->frequency); + state->config->pll_set(fe, param, buf+8); buf[13] = 0x01; /* TUNER_GO!! */ @@ -623,39 +258,18 @@ static int mt352_set_parameters(struct i2c_adapter *i2c, * parameters already set. Enhances tuning time and prevents stream * breakup when retuning the same transponder. */ for (i = 1; i < 13; i++) - if (buf[i] != mt352_read_register(i2c, i + 0x50)) { - mt352_write(buf, sizeof(buf)); + if (buf[i] != mt352_read_register(state, i + 0x50)) { + mt352_write(fe, buf, sizeof(buf)); break; } return 0; } -static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg) -{ - int ret; - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = I2C_MT352_ADDR, - .flags = 0, - .buf = b0, .len = 1 }, - { .addr = I2C_MT352_ADDR, - .flags = I2C_M_RD, - .buf = b1, .len = 1 } }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - printk(KERN_WARNING - "%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - - -static int mt352_get_parameters(struct i2c_adapter *i2c, +static int mt352_get_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; u16 tps; u16 div; u8 trl; @@ -672,7 +286,7 @@ static int mt352_get_parameters(struct i2c_adapter *i2c, FEC_AUTO }; - if ( (mt352_read_register(i2c,0x00) & 0xC0) != 0xC0 ) + if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) { return -EINVAL; } @@ -680,9 +294,9 @@ static int mt352_get_parameters(struct i2c_adapter *i2c, /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because * the mt352 sometimes works with the wrong parameters */ - tps = (mt352_read_register(i2c, TPS_RECEIVED_1) << 8) | mt352_read_register(i2c, TPS_RECEIVED_0); - div = (mt352_read_register(i2c, CHAN_START_1) << 8) | mt352_read_register(i2c, CHAN_START_0); - trl = mt352_read_register(i2c, TRL_NOMINAL_RATE_1); + tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); + div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); + trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; @@ -759,7 +373,7 @@ static int mt352_get_parameters(struct i2c_adapter *i2c, } - if (mt352_read_register(i2c, STATUS_2) & 0x02) + if (mt352_read_register(state, STATUS_2) & 0x02) param->inversion = INVERSION_OFF; else param->inversion = INVERSION_ON; @@ -767,26 +381,13 @@ static int mt352_get_parameters(struct i2c_adapter *i2c, return 0; } - -static int mt352_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct mt352_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - int card_type = state->card_type; - u8 r,snr; - fe_status_t *status; - u16 signal; - struct dvb_frontend_tune_settings *fe_tune_settings; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &state->fe_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - status = arg; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + u8 r; + *status = 0; - r = mt352_read_register (i2c, STATUS_0); + r = mt352_read_register (state, STATUS_0); if (r & (1 << 4)) *status = FE_HAS_CARRIER; if (r & (1 << 1)) @@ -794,243 +395,164 @@ static int mt352_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) if (r & (1 << 5)) *status |= FE_HAS_LOCK; - r = mt352_read_register (i2c, STATUS_1); + r = mt352_read_register (state, STATUS_1); if (r & (1 << 1)) *status |= FE_HAS_SYNC; - r = mt352_read_register (i2c, STATUS_3); + r = mt352_read_register (state, STATUS_3); if (r & (1 << 6)) *status |= FE_HAS_SIGNAL; - break; - - case FE_READ_BER: - *((u32 *) arg) = (mt352_read_register (i2c, RS_ERR_CNT_2) << 16) | - (mt352_read_register (i2c, RS_ERR_CNT_1) << 8) | - (mt352_read_register (i2c, RS_ERR_CNT_0)); - break; - - case FE_READ_SIGNAL_STRENGTH: - signal = (mt352_read_register (i2c, AGC_GAIN_3) << 8) | - (mt352_read_register (i2c, AGC_GAIN_2)); - *((u16*) arg) = ~signal; - break; - - case FE_READ_SNR: - snr = mt352_read_register (i2c, SNR); - *((u16*) arg) = (snr << 8) | snr; - break; - - case FE_READ_UNCORRECTED_BLOCKS: - *(u32*) arg = (mt352_read_register (i2c, RS_UBC_1) << 8) | - (mt352_read_register (i2c, RS_UBC_0)); - break; - - case FE_SET_FRONTEND: - return mt352_set_parameters (i2c, - (struct dvb_frontend_parameters *) arg, - card_type); - - case FE_GET_FRONTEND: - return mt352_get_parameters (i2c, - (struct dvb_frontend_parameters *) arg); - - case FE_GET_TUNE_SETTINGS: - fe_tune_settings = (struct dvb_frontend_tune_settings *) arg; - fe_tune_settings->min_delay_ms = 800; - fe_tune_settings->step_size = 0; - fe_tune_settings->max_drift = 0; - break; - - case FE_SLEEP: - return mt352_sleep(i2c); - - case FE_INIT: - /* Only send the initialisation command if the demodulator - * isn't already enabled. Greatly enhances tuning time. */ - if ((mt352_read_register(i2c, CLOCK_CTL) & 0x10) == 0 || - (mt352_read_register(i2c, CONFIG) & 0x20) == 0) - return mt352_init(i2c, card_type); - else - return 0; + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} - default: - return -EOPNOTSUPP; +static int mt352_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + + *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | + (mt352_read_register (state, RS_ERR_CNT_1) << 8) | + (mt352_read_register (state, RS_ERR_CNT_0)); + + return 0; } +static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + + u16 signal = (mt352_read_register (state, AGC_GAIN_3) << 8) | + (mt352_read_register (state, AGC_GAIN_2)); + + *strength = ~signal; return 0; } -static struct i2c_client client_template; - -static int mt352_attach_adapter(struct i2c_adapter *i2c) +static int mt352_read_snr(struct dvb_frontend* fe, u16* snr) { - static int num_cards_probed; - struct mt352_state *state; - struct i2c_client *client; - static u8 mt352_reset_attach [] = { RESET, 0xC0 }; - int ret; - int card_type, forced_card = -1; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - dprintk("Trying to attach to adapter 0x%x:%s.\n", - i2c->id, i2c->name); + u8 _snr = mt352_read_register (state, SNR); + *snr = (_snr << 8) | _snr; - if (mt352_read_register(i2c, CHIP_ID) != ID_MT352) - return -ENODEV; + return 0; +} - if ( !(state = kmalloc(sizeof(struct mt352_state), GFP_KERNEL)) ) - return -ENOMEM; +static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - memset(state, 0, sizeof(struct mt352_state)); - state->i2c = i2c; - state->card_type = -1; - memcpy(&state->fe_info, &mt352_info_template, sizeof(struct dvb_frontend_info)); - - /* Attempt autodetection of card type based on PCI ID information - * stored in any on-board EEPROM. */ - switch (mt352_read_eeprom_dword(i2c, 0xFC)) { /* BT878A chipset */ - case 0x07711461: - state->card_type = CARD_AVDVBT771; - break; - case 0xdb1018ac: - state->card_type = CARD_DVICODVBTLITE; - break; - default: - break; - } + *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | + (mt352_read_register (state, RS_UBC_0)); - switch (mt352_read_eeprom_dword(i2c, 0x04)) { /* CX2388x chipset */ - case 0xac1800db: - state->card_type = CARD_DVICODVBT1; - break; - default: - break; + return 0; } - if (num_cards_probed < force_card_count) - forced_card = force_card[num_cards_probed++]; +static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 800; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; - if (state->card_type == -1 && forced_card < 0) { - dprintk("Card type not automatically detected. You " - "must use the 'force_card' module parameter.\n"); - kfree(state); - return -ENODEV; + return 0; } - if (forced_card >= 0) { - if (state->card_type >= 0 && forced_card != state->card_type) - printk(KERN_WARNING FRONTEND_NAME ": Warning, overriding" - " detected card type.\n"); - state->card_type = forced_card; - } +static int mt352_init(struct dvb_frontend* fe) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - card_type = state->card_type; - printk(KERN_INFO FRONTEND_NAME ": Setup for %s\n", FE_NAME); + static u8 mt352_reset_attach [] = { RESET, 0xC0 }; - /* set the frontend name and card-specific frequency info */ - strlcpy(state->fe_info.name, FE_NAME, sizeof(state->fe_info.name)); - state->fe_info.frequency_min = FE_FREQ_MIN; - state->fe_info.frequency_max = FE_FREQ_MAX; - state->fe_info.frequency_stepsize = FE_FREQ_STEPSIZE; + if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || + (mt352_read_register(state, CONFIG) & 0x20) == 0) { /* Do a "hard" reset */ - mt352_write(mt352_reset_attach, sizeof(mt352_reset_attach)); - - /* Try to intiialise the device */ - if (mt352_init(i2c, card_type) != 0) { - kfree(state); - return -ENODEV; + mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); + return state->config->demod_init(fe); } - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; + return 0; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = i2c; - client->addr = 0; // XXX - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(client); +static void mt352_release(struct dvb_frontend* fe) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; kfree(state); - return ret; } - return 0; -} +static struct dvb_frontend_ops mt352_ops; -static int mt352_detach_client(struct i2c_client *client) +struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c) { - struct mt352_state *state = i2c_get_clientdata(client); + struct mt352_state* state = NULL; - if (state->dvb) - dvb_unregister_frontend (mt352_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); - return 0; -} + /* allocate memory for the internal state */ + state = (struct mt352_state*) kmalloc(sizeof(struct mt352_state), GFP_KERNEL); + if (state == NULL) goto error; -static int mt352_command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct mt352_state *state = i2c_get_clientdata(client); - int ret; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); - switch (cmd) { - case FE_REGISTER: - if (!state->dvb) { - if ((ret = dvb_register_frontend(mt352_ioctl, arg, - state, &state->fe_info, - THIS_MODULE))) - return ret; - state->dvb = arg; - } - break; - case FE_UNREGISTER: - if (state->dvb == arg) { - dvb_unregister_frontend(mt352_ioctl, state->dvb); - state->dvb = NULL; - } - break; - default: - return -EOPNOTSUPP; - } - return 0; + /* check if the demod is there */ + if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_MT352, - .flags = I2C_DF_NOTIFY, - .attach_adapter = mt352_attach_adapter, - .detach_client = mt352_detach_client, - .command = mt352_command, -}; +static struct dvb_frontend_ops mt352_ops = { + + .info = { + .name = "Zarlink MT352 DVB-T", + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .frequency_tolerance = 0, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + .release = mt352_release, -static int __init mt352_module_init(void) -{ - return i2c_add_driver(&driver); -} + .init = mt352_init, + .sleep = mt352_sleep, -static void __exit mt352_module_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "mt352: driver deregistration failed.\n"); -} + .set_frontend = mt352_set_parameters, + .get_frontend = mt352_get_parameters, + .get_tune_settings = mt352_get_tune_settings, + + .read_status = mt352_read_status, + .read_ber = mt352_read_ber, + .read_signal_strength = mt352_read_signal_strength, + .read_snr = mt352_read_snr, + .read_ucblocks = mt352_read_ucblocks, +}; -module_init(mt352_module_init); -module_exit(mt352_module_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("DVB-T MT352 Zarlink"); +MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(mt352_attach); +EXPORT_SYMBOL(mt352_write); diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h index 5b5fc2da150e..635095b49066 100644 --- a/drivers/media/dvb/frontends/mt352.h +++ b/drivers/media/dvb/frontends/mt352.h @@ -30,145 +30,29 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= */ -#ifndef _MT352_ -#define _MT352_ +#ifndef MT352_H +#define MT352_H -#define I2C_MT352_ADDR 0x0f -#define ID_MT352 0x13 +#include <linux/dvb/frontend.h> -#define CARD_AVDVBT771 0x00 -#define CARD_TUA6034 0x01 -#define CARD_TDTC9251DH01C 0x02 -#define CARD_DVICODVBT1 0x03 -#define CARD_DVICODVBTLITE 0x04 +struct mt352_config +{ + /* the demodulator's i2c address */ + u8 demod_address; -#define msb(x) (((x) >> 8) & 0xff) -#define lsb(x) ((x) & 0xff) + /* Initialise the demodulator and PLL. Cannot be NULL */ + int (*demod_init)(struct dvb_frontend* fe); -enum mt352_reg_addr { - STATUS_0 = 0x00, - STATUS_1 = 0x01, - STATUS_2 = 0x02, - STATUS_3 = 0x03, - STATUS_4 = 0x04, - INTERRUPT_0 = 0x05, - INTERRUPT_1 = 0x06, - INTERRUPT_2 = 0x07, - INTERRUPT_3 = 0x08, - SNR = 0x09, - VIT_ERR_CNT_2 = 0x0A, - VIT_ERR_CNT_1 = 0x0B, - VIT_ERR_CNT_0 = 0x0C, - RS_ERR_CNT_2 = 0x0D, - RS_ERR_CNT_1 = 0x0E, - RS_ERR_CNT_0 = 0x0F, - RS_UBC_1 = 0x10, - RS_UBC_0 = 0x11, - AGC_GAIN_3 = 0x12, - AGC_GAIN_2 = 0x13, - AGC_GAIN_1 = 0x14, - AGC_GAIN_0 = 0x15, - FREQ_OFFSET_2 = 0x17, - FREQ_OFFSET_1 = 0x18, - FREQ_OFFSET_0 = 0x19, - TIMING_OFFSET_1 = 0x1A, - TIMING_OFFSET_0 = 0x1B, - CHAN_FREQ_1 = 0x1C, - CHAN_FREQ_0 = 0x1D, - TPS_RECEIVED_1 = 0x1E, - TPS_RECEIVED_0 = 0x1F, - TPS_CURRENT_1 = 0x20, - TPS_CURRENT_0 = 0x21, - TPS_CELL_ID_1 = 0x22, - TPS_CELL_ID_0 = 0x23, - TPS_MISC_DATA_2 = 0x24, - TPS_MISC_DATA_1 = 0x25, - TPS_MISC_DATA_0 = 0x26, - RESET = 0x50, - TPS_GIVEN_1 = 0x51, - TPS_GIVEN_0 = 0x52, - ACQ_CTL = 0x53, - TRL_NOMINAL_RATE_1 = 0x54, - TRL_NOMINAL_RATE_0 = 0x55, - INPUT_FREQ_1 = 0x56, - INPUT_FREQ_0 = 0x57, - TUNER_ADDR = 0x58, - CHAN_START_1 = 0x59, - CHAN_START_0 = 0x5A, - CONT_1 = 0x5B, - CONT_0 = 0x5C, - TUNER_GO = 0x5D, - STATUS_EN_0 = 0x5F, - STATUS_EN_1 = 0x60, - INTERRUPT_EN_0 = 0x61, - INTERRUPT_EN_1 = 0x62, - INTERRUPT_EN_2 = 0x63, - INTERRUPT_EN_3 = 0x64, - AGC_TARGET = 0x67, - AGC_CTL = 0x68, - CAPT_RANGE = 0x75, - SNR_SELECT_1 = 0x79, - SNR_SELECT_0 = 0x7A, - RS_ERR_PER_1 = 0x7C, - RS_ERR_PER_0 = 0x7D, - CHIP_ID = 0x7F, - CHAN_STOP_1 = 0x80, - CHAN_STOP_0 = 0x81, - CHAN_STEP_1 = 0x82, - CHAN_STEP_0 = 0x83, - FEC_LOCK_TIME = 0x85, - OFDM_LOCK_TIME = 0x86, - ACQ_DELAY = 0x87, - SCAN_CTL = 0x88, - CLOCK_CTL = 0x89, - CONFIG = 0x8A, - MCLK_RATIO = 0x8B, - GPP_CTL = 0x8C, - ADC_CTL_1 = 0x8E, - ADC_CTL_0 = 0x8F + /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings. + * byte0: Set to pll i2c address (nonlinux; left shifted by 1) + * byte1-4: PLL configuration. + */ + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf); }; -struct _tuner_info { - char *fe_name; -#define FE_NAME tuner_info[card_type].fe_name +extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c); - __u32 fe_frequency_min; -#define FE_FREQ_MIN tuner_info[card_type].fe_frequency_min +extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen); - __u32 fe_frequency_max; -#define FE_FREQ_MAX tuner_info[card_type].fe_frequency_max - - __u32 fe_frequency_stepsize; //verificare se u32 e' corretto -#define FE_FREQ_STEPSIZE tuner_info[card_type].fe_frequency_stepsize - - u8 pll_i2c_addr; -#define PLL_I2C_ADDR tuner_info[card_type].pll_i2c_addr - - int (* mt352_init) (struct i2c_adapter *i2c); -#define MT352_INIT tuner_info[card_type].mt352_init - - unsigned char (* mt352_charge_pump) (u32 freq); -#define MT352_CHARGE_PUMP tuner_info[card_type].mt352_charge_pump - - unsigned char (* mt352_band_select) (u32 freq); -#define MT352_BAND_SELECT tuner_info[card_type].mt352_band_select -}; - -static int mt352_init_TUA6034(struct i2c_adapter *i2c); -static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c); -static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c); -static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c); -static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c); -static unsigned char mt352_cp_TUA6034(u32 freq); -static unsigned char mt352_cp_AVERMEDIA771(u32 freq); -static unsigned char mt352_cp_TDTC9251DH01C(u32 freq); -static unsigned char mt352_cp_DVICODVBT1(u32 freq); -static unsigned char mt352_cp_DVICODVBTLITE(u32 freq); -static unsigned char mt352_bs_TUA6034(u32 freq); -static unsigned char mt352_bs_AVERMEDIA771(u32 freq); -static unsigned char mt352_bs_TDTC9251DH01C(u32 freq); -static unsigned char mt352_bs_DVICODVBT1(u32 freq); -static unsigned char mt352_bs_DVICODVBTLITE(u32 freq); -static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg); - -#endif /* _MT352_ */ +#endif // MT352_H diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb/frontends/mt352_priv.h new file mode 100644 index 000000000000..44ad0d4c8f12 --- /dev/null +++ b/drivers/media/dvb/frontends/mt352_priv.h @@ -0,0 +1,127 @@ +/* + * Driver for Zarlink DVB-T MT352 demodulator + * + * Written by Holger Waechtler <holger@qanu.de> + * and Daniel Mack <daniel@qanu.de> + * + * AVerMedia AVerTV DVB-T 771 support by + * Wolfram Joost <dbox2@frokaschwei.de> + * + * Support for Samsung TDTC9251DH01C(M) tuner + * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it> + * Amauri Celani <acelani@essegi.net> + * + * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by + * Christopher Pascoe <c.pascoe@itee.uq.edu.au> + * + * 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 _MT352_PRIV_ +#define _MT352_PRIV_ + +#define ID_MT352 0x13 + +#define msb(x) (((x) >> 8) & 0xff) +#define lsb(x) ((x) & 0xff) + +enum mt352_reg_addr { + STATUS_0 = 0x00, + STATUS_1 = 0x01, + STATUS_2 = 0x02, + STATUS_3 = 0x03, + STATUS_4 = 0x04, + INTERRUPT_0 = 0x05, + INTERRUPT_1 = 0x06, + INTERRUPT_2 = 0x07, + INTERRUPT_3 = 0x08, + SNR = 0x09, + VIT_ERR_CNT_2 = 0x0A, + VIT_ERR_CNT_1 = 0x0B, + VIT_ERR_CNT_0 = 0x0C, + RS_ERR_CNT_2 = 0x0D, + RS_ERR_CNT_1 = 0x0E, + RS_ERR_CNT_0 = 0x0F, + RS_UBC_1 = 0x10, + RS_UBC_0 = 0x11, + AGC_GAIN_3 = 0x12, + AGC_GAIN_2 = 0x13, + AGC_GAIN_1 = 0x14, + AGC_GAIN_0 = 0x15, + FREQ_OFFSET_2 = 0x17, + FREQ_OFFSET_1 = 0x18, + FREQ_OFFSET_0 = 0x19, + TIMING_OFFSET_1 = 0x1A, + TIMING_OFFSET_0 = 0x1B, + CHAN_FREQ_1 = 0x1C, + CHAN_FREQ_0 = 0x1D, + TPS_RECEIVED_1 = 0x1E, + TPS_RECEIVED_0 = 0x1F, + TPS_CURRENT_1 = 0x20, + TPS_CURRENT_0 = 0x21, + TPS_CELL_ID_1 = 0x22, + TPS_CELL_ID_0 = 0x23, + TPS_MISC_DATA_2 = 0x24, + TPS_MISC_DATA_1 = 0x25, + TPS_MISC_DATA_0 = 0x26, + RESET = 0x50, + TPS_GIVEN_1 = 0x51, + TPS_GIVEN_0 = 0x52, + ACQ_CTL = 0x53, + TRL_NOMINAL_RATE_1 = 0x54, + TRL_NOMINAL_RATE_0 = 0x55, + INPUT_FREQ_1 = 0x56, + INPUT_FREQ_0 = 0x57, + TUNER_ADDR = 0x58, + CHAN_START_1 = 0x59, + CHAN_START_0 = 0x5A, + CONT_1 = 0x5B, + CONT_0 = 0x5C, + TUNER_GO = 0x5D, + STATUS_EN_0 = 0x5F, + STATUS_EN_1 = 0x60, + INTERRUPT_EN_0 = 0x61, + INTERRUPT_EN_1 = 0x62, + INTERRUPT_EN_2 = 0x63, + INTERRUPT_EN_3 = 0x64, + AGC_TARGET = 0x67, + AGC_CTL = 0x68, + CAPT_RANGE = 0x75, + SNR_SELECT_1 = 0x79, + SNR_SELECT_0 = 0x7A, + RS_ERR_PER_1 = 0x7C, + RS_ERR_PER_0 = 0x7D, + CHIP_ID = 0x7F, + CHAN_STOP_1 = 0x80, + CHAN_STOP_0 = 0x81, + CHAN_STEP_1 = 0x82, + CHAN_STEP_0 = 0x83, + FEC_LOCK_TIME = 0x85, + OFDM_LOCK_TIME = 0x86, + ACQ_DELAY = 0x87, + SCAN_CTL = 0x88, + CLOCK_CTL = 0x89, + CONFIG = 0x8A, + MCLK_RATIO = 0x8B, + GPP_CTL = 0x8C, + ADC_CTL_1 = 0x8E, + ADC_CTL_0 = 0x8F +}; + +/* here we assume 1/6MHz == 166.66kHz stepsize */ +#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + +#endif /* _MT352_PRIV_ */ diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c index 8b342dc95f49..a55fd99a6383 100644 --- a/drivers/media/dvb/frontends/nxt6000.c +++ b/drivers/media/dvb/frontends/nxt6000.c @@ -1,13 +1,6 @@ /* - NxtWave Communications - NXT6000 demodulator driver - This driver currently supports: - - Alps TDME7 (Tuner: MITEL SP5659) - Alps TDED4 (Tuner: TI ALP510, external Nxt6000) - Comtech DVBT-6k07 (PLL IC: SP5730) - Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org> Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au> @@ -34,217 +27,68 @@ #include <linux/slab.h> #include "dvb_frontend.h" +#include "nxt6000_priv.h" #include "nxt6000.h" -MODULE_DESCRIPTION("NxtWave NXT6000 DVB demodulator driver"); -MODULE_AUTHOR("Florian Schirmer"); -MODULE_LICENSE("GPL"); -static int debug = 0; -MODULE_PARM(debug, "i"); - -static struct dvb_frontend_info nxt6000_info = { - - .name = "NxtWave NXT6000", - .type = FE_OFDM, - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, - /*.frequency_tolerance = */ /* FIXME: 12% of SR */ - .symbol_rate_min = 0, /* FIXME */ - .symbol_rate_max = 9360000, /* FIXME */ - .symbol_rate_tolerance = 4000, - .notifier_delay = 0, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; -struct nxt6000_config { - u8 demod_addr; - u8 tuner_addr; - u8 tuner_type; - u8 clock_inversion; +struct nxt6000_state { + struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; -#define TUNER_TYPE_ALP510 0 -#define TUNER_TYPE_SP5659 1 -#define TUNER_TYPE_SP5730 2 + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct nxt6000_config* config; -// #define FE2NXT(fe) ((struct nxt6000_config *)((fe)->data)) -#define FREQ2DIV(freq) ((freq + 36166667) / 166667) + struct dvb_frontend frontend; +}; + +static int debug = 0; #define dprintk if (debug) printk -static int nxt6000_write(struct i2c_adapter *i2c, u8 addr, u8 reg, u8 data) +static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) { u8 buf[] = {reg, data}; - struct i2c_msg msg = {.addr = addr >> 1, .flags = 0, .buf = buf, .len = 2}; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; int ret; - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) - dprintk("nxt6000: nxt6000_write error (.addr = 0x%02X, reg: 0x%02X, data: 0x%02X, ret: %d)\n", addr, reg, data, ret); + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret); return (ret != 1) ? -EFAULT : 0; } -static u8 nxt6000_writereg(struct nxt6000_config *nxt, u8 reg, u8 data) -{ - return nxt6000_write(nxt->i2c, nxt->demod_addr, reg, data); -} - -static u8 nxt6000_read(struct i2c_adapter *i2c, u8 addr, u8 reg) +static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg) { int ret; u8 b0[] = {reg}; u8 b1[] = {0}; struct i2c_msg msgs[] = { - {.addr = addr >> 1,.flags = 0,.buf = b0,.len = 1}, - {.addr = addr >> 1,.flags = I2C_M_RD,.buf = b1,.len = 1} + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} }; - ret = i2c_transfer(i2c, msgs, 2); + ret = i2c_transfer(state->i2c, msgs, 2); if (ret != 2) - dprintk("nxt6000: nxt6000_read error (.addr = 0x%02X, reg: 0x%02X, ret: %d)\n", addr, reg, ret); + dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret); return b1[0]; } -static u8 nxt6000_readreg(struct nxt6000_config *nxt, u8 reg) -{ - return nxt6000_read(nxt->i2c, nxt->demod_addr, reg); -} - -static int pll_test(struct i2c_adapter *i2c, u8 demod_addr, u8 tuner_addr) -{ - u8 buf [1]; - struct i2c_msg msg = {.addr = tuner_addr >> 1,.flags = I2C_M_RD,.buf = buf,.len = 1 }; - int ret; - - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - ret = i2c_transfer(i2c, &msg, 1); - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - - return (ret != 1) ? -EFAULT : 0; -} - -static int pll_write(struct i2c_adapter *i2c, u8 demod_addr, u8 tuner_addr, u8 * buf, u8 len) -{ - struct i2c_msg msg = {.addr = tuner_addr >> 1, .flags = 0, .buf = buf, .len = len}; - int ret; - - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - ret = i2c_transfer(i2c, &msg, 1); - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - - if (ret != 1) - dprintk("nxt6000: pll_write error %d\n", ret); - - return (ret != 1) ? -EFAULT : 0; -} - -static int sp5659_set_tv_freq(struct nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F; - buf[1] = FREQ2DIV(freq) & 0xFF; - buf[2] = (((FREQ2DIV(freq) >> 15) & 0x03) << 5) | 0x85; - - if ((freq >= 174000000) && (freq < 230000000)) - buf[3] = 0x82; - else if ((freq >= 470000000) && (freq < 782000000)) - buf[3] = 0x85; - else if ((freq >= 782000000) && (freq < 863000000)) - buf[3] = 0xC5; - else - return -EINVAL; - - return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static int alp510_set_tv_freq(struct nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F; - buf[1] = FREQ2DIV(freq) & 0xFF; - buf[2] = 0x85; - -#if 0 - if ((freq >= 47000000) && (freq < 153000000)) - buf[3] = 0x01; - else if ((freq >= 153000000) && (freq < 430000000)) - buf[3] = 0x02; - else if ((freq >= 430000000) && (freq < 824000000)) - buf[3] = 0x08; - else if ((freq >= 824000000) && (freq < 863000000)) - buf[3] = 0x88; - else - return -EINVAL; -#else - if ((freq >= 47000000) && (freq < 153000000)) - buf[3] = 0x01; - else if ((freq >= 153000000) && (freq < 430000000)) - buf[3] = 0x02; - else if ((freq >= 430000000) && (freq < 824000000)) - buf[3] = 0x0C; - else if ((freq >= 824000000) && (freq < 863000000)) - buf[3] = 0x8C; - else - return -EINVAL; -#endif - - return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static int sp5730_set_tv_freq(struct nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F; - buf[1] = FREQ2DIV(freq) & 0xFF; - buf[2] = 0x93; - - if ((freq >= 51000000) && (freq < 132100000)) - buf[3] = 0x05; - else if ((freq >= 132100000) && (freq < 143000000)) - buf[3] = 0x45; - else if ((freq >= 146000000) && (freq < 349100000)) - buf[3] = 0x06; - else if ((freq >= 349100000) && (freq < 397100000)) - buf[3] = 0x46; - else if ((freq >= 397100000) && (freq < 426000000)) - buf[3] = 0x86; - else if ((freq >= 430000000) && (freq < 659100000)) - buf[3] = 0x03; - else if ((freq >= 659100000) && (freq < 759100000)) - buf[3] = 0x43; - else if ((freq >= 759100000) && (freq < 858000000)) - buf[3] = 0x83; - else - return -EINVAL; - - return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static void nxt6000_reset(struct nxt6000_config *fe) +static void nxt6000_reset(struct nxt6000_state* state) { u8 val; - val = nxt6000_readreg(fe, OFDM_COR_CTL); + val = nxt6000_readreg(state, OFDM_COR_CTL); - nxt6000_writereg(fe, OFDM_COR_CTL, val & ~COREACT); - nxt6000_writereg(fe, OFDM_COR_CTL, val | COREACT); + nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT); + nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT); } -static int nxt6000_set_bandwidth(struct nxt6000_config *fe, fe_bandwidth_t bandwidth) +static int nxt6000_set_bandwidth(struct nxt6000_state* state, fe_bandwidth_t bandwidth) { u16 nominal_rate; int result; @@ -270,119 +114,115 @@ static int nxt6000_set_bandwidth(struct nxt6000_config *fe, fe_bandwidth_t bandw break; default: - return -EINVAL; - } - if ((result = nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) + if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) return result; - return nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); + return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); } -static int nxt6000_set_guard_interval(struct nxt6000_config *fe, fe_guard_interval_t guard_interval) +static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval) { switch(guard_interval) { case GUARD_INTERVAL_1_32: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); case GUARD_INTERVAL_1_16: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); case GUARD_INTERVAL_AUTO: case GUARD_INTERVAL_1_8: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); case GUARD_INTERVAL_1_4: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); default: return -EINVAL; } } -static int nxt6000_set_inversion(struct nxt6000_config *fe, fe_spectral_inversion_t inversion) +static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion) { switch(inversion) { case INVERSION_OFF: - - return nxt6000_writereg(fe, OFDM_ITB_CTL, 0x00); + return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00); case INVERSION_ON: - - return nxt6000_writereg(fe, OFDM_ITB_CTL, ITBINV); + return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); default: - return -EINVAL; } } -static int nxt6000_set_transmission_mode(struct nxt6000_config *fe, fe_transmit_mode_t transmission_mode) +static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode) { int result; switch(transmission_mode) { case TRANSMISSION_MODE_2K: - - if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0) + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) return result; - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); case TRANSMISSION_MODE_8K: case TRANSMISSION_MODE_AUTO: - - if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0) + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) return result; - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); default: - return -EINVAL; } } -static void nxt6000_setup(struct nxt6000_config *fe) +static void nxt6000_setup(struct dvb_frontend* fe) { - nxt6000_writereg(fe, RS_COR_SYNC_PARAM, SYNC_PARAM); - nxt6000_writereg(fe, BER_CTRL, /*(1 << 2) |*/ (0x01 << 1) | 0x01); - nxt6000_writereg(fe, VIT_COR_CTL, VIT_COR_RESYNC); - nxt6000_writereg(fe, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(fe, OFDM_COR_CTL) & 0x0F)); - nxt6000_writereg(fe, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); - nxt6000_writereg(fe, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); - nxt6000_writereg(fe, OFDM_ITB_FREQ_1, 0x06); - nxt6000_writereg(fe, OFDM_ITB_FREQ_2, 0x31); - nxt6000_writereg(fe, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); - nxt6000_writereg(fe, CAS_FREQ, 0xBB); /* CHECKME */ - nxt6000_writereg(fe, OFDM_SYR_CTL, 1 << 2); - nxt6000_writereg(fe, OFDM_PPM_CTL_1, PPM256); - nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, 0x49); - nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, 0x72); - nxt6000_writereg(fe, ANALOG_CONTROL_0, 1 << 5); - nxt6000_writereg(fe, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); - nxt6000_writereg(fe, DIAG_CONFIG, TB_SET); - - if (fe->clock_inversion) - nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, CLKINVERSION); + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; + + nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); + nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01); + nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC); + nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); + nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); + nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); + nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06); + nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31); + nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); + nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */ + nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2); + nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72); + nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5); + nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); + nxt6000_writereg(state, DIAG_CONFIG, TB_SET); + + if (state->config->clock_inversion) + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); else - nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, 0); + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); + + nxt6000_writereg(state, TS_FORMAT, 0); - nxt6000_writereg(fe, TS_FORMAT, 0); + if (state->config->pll_init) { + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ + state->config->pll_init(fe); + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ + } } -static void nxt6000_dump_status(struct nxt6000_config *fe) +static void nxt6000_dump_status(struct nxt6000_state *state) { u8 val; @@ -400,12 +240,12 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) */ printk("NXT6000 status:"); - val = nxt6000_readreg(fe, RS_COR_STAT); + val = nxt6000_readreg(state, RS_COR_STAT); printk(" DATA DESCR LOCK: %d,", val & 0x01); printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01); - val = nxt6000_readreg(fe, VIT_SYNC_STATUS); + val = nxt6000_readreg(state, VIT_SYNC_STATUS); printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01); @@ -443,7 +283,7 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) } - val = nxt6000_readreg(fe, OFDM_COR_STAT); + val = nxt6000_readreg(state, OFDM_COR_STAT); printk(" CHCTrack: %d,", (val >> 7) & 0x01); printk(" TPSLock: %d,", (val >> 6) & 0x01); @@ -496,7 +336,7 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) } - val = nxt6000_readreg(fe, OFDM_SYR_STAT); + val = nxt6000_readreg(state, OFDM_SYR_STAT); printk(" SYRLock: %d,", (val >> 4) & 0x01); printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K"); @@ -522,14 +362,11 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) break; case 0x03: - printk(" SYRGuard: 1/4,"); - break; - } - val = nxt6000_readreg(fe, OFDM_TPS_RCVD_3); + val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); switch((val >> 4) & 0x07) { @@ -599,7 +436,7 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) } - val = nxt6000_readreg(fe, OFDM_TPS_RCVD_4); + val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K"); @@ -632,299 +469,156 @@ static void nxt6000_dump_status(struct nxt6000_config *fe) } /* Strange magic required to gain access to RF_AGC_STATUS */ - nxt6000_readreg(fe, RF_AGC_VAL_1); - val = nxt6000_readreg(fe, RF_AGC_STATUS); - val = nxt6000_readreg(fe, RF_AGC_STATUS); + nxt6000_readreg(state, RF_AGC_VAL_1); + val = nxt6000_readreg(state, RF_AGC_STATUS); + val = nxt6000_readreg(state, RF_AGC_STATUS); printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01); printk("\n"); } -static int nxt6000_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg) -{ - struct nxt6000_config *fe = (struct nxt6000_config *) f->data; - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &nxt6000_info, sizeof (struct dvb_frontend_info)); - return 0; - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *)arg; + + + + +static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ u8 core_status; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; *status = 0; - core_status = nxt6000_readreg(fe, OFDM_COR_STAT); + core_status = nxt6000_readreg(state, OFDM_COR_STAT); if (core_status & AGCLOCKED) *status |= FE_HAS_SIGNAL; - if (nxt6000_readreg(fe, OFDM_SYR_STAT) & GI14_SYR_LOCK) + if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) *status |= FE_HAS_CARRIER; - if (nxt6000_readreg(fe, VIT_SYNC_STATUS) & VITINSYNC) + if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) *status |= FE_HAS_VITERBI; - if (nxt6000_readreg(fe, RS_COR_STAT) & RSCORESTATUS) + if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS) *status |= FE_HAS_SYNC; if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))) *status |= FE_HAS_LOCK; if (debug) - nxt6000_dump_status(fe); + nxt6000_dump_status(state); return 0; - } - case FE_READ_BER: +static int nxt6000_init(struct dvb_frontend* fe) { - u32 *ber = (u32 *)arg; - - *ber=0; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; - return 0; - - } + nxt6000_reset(state); + nxt6000_setup(fe); - case FE_READ_SIGNAL_STRENGTH: - { - s16 *signal = (s16 *) arg; -/* - *signal=(((signed char)readreg(client, 0x16))+128)<<8; -*/ - *signal = 0; return 0; - - } - - case FE_READ_SNR: - { - s16 *snr = (s16 *) arg; -/* - *snr=readreg(client, 0x24)<<8; - *snr|=readreg(client, 0x25); -*/ - *snr = 0; - break; - } - - case FE_READ_UNCORRECTED_BLOCKS: - { - u32 *ublocks = (u32 *)arg; - - *ublocks = 0; - - break; } - case FE_INIT: - nxt6000_reset(fe); - nxt6000_setup(fe); - break; - case FE_SET_FRONTEND: +static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { - struct dvb_frontend_parameters *param = (struct dvb_frontend_parameters *)arg; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; int result; - switch (fe->tuner_type) { - - case TUNER_TYPE_ALP510: - if ((result = alp510_set_tv_freq(fe, param->frequency)) < 0) - return result; - break; + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ + state->config->pll_set(fe, param); + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - case TUNER_TYPE_SP5659: - - if ((result = sp5659_set_tv_freq(fe, param->frequency)) < 0) + if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0) return result; - - break; - - case TUNER_TYPE_SP5730: - - if ((result = sp5730_set_tv_freq(fe, param->frequency)) < 0) - return result; - - break; - - default: - - return -EFAULT; - - } - - if ((result = nxt6000_set_bandwidth(fe, param->u.ofdm.bandwidth)) < 0) + if ((result = nxt6000_set_guard_interval(state, param->u.ofdm.guard_interval)) < 0) return result; - if ((result = nxt6000_set_guard_interval(fe, param->u.ofdm.guard_interval)) < 0) + if ((result = nxt6000_set_transmission_mode(state, param->u.ofdm.transmission_mode)) < 0) return result; - if ((result = nxt6000_set_transmission_mode(fe, param->u.ofdm.transmission_mode)) < 0) + if ((result = nxt6000_set_inversion(state, param->inversion)) < 0) return result; - if ((result = nxt6000_set_inversion(fe, param->inversion)) < 0) - return result; - - break; - } - - default: - - return -EOPNOTSUPP; - - } return 0; - } -static u8 demod_addr_tbl[] = {0x14, 0x18, 0x24, 0x28}; -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static void nxt6000_release(struct dvb_frontend* fe) { - struct i2c_client *client; - struct nxt6000_config *nxt; - u8 addr_nr; - int ret; - - if ((nxt = kmalloc(sizeof(struct nxt6000_config), GFP_KERNEL)) == NULL) - return -ENOMEM; - - memset(nxt, 0, sizeof(*nxt)); - nxt->i2c = adapter; - - for (addr_nr = 0; addr_nr < sizeof(demod_addr_tbl); addr_nr++) { - - if (nxt6000_read(adapter, demod_addr_tbl[addr_nr], OFDM_MSC_REV) != NXT6000ASICDEVICE) - continue; - - if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 0) { - nxt->tuner_addr = 0xC0; - nxt->tuner_type = TUNER_TYPE_ALP510; - nxt->clock_inversion = 1; - - dprintk("nxt6000: detected TI ALP510 tuner at 0x%02X\n", nxt->tuner_addr); - - } else if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC2) == 0) { - nxt->tuner_addr = 0xC2; - nxt->tuner_type = TUNER_TYPE_SP5659; - nxt->clock_inversion = 0; - - dprintk("nxt6000: detected MITEL SP5659 tuner at 0x%02X\n", nxt->tuner_addr); - - } else if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 0) { - nxt->tuner_addr = 0xC0; - nxt->tuner_type = TUNER_TYPE_SP5730; - nxt->clock_inversion = 0; - - dprintk("nxt6000: detected SP5730 tuner at 0x%02X\n", nxt->tuner_addr); - - } else { - printk("nxt6000: unable to detect tuner\n"); - continue; - } + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; + kfree(state); } - if (addr_nr == sizeof(demod_addr_tbl)) { - kfree(nxt); - return -ENODEV; - } +static struct dvb_frontend_ops nxt6000_ops; - nxt->demod_addr = demod_addr_tbl[addr_nr]; +struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c) +{ + struct nxt6000_state* state = NULL; - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(nxt); - return -ENOMEM; -} + /* allocate memory for the internal state */ + state = (struct nxt6000_state*) kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + if (state == NULL) goto error; - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = demod_addr_tbl[addr_nr]; - i2c_set_clientdata(client, (void *) nxt); + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); - ret = i2c_attach_client(client); - if (ret) - goto out; + /* check if the demod is there */ + if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; - BUG_ON(!nxt->dvb); + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - ret = dvb_register_frontend(nxt6000_ioctl, nxt->dvb, nxt, &nxt6000_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - goto out; +error: + if (state) kfree(state); + return NULL; } - ret = 0; -out: - kfree(client); - kfree(nxt); - return ret; -} - -static int detach_client(struct i2c_client *client) -{ - struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client); - dvb_unregister_frontend(nxt6000_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client); - - switch (cmd) { - case FE_REGISTER:{ - state->dvb = (struct dvb_adapter *) arg; - break; - } - case FE_UNREGISTER:{ - state->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "nxt6000", - .id = I2C_DRIVERID_DVBFE_NXT6000, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - I2C_DEVNAME("nxt6000"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, +static struct dvb_frontend_ops nxt6000_ops = { + + .info = { + .name = "NxtWave NXT6000 DVB-T", + .type = FE_OFDM, + .frequency_min = 0, + .frequency_max = 863250000, + .frequency_stepsize = 62500, + /*.frequency_tolerance = *//* FIXME: 12% of SR */ + .symbol_rate_min = 0, /* FIXME */ + .symbol_rate_max = 9360000, /* FIXME */ + .symbol_rate_tolerance = 4000, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = nxt6000_release, + + .init = nxt6000_init, + + .set_frontend = nxt6000_set_frontend, + + .read_status = nxt6000_read_status, }; -static __init int nxt6000_init(void) -{ - return i2c_add_driver(&driver); -} +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -static __exit void nxt6000_exit(void) -{ - if (i2c_del_driver(&driver)) - printk("nxt6000: driver deregistration failed\n"); -} +MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); +MODULE_AUTHOR("Florian Schirmer"); +MODULE_LICENSE("GPL"); -module_init(nxt6000_init); -module_exit(nxt6000_exit); +EXPORT_SYMBOL(nxt6000_attach); diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h index b47d25f91872..b7d9bead3002 100644 --- a/drivers/media/dvb/frontends/nxt6000.h +++ b/drivers/media/dvb/frontends/nxt6000.h @@ -1,266 +1,43 @@ /* - * Public Include File for DRV6000 users - * (ie. NxtWave Communications - NXT6000 demodulator driver) - * - * Copyright (C) 2001 NxtWave Communications, Inc. - * - */ + NxtWave Communications - NXT6000 demodulator driver -/* Nxt6000 Register Addresses and Bit Masks */ + Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org> + Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au> -/* Maximum Register Number */ -#define MAXNXT6000REG (0x9A) + 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. -/* 0x1B A_VIT_BER_0 aka 0x3A */ -#define A_VIT_BER_0 (0x1B) + 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. -/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */ -#define A_VIT_BER_TIMER_0 (0x1D) + 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. +*/ -/* 0x21 RS_COR_STAT */ -#define RS_COR_STAT (0x21) -#define RSCORESTATUS (0x03) +#ifndef NXT6000_H +#define NXT6000_H -/* 0x22 RS_COR_INTEN */ -#define RS_COR_INTEN (0x22) +#include <linux/dvb/frontend.h> -/* 0x23 RS_COR_INSTAT */ -#define RS_COR_INSTAT (0x23) -#define INSTAT_ERROR (0x04) -#define LOCK_LOSS_BITS (0x03) +struct nxt6000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; -/* 0x24 RS_COR_SYNC_PARAM */ -#define RS_COR_SYNC_PARAM (0x24) -#define SYNC_PARAM (0x03) + /* should clock inversion be used? */ + u8 clock_inversion:1; -/* 0x25 BER_CTRL */ -#define BER_CTRL (0x25) -#define BER_ENABLE (0x02) -#define BER_RESET (0x01) + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; -/* 0x26 BER_PAY */ -#define BER_PAY (0x26) - -/* 0x27 BER_PKT_L */ -#define BER_PKT_L (0x27) -#define BER_PKTOVERFLOW (0x80) - -/* 0x30 VIT_COR_CTL */ -#define VIT_COR_CTL (0x30) -#define BER_CONTROL (0x02) -#define VIT_COR_MASK (0x82) -#define VIT_COR_RESYNC (0x80) - - -/* 0x32 VIT_SYNC_STATUS */ -#define VIT_SYNC_STATUS (0x32) -#define VITINSYNC (0x80) - -/* 0x33 VIT_COR_INTEN */ -#define VIT_COR_INTEN (0x33) -#define GLOBAL_ENABLE (0x80) - -/* 0x34 VIT_COR_INTSTAT */ -#define VIT_COR_INTSTAT (0x34) -#define BER_DONE (0x08) -#define BER_OVERFLOW (0x10) - -/* 0x38 OFDM_BERTimer */ /* Use the alias registers */ -#define A_VIT_BER_TIMER_0 (0x1D) - -/* 0x3A VIT_BER_TIMER_0 */ /* Use the alias registers */ -#define A_VIT_BER_0 (0x1B) - -/* 0x40 OFDM_COR_CTL */ -#define OFDM_COR_CTL (0x40) -#define COREACT (0x20) -#define HOLDSM (0x10) -#define WAIT_AGC (0x02) -#define WAIT_SYR (0x03) - -/* 0x41 OFDM_COR_STAT */ -#define OFDM_COR_STAT (0x41) -#define COR_STATUS (0x0F) -#define MONITOR_TPS (0x06) -#define TPSLOCKED (0x40) -#define AGCLOCKED (0x10) - -/* 0x42 OFDM_COR_INTEN */ -#define OFDM_COR_INTEN (0x42) -#define TPSRCVBAD (0x04) -#define TPSRCVCHANGED (0x02) -#define TPSRCVUPDATE (0x01) - -/* 0x43 OFDM_COR_INSTAT */ -#define OFDM_COR_INSTAT (0x43) - -/* 0x44 OFDM_COR_MODEGUARD */ -#define OFDM_COR_MODEGUARD (0x44) -#define FORCEMODE (0x08) -#define FORCEMODE8K (0x04) - -/* 0x45 OFDM_AGC_CTL */ -#define OFDM_AGC_CTL (0x45) -#define INITIAL_AGC_BW (0x08) -#define AGCNEG (0x02) -#define AGCLAST (0x10) - -/* 0x48 OFDM_AGC_TARGET */ -#define OFDM_AGC_TARGET (0x48) -#define OFDM_AGC_TARGET_DEFAULT (0x28) -#define OFDM_AGC_TARGET_IMPULSE (0x38) - -/* 0x49 OFDM_AGC_GAIN_1 */ -#define OFDM_AGC_GAIN_1 (0x49) - -/* 0x4B OFDM_ITB_CTL */ -#define OFDM_ITB_CTL (0x4B) -#define ITBINV (0x01) - -/* 0x4C OFDM_ITB_FREQ_1 */ -#define OFDM_ITB_FREQ_1 (0x4C) - -/* 0x4D OFDM_ITB_FREQ_2 */ -#define OFDM_ITB_FREQ_2 (0x4D) - -/* 0x4E OFDM_CAS_CTL */ -#define OFDM_CAS_CTL (0x4E) -#define ACSDIS (0x40) -#define CCSEN (0x80) - -/* 0x4F CAS_FREQ */ -#define CAS_FREQ (0x4F) - -/* 0x51 OFDM_SYR_CTL */ -#define OFDM_SYR_CTL (0x51) -#define SIXTH_ENABLE (0x80) -#define SYR_TRACKING_DISABLE (0x01) - -/* 0x52 OFDM_SYR_STAT */ -#define OFDM_SYR_STAT (0x52) -#define GI14_2K_SYR_LOCK (0x13) -#define GI14_8K_SYR_LOCK (0x17) -#define GI14_SYR_LOCK (0x10) - -/* 0x55 OFDM_SYR_OFFSET_1 */ -#define OFDM_SYR_OFFSET_1 (0x55) - -/* 0x56 OFDM_SYR_OFFSET_2 */ -#define OFDM_SYR_OFFSET_2 (0x56) - -/* 0x58 OFDM_SCR_CTL */ -#define OFDM_SCR_CTL (0x58) -#define SYR_ADJ_DECAY_MASK (0x70) -#define SYR_ADJ_DECAY (0x30) - -/* 0x59 OFDM_PPM_CTL_1 */ -#define OFDM_PPM_CTL_1 (0x59) -#define PPMMAX_MASK (0x30) -#define PPM256 (0x30) - -/* 0x5B OFDM_TRL_NOMINALRATE_1 */ -#define OFDM_TRL_NOMINALRATE_1 (0x5B) - -/* 0x5C OFDM_TRL_NOMINALRATE_2 */ -#define OFDM_TRL_NOMINALRATE_2 (0x5C) - -/* 0x5D OFDM_TRL_TIME_1 */ -#define OFDM_TRL_TIME_1 (0x5D) - -/* 0x60 OFDM_CRL_FREQ_1 */ -#define OFDM_CRL_FREQ_1 (0x60) - -/* 0x63 OFDM_CHC_CTL_1 */ -#define OFDM_CHC_CTL_1 (0x63) -#define MANMEAN1 (0xF0); -#define CHCFIR (0x01) - -/* 0x64 OFDM_CHC_SNR */ -#define OFDM_CHC_SNR (0x64) - -/* 0x65 OFDM_BDI_CTL */ -#define OFDM_BDI_CTL (0x65) -#define LP_SELECT (0x02) - -/* 0x67 OFDM_TPS_RCVD_1 */ -#define OFDM_TPS_RCVD_1 (0x67) -#define TPSFRAME (0x03) - -/* 0x68 OFDM_TPS_RCVD_2 */ -#define OFDM_TPS_RCVD_2 (0x68) - -/* 0x69 OFDM_TPS_RCVD_3 */ -#define OFDM_TPS_RCVD_3 (0x69) - -/* 0x6A OFDM_TPS_RCVD_4 */ -#define OFDM_TPS_RCVD_4 (0x6A) - -/* 0x6B OFDM_TPS_RESERVED_1 */ -#define OFDM_TPS_RESERVED_1 (0x6B) - -/* 0x6C OFDM_TPS_RESERVED_2 */ -#define OFDM_TPS_RESERVED_2 (0x6C) - -/* 0x73 OFDM_MSC_REV */ -#define OFDM_MSC_REV (0x73) - -/* 0x76 OFDM_SNR_CARRIER_2 */ -#define OFDM_SNR_CARRIER_2 (0x76) -#define MEAN_MASK (0x80) -#define MEANBIT (0x80) - -/* 0x80 ANALOG_CONTROL_0 */ -#define ANALOG_CONTROL_0 (0x80) -#define POWER_DOWN_ADC (0x40) - -/* 0x81 ENABLE_TUNER_IIC */ -#define ENABLE_TUNER_IIC (0x81) -#define ENABLE_TUNER_BIT (0x01) - -/* 0x82 EN_DMD_RACQ */ -#define EN_DMD_RACQ (0x82) -#define EN_DMD_RACQ_REG_VAL (0x81) -#define EN_DMD_RACQ_REG_VAL_14 (0x01) - -/* 0x84 SNR_COMMAND */ -#define SNR_COMMAND (0x84) -#define SNRStat (0x80) - -/* 0x85 SNRCARRIERNUMBER_LSB */ -#define SNRCARRIERNUMBER_LSB (0x85) - -/* 0x87 SNRMINTHRESHOLD_LSB */ -#define SNRMINTHRESHOLD_LSB (0x87) - -/* 0x89 SNR_PER_CARRIER_LSB */ -#define SNR_PER_CARRIER_LSB (0x89) - -/* 0x8B SNRBELOWTHRESHOLD_LSB */ -#define SNRBELOWTHRESHOLD_LSB (0x8B) - -/* 0x91 RF_AGC_VAL_1 */ -#define RF_AGC_VAL_1 (0x91) - -/* 0x92 RF_AGC_STATUS */ -#define RF_AGC_STATUS (0x92) - -/* 0x98 DIAG_CONFIG */ -#define DIAG_CONFIG (0x98) -#define DIAG_MASK (0x70) -#define TB_SET (0x10) -#define TRAN_SELECT (0x07) -#define SERIAL_SELECT (0x01) - -/* 0x99 SUB_DIAG_MODE_SEL */ -#define SUB_DIAG_MODE_SEL (0x99) -#define CLKINVERSION (0x01) - -/* 0x9A TS_FORMAT */ -#define TS_FORMAT (0x9A) -#define ERROR_SENSE (0x08) -#define VALID_SENSE (0x04) -#define SYNC_SENSE (0x02) -#define GATED_CLOCK (0x01) - -#define NXT6000ASICDEVICE (0x0b) +extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c); +#endif // NXT6000_H diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb/frontends/nxt6000_priv.h new file mode 100644 index 000000000000..64b1a89b2a22 --- /dev/null +++ b/drivers/media/dvb/frontends/nxt6000_priv.h @@ -0,0 +1,265 @@ +/* + * Public Include File for DRV6000 users + * (ie. NxtWave Communications - NXT6000 demodulator driver) + * + * Copyright (C) 2001 NxtWave Communications, Inc. + * + */ + +/* Nxt6000 Register Addresses and Bit Masks */ + +/* Maximum Register Number */ +#define MAXNXT6000REG (0x9A) + +/* 0x1B A_VIT_BER_0 aka 0x3A */ +#define A_VIT_BER_0 (0x1B) + +/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */ +#define A_VIT_BER_TIMER_0 (0x1D) + +/* 0x21 RS_COR_STAT */ +#define RS_COR_STAT (0x21) +#define RSCORESTATUS (0x03) + +/* 0x22 RS_COR_INTEN */ +#define RS_COR_INTEN (0x22) + +/* 0x23 RS_COR_INSTAT */ +#define RS_COR_INSTAT (0x23) +#define INSTAT_ERROR (0x04) +#define LOCK_LOSS_BITS (0x03) + +/* 0x24 RS_COR_SYNC_PARAM */ +#define RS_COR_SYNC_PARAM (0x24) +#define SYNC_PARAM (0x03) + +/* 0x25 BER_CTRL */ +#define BER_CTRL (0x25) +#define BER_ENABLE (0x02) +#define BER_RESET (0x01) + +/* 0x26 BER_PAY */ +#define BER_PAY (0x26) + +/* 0x27 BER_PKT_L */ +#define BER_PKT_L (0x27) +#define BER_PKTOVERFLOW (0x80) + +/* 0x30 VIT_COR_CTL */ +#define VIT_COR_CTL (0x30) +#define BER_CONTROL (0x02) +#define VIT_COR_MASK (0x82) +#define VIT_COR_RESYNC (0x80) + + +/* 0x32 VIT_SYNC_STATUS */ +#define VIT_SYNC_STATUS (0x32) +#define VITINSYNC (0x80) + +/* 0x33 VIT_COR_INTEN */ +#define VIT_COR_INTEN (0x33) +#define GLOBAL_ENABLE (0x80) + +/* 0x34 VIT_COR_INTSTAT */ +#define VIT_COR_INTSTAT (0x34) +#define BER_DONE (0x08) +#define BER_OVERFLOW (0x10) + + /* 0x38 OFDM_BERTimer *//* Use the alias registers */ +#define A_VIT_BER_TIMER_0 (0x1D) + + /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */ +#define A_VIT_BER_0 (0x1B) + +/* 0x40 OFDM_COR_CTL */ +#define OFDM_COR_CTL (0x40) +#define COREACT (0x20) +#define HOLDSM (0x10) +#define WAIT_AGC (0x02) +#define WAIT_SYR (0x03) + +/* 0x41 OFDM_COR_STAT */ +#define OFDM_COR_STAT (0x41) +#define COR_STATUS (0x0F) +#define MONITOR_TPS (0x06) +#define TPSLOCKED (0x40) +#define AGCLOCKED (0x10) + +/* 0x42 OFDM_COR_INTEN */ +#define OFDM_COR_INTEN (0x42) +#define TPSRCVBAD (0x04) +#define TPSRCVCHANGED (0x02) +#define TPSRCVUPDATE (0x01) + +/* 0x43 OFDM_COR_INSTAT */ +#define OFDM_COR_INSTAT (0x43) + +/* 0x44 OFDM_COR_MODEGUARD */ +#define OFDM_COR_MODEGUARD (0x44) +#define FORCEMODE (0x08) +#define FORCEMODE8K (0x04) + +/* 0x45 OFDM_AGC_CTL */ +#define OFDM_AGC_CTL (0x45) +#define INITIAL_AGC_BW (0x08) +#define AGCNEG (0x02) +#define AGCLAST (0x10) + +/* 0x48 OFDM_AGC_TARGET */ +#define OFDM_AGC_TARGET (0x48) +#define OFDM_AGC_TARGET_DEFAULT (0x28) +#define OFDM_AGC_TARGET_IMPULSE (0x38) + +/* 0x49 OFDM_AGC_GAIN_1 */ +#define OFDM_AGC_GAIN_1 (0x49) + +/* 0x4B OFDM_ITB_CTL */ +#define OFDM_ITB_CTL (0x4B) +#define ITBINV (0x01) + +/* 0x4C OFDM_ITB_FREQ_1 */ +#define OFDM_ITB_FREQ_1 (0x4C) + +/* 0x4D OFDM_ITB_FREQ_2 */ +#define OFDM_ITB_FREQ_2 (0x4D) + +/* 0x4E OFDM_CAS_CTL */ +#define OFDM_CAS_CTL (0x4E) +#define ACSDIS (0x40) +#define CCSEN (0x80) + +/* 0x4F CAS_FREQ */ +#define CAS_FREQ (0x4F) + +/* 0x51 OFDM_SYR_CTL */ +#define OFDM_SYR_CTL (0x51) +#define SIXTH_ENABLE (0x80) +#define SYR_TRACKING_DISABLE (0x01) + +/* 0x52 OFDM_SYR_STAT */ +#define OFDM_SYR_STAT (0x52) +#define GI14_2K_SYR_LOCK (0x13) +#define GI14_8K_SYR_LOCK (0x17) +#define GI14_SYR_LOCK (0x10) + +/* 0x55 OFDM_SYR_OFFSET_1 */ +#define OFDM_SYR_OFFSET_1 (0x55) + +/* 0x56 OFDM_SYR_OFFSET_2 */ +#define OFDM_SYR_OFFSET_2 (0x56) + +/* 0x58 OFDM_SCR_CTL */ +#define OFDM_SCR_CTL (0x58) +#define SYR_ADJ_DECAY_MASK (0x70) +#define SYR_ADJ_DECAY (0x30) + +/* 0x59 OFDM_PPM_CTL_1 */ +#define OFDM_PPM_CTL_1 (0x59) +#define PPMMAX_MASK (0x30) +#define PPM256 (0x30) + +/* 0x5B OFDM_TRL_NOMINALRATE_1 */ +#define OFDM_TRL_NOMINALRATE_1 (0x5B) + +/* 0x5C OFDM_TRL_NOMINALRATE_2 */ +#define OFDM_TRL_NOMINALRATE_2 (0x5C) + +/* 0x5D OFDM_TRL_TIME_1 */ +#define OFDM_TRL_TIME_1 (0x5D) + +/* 0x60 OFDM_CRL_FREQ_1 */ +#define OFDM_CRL_FREQ_1 (0x60) + +/* 0x63 OFDM_CHC_CTL_1 */ +#define OFDM_CHC_CTL_1 (0x63) +#define MANMEAN1 (0xF0); +#define CHCFIR (0x01) + +/* 0x64 OFDM_CHC_SNR */ +#define OFDM_CHC_SNR (0x64) + +/* 0x65 OFDM_BDI_CTL */ +#define OFDM_BDI_CTL (0x65) +#define LP_SELECT (0x02) + +/* 0x67 OFDM_TPS_RCVD_1 */ +#define OFDM_TPS_RCVD_1 (0x67) +#define TPSFRAME (0x03) + +/* 0x68 OFDM_TPS_RCVD_2 */ +#define OFDM_TPS_RCVD_2 (0x68) + +/* 0x69 OFDM_TPS_RCVD_3 */ +#define OFDM_TPS_RCVD_3 (0x69) + +/* 0x6A OFDM_TPS_RCVD_4 */ +#define OFDM_TPS_RCVD_4 (0x6A) + +/* 0x6B OFDM_TPS_RESERVED_1 */ +#define OFDM_TPS_RESERVED_1 (0x6B) + +/* 0x6C OFDM_TPS_RESERVED_2 */ +#define OFDM_TPS_RESERVED_2 (0x6C) + +/* 0x73 OFDM_MSC_REV */ +#define OFDM_MSC_REV (0x73) + +/* 0x76 OFDM_SNR_CARRIER_2 */ +#define OFDM_SNR_CARRIER_2 (0x76) +#define MEAN_MASK (0x80) +#define MEANBIT (0x80) + +/* 0x80 ANALOG_CONTROL_0 */ +#define ANALOG_CONTROL_0 (0x80) +#define POWER_DOWN_ADC (0x40) + +/* 0x81 ENABLE_TUNER_IIC */ +#define ENABLE_TUNER_IIC (0x81) +#define ENABLE_TUNER_BIT (0x01) + +/* 0x82 EN_DMD_RACQ */ +#define EN_DMD_RACQ (0x82) +#define EN_DMD_RACQ_REG_VAL (0x81) +#define EN_DMD_RACQ_REG_VAL_14 (0x01) + +/* 0x84 SNR_COMMAND */ +#define SNR_COMMAND (0x84) +#define SNRStat (0x80) + +/* 0x85 SNRCARRIERNUMBER_LSB */ +#define SNRCARRIERNUMBER_LSB (0x85) + +/* 0x87 SNRMINTHRESHOLD_LSB */ +#define SNRMINTHRESHOLD_LSB (0x87) + +/* 0x89 SNR_PER_CARRIER_LSB */ +#define SNR_PER_CARRIER_LSB (0x89) + +/* 0x8B SNRBELOWTHRESHOLD_LSB */ +#define SNRBELOWTHRESHOLD_LSB (0x8B) + +/* 0x91 RF_AGC_VAL_1 */ +#define RF_AGC_VAL_1 (0x91) + +/* 0x92 RF_AGC_STATUS */ +#define RF_AGC_STATUS (0x92) + +/* 0x98 DIAG_CONFIG */ +#define DIAG_CONFIG (0x98) +#define DIAG_MASK (0x70) +#define TB_SET (0x10) +#define TRAN_SELECT (0x07) +#define SERIAL_SELECT (0x01) + +/* 0x99 SUB_DIAG_MODE_SEL */ +#define SUB_DIAG_MODE_SEL (0x99) +#define CLKINVERSION (0x01) + +/* 0x9A TS_FORMAT */ +#define TS_FORMAT (0x9A) +#define ERROR_SENSE (0x08) +#define VALID_SENSE (0x04) +#define SYNC_SENSE (0x02) +#define GATED_CLOCK (0x01) + +#define NXT6000ASICDEVICE (0x0b) diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c new file mode 100644 index 000000000000..4a8178d56458 --- /dev/null +++ b/drivers/media/dvb/frontends/sp8870.c @@ -0,0 +1,613 @@ +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/* + * This driver needs external firmware. Please use the command + * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to + * download/extract it, and then copy it to /usr/lib/hotplug/firmware. + */ +#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/delay.h> + +#include "dvb_frontend.h" +#include "sp8870.h" + + +struct sp8870_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct sp8870_config* config; + + struct dvb_frontend frontend; + + /* demodulator private data */ + u8 initialised:1; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "sp8870: " args); \ + } while (0) + +/* firmware size for sp8870 */ +#define SP8870_FIRMWARE_SIZE 16382 + +/* starting point for firmware in file 'Sc_main.mc' */ +#define SP8870_FIRMWARE_OFFSET 0x0A + +static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) +{ + u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; + int err; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int sp8870_readreg (struct sp8870_state* state, u16 reg) +{ + int ret; + u8 b0 [] = { reg >> 8 , reg & 0xff }; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + return -1; + } + + return (b1[0] << 8 | b1[1]); +} + +static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) +{ + struct i2c_msg msg; + char *fw_buf = fw->data; + int fw_pos; + u8 tx_buf[255]; + int tx_len; + int err = 0; + + dprintk ("%s: ...\n", __FUNCTION__); + + if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) + return -EINVAL; + + // system controller stop + sp8870_writereg(state, 0x0F00, 0x0000); + + // instruction RAM register hiword + sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); + + // instruction RAM MWR + sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); + + // do firmware upload + fw_pos = SP8870_FIRMWARE_OFFSET; + while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ + tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; + // write register 0xCF0A + tx_buf[0] = 0xCF; + tx_buf[1] = 0x0A; + memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = tx_buf; + msg.len = tx_len + 2; + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + printk("%s: firmware upload failed!\n", __FUNCTION__); + printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); + return err; + } + fw_pos += tx_len; + } + + dprintk ("%s: done!\n", __FUNCTION__); + return 0; +}; + +static void sp8870_microcontroller_stop (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller STOP + sp8870_writereg(state, 0x0F00, 0x000); +} + +static void sp8870_microcontroller_start (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller START + sp8870_writereg(state, 0x0F00, 0x001); + // not documented but if we don't read 0x0D01 out here + // we don't get a correct data valid signal + sp8870_readreg(state, 0x0D01); +} + +static int sp8870_read_data_valid_signal(struct sp8870_state* state) +{ + return (sp8870_readreg(state, 0x0D02) > 0); +} + +static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05) +{ + int known_parameters = 1; + + *reg0xc05 = 0x000; + + switch (p->u.ofdm.constellation) { + case QPSK: + break; + case QAM_16: + *reg0xc05 |= (1 << 10); + break; + case QAM_64: + *reg0xc05 |= (2 << 10); + break; + case QAM_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + }; + + switch (p->u.ofdm.hierarchy_information) { + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + *reg0xc05 |= (1 << 7); + break; + case HIERARCHY_2: + *reg0xc05 |= (2 << 7); + break; + case HIERARCHY_4: + *reg0xc05 |= (3 << 7); + break; + case HIERARCHY_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + }; + + switch (p->u.ofdm.code_rate_HP) { + case FEC_1_2: + break; + case FEC_2_3: + *reg0xc05 |= (1 << 3); + break; + case FEC_3_4: + *reg0xc05 |= (2 << 3); + break; + case FEC_5_6: + *reg0xc05 |= (3 << 3); + break; + case FEC_7_8: + *reg0xc05 |= (4 << 3); + break; + case FEC_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + }; + + if (known_parameters) + *reg0xc05 |= (2 << 1); /* use specified parameters */ + else + *reg0xc05 |= (1 << 1); /* enable autoprobing */ + + return 0; +} + +static int sp8870_wake_up(struct sp8870_state* state) +{ + // enable TS output and interface pins + return sp8870_writereg(state, 0xC18, 0x00D); +} + +static int sp8870_set_frontend_parameters (struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int err; + u16 reg0xc05; + + if ((err = configure_reg0xc05(p, ®0xc05))) + return err; + + // system controller stop + sp8870_microcontroller_stop(state); + + // set tuner parameters + sp8870_writereg(state, 0x206, 0x001); + state->config->pll_set(fe, p); + sp8870_writereg(state, 0x206, 0x000); + + // sample rate correction bit [23..17] + sp8870_writereg(state, 0x0319, 0x000A); + + // sample rate correction bit [16..0] + sp8870_writereg(state, 0x031A, 0x0AAB); + + // integer carrier offset + sp8870_writereg(state, 0x0309, 0x0400); + + // fractional carrier offset + sp8870_writereg(state, 0x030A, 0x0000); + + // filter for 6/7/8 Mhz channel + if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) + sp8870_writereg(state, 0x0311, 0x0002); + else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + sp8870_writereg(state, 0x0311, 0x0001); + else + sp8870_writereg(state, 0x0311, 0x0000); + + // scan order: 2k first = 0x0000, 8k first = 0x0001 + if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) + sp8870_writereg(state, 0x0338, 0x0000); + else + sp8870_writereg(state, 0x0338, 0x0001); + + sp8870_writereg(state, 0xc05, reg0xc05); + + // read status reg in order to clear pending irqs + sp8870_readreg(state, 0x200); + + // system controller start + sp8870_microcontroller_start(state); + + return 0; +} + +static int sp8870_init (struct dvb_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + const struct firmware *fw = NULL; + + sp8870_wake_up(state); + if (state->initialised) return 0; + state->initialised = 1; + + dprintk ("%s\n", __FUNCTION__); + + + /* request the firmware, this will block until someone uploads it */ + printk("sp8870: waiting for firmware upload...\n"); + if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { + printk("sp8870: no firmware upload (timeout or file not found?)\n"); + release_firmware(fw); + return -EIO; + } + + if (sp8870_firmware_upload(state, fw)) { + printk("sp8870: writing firmware to device failed\n"); + release_firmware(fw); + return -EIO; + } + + /* enable TS output and interface pins */ + sp8870_writereg(state, 0xc18, 0x00d); + + // system controller stop + sp8870_microcontroller_stop(state); + + // ADC mode + sp8870_writereg(state, 0x0301, 0x0003); + + // Reed Solomon parity bytes passed to output + sp8870_writereg(state, 0x0C13, 0x0001); + + // MPEG clock is suppressed if no valid data + sp8870_writereg(state, 0x0C14, 0x0001); + + /* bit 0x010: enable data valid signal */ + sp8870_writereg(state, 0x0D00, 0x010); + sp8870_writereg(state, 0x0D01, 0x000); + + /* setup PLL */ + if (state->config->pll_init) { + sp8870_writereg(state, 0x206, 0x001); + state->config->pll_init(fe); + sp8870_writereg(state, 0x206, 0x000); + } + + return 0; +} + +static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int status; + int signal; + + *fe_status = 0; + + status = sp8870_readreg (state, 0x0200); + if (status < 0) + return -EIO; + + signal = sp8870_readreg (state, 0x0303); + if (signal < 0) + return -EIO; + + if (signal > 0x0F) + *fe_status |= FE_HAS_SIGNAL; + if (status & 0x08) + *fe_status |= FE_HAS_SYNC; + if (status & 0x04) + *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; + + return 0; +} + +static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + u32 tmp; + + *ber = 0; + + ret = sp8870_readreg(state, 0xC08); + if (ret < 0) + return -EIO; + + tmp = ret & 0x3F; + + ret = sp8870_readreg(state, 0xC07); + if (ret < 0) + return -EIO; + + tmp = ret << 6; + + if (tmp >= 0x3FFF0) + tmp = ~0; + + *ber = tmp; + + return 0; +} + +static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + u16 tmp; + + *signal = 0; + + ret = sp8870_readreg (state, 0x306); + if (ret < 0) + return -EIO; + + tmp = ret << 8; + + ret = sp8870_readreg (state, 0x303); + if (ret < 0) + return -EIO; + + tmp |= ret; + + if (tmp) + *signal = 0xFFFF - tmp; + + return 0; +} + +static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + + *ublocks = 0; + + ret = sp8870_readreg(state, 0xC0C); + if (ret < 0) + return -EIO; + + if (ret == 0xFFFF) + ret = ~0; + + *ublocks = ret; + + return 0; +} + +// number of trials to recover from lockup +#define MAXTRIALS 5 +// maximum checks for data valid signal +#define MAXCHECKS 100 + +// only for debugging: counter for detected lockups +static int lockups = 0; +// only for debugging: counter for channel switches +static int switches = 0; + +static int sp8870_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + + /* + The firmware of the sp8870 sometimes locks up after setting frontend parameters. + We try to detect this by checking the data valid signal. + If it is not set after MAXCHECKS we try to recover the lockup by setting + the frontend parameters again. + */ + + int err = 0; + int valid = 0; + int trials = 0; + int check_count = 0; + + dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency); + + for (trials = 1; trials <= MAXTRIALS; trials++) { + + if ((err = sp8870_set_frontend_parameters(fe, p))) + return err; + + for (check_count = 0; check_count < MAXCHECKS; check_count++) { +// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); + valid = sp8870_read_data_valid_signal(state); + if (valid) { + dprintk("%s: delay = %i usec\n", + __FUNCTION__, check_count * 10); + break; + } + udelay(10); + } + if (valid) + break; + } + + if (!valid) { + printk("%s: firmware crash!!!!!!\n", __FUNCTION__); + return -EIO; + } + + if (debug) { + if (valid) { + if (trials > 1) { + printk("%s: firmware lockup!!!\n", __FUNCTION__); + printk("%s: recovered after %i trial(s))\n", __FUNCTION__, trials - 1); + lockups++; + } + } + switches++; + printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups); + } + + return 0; +} + +static int sp8870_sleep(struct dvb_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + + // tristate TS output and disable interface pins + return sp8870_writereg(state, 0xC18, 0x000); +} + +static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 350; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void sp8870_release(struct dvb_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops sp8870_ops; + +struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c) +{ + struct sp8870_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + + /* check if the demod is there */ + if (sp8870_readreg(state, 0x0200) < 0) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops sp8870_ops = { + + .info = { + .name = "Spase SP8870 DVB-T", + .type = FE_OFDM, + .frequency_min = 470000000, + .frequency_max = 860000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER + }, + + .release = sp8870_release, + + .init = sp8870_init, + .sleep = sp8870_sleep, + + .set_frontend = sp8870_set_frontend, + .get_tune_settings = sp8870_get_tune_settings, + + .read_status = sp8870_read_status, + .read_ber = sp8870_read_ber, + .read_signal_strength = sp8870_read_signal_strength, + .read_ucblocks = sp8870_read_uncorrected_blocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); +MODULE_AUTHOR("Juergen Peitz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(sp8870_attach); diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h new file mode 100644 index 000000000000..f3b555dbc960 --- /dev/null +++ b/drivers/media/dvb/frontends/sp8870.h @@ -0,0 +1,45 @@ +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + 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 SP8870_H +#define SP8870_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct sp8870_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c); + +#endif // SP8870_H diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index 786aa3e8a7cd..ac2c86db472c 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -1,5 +1,5 @@ /* - Driver for the Microtune 7202D Frontend + Driver for the Spase sp887x demodulator */ /* @@ -16,74 +16,50 @@ #include <linux/firmware.h> #include "dvb_frontend.h" +#include "sp887x.h" -#define FRONTEND_NAME "dvbfe_sp887x" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct sp887x_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; -#if 0 -#define LOG(dir,addr,buf,len) \ - do { \ - int i; \ - printk("%s (%02x):", dir, addr & 0xff); \ - for (i=0; i<len; i++) \ - printk(" 0x%02x,", buf[i] & 0xff); \ - printk("\n"); \ - } while (0) -#else -#define LOG(dir,addr,buf,len) -#endif - -static struct dvb_frontend_info sp887x_info = { - .name = "Microtune MT7202DTF", - .type = FE_OFDM, - .frequency_min = 50500000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | - FE_CAN_RECOVER -}; + const struct sp887x_config* config; -struct sp887x_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; + struct dvb_frontend frontend; + + /* demodulator private data */ + u8 initialised:1; }; -static int i2c_writebytes (struct i2c_adapter *i2c, u8 addr, u8 *buf, u8 len) +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "sp887x: " args); \ + } while (0) + +static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len) { - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len }; int err; - LOG("i2c_writebytes", msg.addr, msg.buf, msg.len); - - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { printk ("%s: i2c write error (addr %02x, err == %i)\n", - __FUNCTION__, addr, err); + __FUNCTION__, state->config->demod_address, err); return -EREMOTEIO; } return 0; } -static int sp887x_writereg (struct i2c_adapter *i2c, u16 reg, u16 data) +static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data) { u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { .addr = 0x70, .flags = 0, .buf = b0, .len = 4 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 }; int ret; - LOG("sp887x_writereg", msg.addr, msg.buf, msg.len); - - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) { + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { /** * in case of soft reset we ignore ACK errors... */ @@ -100,61 +76,60 @@ static int sp887x_writereg (struct i2c_adapter *i2c, u16 reg, u16 data) return 0; } -static u16 sp887x_readreg (struct i2c_adapter *i2c, u16 reg) +static int sp887x_readreg (struct sp887x_state* state, u16 reg) { u8 b0 [] = { reg >> 8 , reg & 0xff }; u8 b1 [2]; int ret; - struct i2c_msg msg[] = {{ .addr = 0x70, .flags = 0, .buf = b0, .len = 2 }, - { .addr = 0x70, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; + struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; - LOG("sp887x_readreg (w)", msg[0].addr, msg[0].buf, msg[0].len); - LOG("sp887x_readreg (r)", msg[1].addr, msg[1].buf, msg[1].len); - - if ((ret = i2c_transfer(i2c, msg, 2)) != 2) + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + return -1; + } return (((b1[0] << 8) | b1[1]) & 0xfff); } -static void sp887x_microcontroller_stop (struct i2c_adapter *fe) +static void sp887x_microcontroller_stop (struct sp887x_state* state) { dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0xf08, 0x000); - sp887x_writereg(fe, 0xf09, 0x000); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); /* microcontroller STOP */ - sp887x_writereg(fe, 0xf00, 0x000); + sp887x_writereg(state, 0xf00, 0x000); } -static void sp887x_microcontroller_start (struct i2c_adapter *fe) +static void sp887x_microcontroller_start (struct sp887x_state* state) { dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0xf08, 0x000); - sp887x_writereg(fe, 0xf09, 0x000); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); /* microcontroller START */ - sp887x_writereg(fe, 0xf00, 0x001); + sp887x_writereg(state, 0xf00, 0x001); } -static void sp887x_setup_agc (struct i2c_adapter *fe) +static void sp887x_setup_agc (struct sp887x_state* state) { /* setup AGC parameters */ dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0x33c, 0x054); - sp887x_writereg(fe, 0x33b, 0x04c); - sp887x_writereg(fe, 0x328, 0x000); - sp887x_writereg(fe, 0x327, 0x005); - sp887x_writereg(fe, 0x326, 0x001); - sp887x_writereg(fe, 0x325, 0x001); - sp887x_writereg(fe, 0x324, 0x001); - sp887x_writereg(fe, 0x318, 0x050); - sp887x_writereg(fe, 0x317, 0x3fe); - sp887x_writereg(fe, 0x316, 0x001); - sp887x_writereg(fe, 0x313, 0x005); - sp887x_writereg(fe, 0x312, 0x002); - sp887x_writereg(fe, 0x306, 0x000); - sp887x_writereg(fe, 0x303, 0x000); + sp887x_writereg(state, 0x33c, 0x054); + sp887x_writereg(state, 0x33b, 0x04c); + sp887x_writereg(state, 0x328, 0x000); + sp887x_writereg(state, 0x327, 0x005); + sp887x_writereg(state, 0x326, 0x001); + sp887x_writereg(state, 0x325, 0x001); + sp887x_writereg(state, 0x324, 0x001); + sp887x_writereg(state, 0x318, 0x050); + sp887x_writereg(state, 0x317, 0x3fe); + sp887x_writereg(state, 0x316, 0x001); + sp887x_writereg(state, 0x313, 0x005); + sp887x_writereg(state, 0x312, 0x002); + sp887x_writereg(state, 0x306, 0x000); + sp887x_writereg(state, 0x303, 0x000); } #define BLOCKSIZE 30 @@ -162,8 +137,9 @@ static void sp887x_setup_agc (struct i2c_adapter *fe) /** * load firmware and setup MPEG interface... */ -static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware *fw) +static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) { + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; u8 buf [BLOCKSIZE+2]; int i; int fw_size = fw->size; @@ -178,18 +154,18 @@ static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware * mem = fw->data + 10; /* soft reset */ - sp887x_writereg(fe, 0xf1a, 0x000); + sp887x_writereg(state, 0xf1a, 0x000); - sp887x_microcontroller_stop (fe); + sp887x_microcontroller_stop (state); printk ("%s: firmware upload... ", __FUNCTION__); /* setup write pointer to -1 (end of memory) */ /* bit 0x8000 in address is set to enable 13bit mode */ - sp887x_writereg(fe, 0x8f08, 0x1fff); + sp887x_writereg(state, 0x8f08, 0x1fff); /* dummy write (wrap around to start of memory) */ - sp887x_writereg(fe, 0x8f0a, 0x0000); + sp887x_writereg(state, 0x8f0a, 0x0000); for (i = 0; i < FW_SIZE; i += BLOCKSIZE) { int c = BLOCKSIZE; @@ -206,7 +182,7 @@ static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware * memcpy(&buf[2], mem + i, c); - if ((err = i2c_writebytes (fe, 0x70, buf, c+2)) < 0) { + if ((err = i2c_writebytes (state, buf, c+2)) < 0) { printk ("failed.\n"); printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); return err; @@ -214,61 +190,37 @@ static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware * } /* don't write RS bytes between packets */ - sp887x_writereg(fe, 0xc13, 0x001); + sp887x_writereg(state, 0xc13, 0x001); /* suppress clock if (!data_valid) */ - sp887x_writereg(fe, 0xc14, 0x000); + sp887x_writereg(state, 0xc14, 0x000); /* setup MPEG interface... */ - sp887x_writereg(fe, 0xc1a, 0x872); - sp887x_writereg(fe, 0xc1b, 0x001); - sp887x_writereg(fe, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ - sp887x_writereg(fe, 0xc1a, 0x871); + sp887x_writereg(state, 0xc1a, 0x872); + sp887x_writereg(state, 0xc1b, 0x001); + sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ + sp887x_writereg(state, 0xc1a, 0x871); /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */ - sp887x_writereg(fe, 0x301, 0x002); + sp887x_writereg(state, 0x301, 0x002); - sp887x_setup_agc(fe); + sp887x_setup_agc(state); /* bit 0x010: enable data valid signal */ - sp887x_writereg(fe, 0xd00, 0x010); - sp887x_writereg(fe, 0x0d1, 0x000); + sp887x_writereg(state, 0xd00, 0x010); + sp887x_writereg(state, 0x0d1, 0x000); + + /* setup the PLL */ + if (state->config->pll_init) { + sp887x_writereg(state, 0x206, 0x001); + state->config->pll_init(fe); + sp887x_writereg(state, 0x206, 0x000); + } printk ("done.\n"); return 0; }; -/** - * returns the actual tuned center frequency which can be used - * to initialise the AFC registers - */ -static int tsa5060_setup_pll (struct i2c_adapter *fe, int freq) -{ - u8 cfg, cpump, band_select; - u8 buf [4]; - u32 div; - - div = (36000000 + freq + 83333) / 166666; - cfg = 0x88; - - cpump = freq < 175000000 ? 2 : freq < 390000000 ? 1 : - freq < 470000000 ? 2 : freq < 750000000 ? 2 : 3; - - band_select = freq < 175000000 ? 0x0e : freq < 470000000 ? 0x05 : 0x03; - - buf [0] = (div >> 8) & 0x7f; - buf [1] = div & 0xff; - buf [2] = ((div >> 10) & 0x60) | cfg; - buf [3] = cpump | band_select; - - /* open i2c gate for PLL message transmission... */ - sp887x_writereg(fe, 0x206, 0x001); - i2c_writebytes(fe, 0x60, buf, 4); - sp887x_writereg(fe, 0x206, 0x000); - - return (div * 166666 - 36000000); -} - static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05) { int known_parameters = 1; @@ -362,7 +314,7 @@ static void divide (int n, int d, int *quotient_i, int *quotient_f) } } -static void sp887x_correct_offsets (struct i2c_adapter *fe, +static void sp887x_correct_offsets (struct sp887x_state* state, struct dvb_frontend_parameters *p, int actual_freq) { @@ -385,17 +337,31 @@ static void sp887x_correct_offsets (struct i2c_adapter *fe, frequency_shift = -frequency_shift; /* sample rate correction */ - sp887x_writereg(fe, 0x319, srate_correction[bw_index] >> 12); - sp887x_writereg(fe, 0x31a, srate_correction[bw_index] & 0xfff); + sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12); + sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff); /* carrier offset correction */ - sp887x_writereg(fe, 0x309, frequency_shift >> 12); - sp887x_writereg(fe, 0x30a, frequency_shift & 0xfff); + sp887x_writereg(state, 0x309, frequency_shift >> 12); + sp887x_writereg(state, 0x30a, frequency_shift & 0xfff); } -static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe, + + + + + + + + + + + + + +static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; int actual_freq, err; u16 val, reg0xc05; @@ -407,14 +373,17 @@ static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe, if ((err = configure_reg0xc05(p, ®0xc05))) return err; - sp887x_microcontroller_stop(fe); + sp887x_microcontroller_stop(state); - actual_freq = tsa5060_setup_pll(fe, p->frequency); + /* setup the PLL */ + sp887x_writereg(state, 0x206, 0x001); + actual_freq = state->config->pll_set(fe, p); + sp887x_writereg(state, 0x206, 0x000); - /* read status reg in order to clear pending irqs */ - sp887x_readreg(fe, 0x200); + /* read status reg in order to clear <pending irqs */ + sp887x_readreg(state, 0x200); - sp887x_correct_offsets(fe, p, actual_freq); + sp887x_correct_offsets(state, p, actual_freq); /* filter for 6/7/8 Mhz channel */ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) @@ -424,15 +393,15 @@ static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe, else val = 0; - sp887x_writereg(fe, 0x311, val); + sp887x_writereg(state, 0x311, val); /* scan order: 2k first = 0, 8k first = 1 */ if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) - sp887x_writereg(fe, 0x338, 0x000); + sp887x_writereg(state, 0x338, 0x000); else - sp887x_writereg(fe, 0x338, 0x001); + sp887x_writereg(state, 0x338, 0x001); - sp887x_writereg(fe, 0xc05, reg0xc05); + sp887x_writereg(state, 0xc05, reg0xc05); if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) val = 2 << 3; @@ -444,29 +413,19 @@ static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe, /* enable OFDM and SAW bits as lock indicators in sync register 0xf17, * optimize algorithm for given bandwidth... */ - sp887x_writereg(fe, 0xf14, 0x160 | val); - sp887x_writereg(fe, 0xf15, 0x000); + sp887x_writereg(state, 0xf14, 0x160 | val); + sp887x_writereg(state, 0xf15, 0x000); - sp887x_microcontroller_start(fe); + sp887x_microcontroller_start(state); return 0; } -static int sp887x_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg) -{ - struct sp887x_state *state = (struct sp887x_state *) f->data; - struct i2c_adapter *fe = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &sp887x_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: +static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status) { - u16 snr12 = sp887x_readreg(fe, 0xf16); - u16 sync0x200 = sp887x_readreg(fe, 0x200); - u16 sync0xf17 = sp887x_readreg(fe, 0xf17); - fe_status_t *status = arg; + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + u16 snr12 = sp887x_readreg(state, 0xf16); + u16 sync0x200 = sp887x_readreg(state, 0x200); + u16 sync0xf17 = sp887x_readreg(state, 0xf17); *status = 0; @@ -492,212 +451,172 @@ static int sp887x_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg) steps); } - break; - + return 0; } - case FE_READ_BER: +static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber) { - u32* ber = arg; - *ber = (sp887x_readreg(fe, 0xc08) & 0x3f) | - (sp887x_readreg(fe, 0xc07) << 6); - sp887x_writereg(fe, 0xc08, 0x000); - sp887x_writereg(fe, 0xc07, 0x000); + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + *ber = (sp887x_readreg(state, 0xc08) & 0x3f) | + (sp887x_readreg(state, 0xc07) << 6); + sp887x_writereg(state, 0xc08, 0x000); + sp887x_writereg(state, 0xc07, 0x000); if (*ber >= 0x3fff0) *ber = ~0; - break; + return 0; } - case FE_READ_SIGNAL_STRENGTH: // FIXME: correct registers ? +static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u16 snr12 = sp887x_readreg(fe, 0xf16); + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + u16 snr12 = sp887x_readreg(state, 0xf16); u32 signal = 3 * (snr12 << 4); - *((u16*) arg) = (signal < 0xffff) ? signal : 0xffff; - break; - } + *strength = (signal < 0xffff) ? signal : 0xffff; - case FE_READ_SNR: - { - u16 snr12 = sp887x_readreg(fe, 0xf16); - *(u16*) arg = (snr12 << 4) | (snr12 >> 8); - break; + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: +static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr) { - u32 *ublocks = (u32 *) arg; - *ublocks = sp887x_readreg(fe, 0xc0c); - if (*ublocks == 0xfff) - *ublocks = ~0; - break; - } + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; - case FE_SET_FRONTEND: - return sp887x_setup_frontend_parameters(fe, arg); + u16 snr12 = sp887x_readreg(state, 0xf16); + *snr = (snr12 << 4) | (snr12 >> 8); - case FE_GET_FRONTEND: // FIXME: read known values back from Hardware... - break; - - case FE_SLEEP: - /* tristate TS output and disable interface pins */ - sp887x_writereg(fe, 0xc18, 0x000); - break; - - case FE_INIT: - /* enable TS output and interface pins */ - sp887x_writereg(fe, 0xc18, 0x00d); - break; - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - fesettings->min_delay_ms = 350; - fesettings->step_size = 166666*2; - fesettings->max_drift = (166666*2)+1; return 0; } - default: - return -EOPNOTSUPP; - }; +static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + *ucblocks = sp887x_readreg(state, 0xc0c); + if (*ucblocks == 0xfff) + *ucblocks = ~0; return 0; } -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static int sp887x_sleep(struct dvb_frontend* fe) { - struct i2c_client *client; - struct sp887x_state *state; - const struct firmware *fw; - int ret; - - struct i2c_msg msg = {.addr = 0x70, .flags = 0, .buf = NULL, .len = 0 }; - - dprintk ("%s\n", __FUNCTION__); + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - return -ENOMEM; - } - - if (NULL == (state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - state->i2c = adapter; + /* tristate TS output and disable interface pins */ + sp887x_writereg(state, 0xc18, 0x000); - if (i2c_transfer (adapter, &msg, 1) != 1) { - kfree(state); - kfree(client); - return -ENODEV; + return 0; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; - } +static int sp887x_init(struct dvb_frontend* fe) +{ + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + const struct firmware *fw = NULL; + int ret; + if (!state->initialised) { /* request the firmware, this will block until someone uploads it */ printk("sp887x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE); if (ret) { printk("sp887x: no firmware upload (timeout or file not found?)\n"); - goto out; + return ret; } - ret = sp887x_initial_setup(adapter, fw); + ret = sp887x_initial_setup(fe, fw); if (ret) { printk("sp887x: writing firmware to device failed\n"); - goto out; + release_firmware(fw); + return ret; } - - ret = dvb_register_frontend(sp887x_ioctl, state->dvb, state, - &sp887x_info, THIS_MODULE); - if (ret) { - printk("sp887x: registering frontend to dvb-core failed.\n"); - goto out; + state->initialised = 1; } + /* enable TS output and interface pins */ + sp887x_writereg(state, 0xc18, 0x00d); + return 0; -out: - release_firmware(fw); - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; } -static int detach_client(struct i2c_client *client) +static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); + fesettings->min_delay_ms = 350; + fesettings->step_size = 166666*2; + fesettings->max_drift = (166666*2)+1; + return 0; +} - dvb_unregister_frontend (sp887x_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); +static void sp887x_release(struct dvb_frontend* fe) +{ + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; kfree(state); - return 0; } -static int command (struct i2c_client *client, unsigned int cmd, void *arg) +static struct dvb_frontend_ops sp887x_ops; + +struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c) { - struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client); + struct sp887x_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct sp887x_state*) kmalloc(sizeof(struct sp887x_state), GFP_KERNEL); + if (state == NULL) goto error; - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter*)arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &sp887x_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_SP887X, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; + /* check if the demod is there */ + if (sp887x_readreg(state, 0x0200) < 0) goto error; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; -static int __init init_sp887x(void) -{ - return i2c_add_driver(&driver); +error: + if (state) kfree(state); + return NULL; } -static void __exit exit_sp887x(void) -{ - if (i2c_del_driver(&driver)) - printk("sp887x: driver deregistration failed\n"); -} +static struct dvb_frontend_ops sp887x_ops = { + + .info = { + .name = "Spase SP887x DVB-T", + .type = FE_OFDM, + .frequency_min = 50500000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_RECOVER + }, + + .release = sp887x_release, + + .init = sp887x_init, + .sleep = sp887x_sleep, + + .set_frontend = sp887x_setup_frontend_parameters, + .get_tune_settings = sp887x_get_tune_settings, + + .read_status = sp887x_read_status, + .read_ber = sp887x_read_ber, + .read_signal_strength = sp887x_read_signal_strength, + .read_snr = sp887x_read_snr, + .read_ucblocks = sp887x_read_ucblocks, +}; -module_init(init_sp887x); -module_exit(exit_sp887x); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("sp887x DVB-T demodulator driver"); +MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(sp887x_attach); diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h new file mode 100644 index 000000000000..6a05d8f8e8cc --- /dev/null +++ b/drivers/media/dvb/frontends/sp887x.h @@ -0,0 +1,29 @@ +/* + Driver for the Spase sp887x demodulator +*/ + +#ifndef SP887X_H +#define SP887X_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct sp887x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + + /* this should return the actual frequency tuned to */ + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c); + +#endif // SP887X_H diff --git a/drivers/media/dvb/frontends/sp887x_firm.h b/drivers/media/dvb/frontends/sp887x_firm.h deleted file mode 100644 index f67de982a44d..000000000000 --- a/drivers/media/dvb/frontends/sp887x_firm.h +++ /dev/null @@ -1,1375 +0,0 @@ -#ifndef __SP887x_FIRM_H__ -#define __SP887x_FIRM_H__ - - -static const -u8 sp887x_firm [16384] __devinitdata = { - 0x00, 0xb9, 0x00, 0xb9, 0x0f, 0xf9, 0x1f, 0x5d, 0x0f, 0xf9, 0x1b, 0x67, - 0x0f, 0xf9, 0x19, 0x2f, 0x0f, 0xf9, 0x00, 0x36, 0x0f, 0xf9, 0x00, 0x3c, - 0x0f, 0xf5, 0x00, 0x3e, 0x0f, 0xf9, 0x03, 0x0c, 0x0f, 0xf9, 0x0c, 0x59, - 0x0f, 0xf9, 0x15, 0xde, 0x0f, 0xf9, 0x19, 0x07, 0x0f, 0xf9, 0x08, 0x98, - 0x0f, 0xf9, 0x08, 0x03, 0x0f, 0xf9, 0x00, 0x26, 0x0f, 0xf9, 0x03, 0x11, - 0x0f, 0xf9, 0x1b, 0x47, 0x0f, 0xf9, 0x00, 0x76, 0x0f, 0xf9, 0x0a, 0xdd, - 0x0f, 0xf9, 0x0d, 0x09, 0x0f, 0xc4, 0x01, 0x62, 0x0b, 0x21, 0x0f, 0x38, - 0x00, 0x15, 0x0f, 0xf8, 0x00, 0x32, 0x09, 0xa1, 0x0f, 0xf9, 0x04, 0x2e, - 0x0f, 0xf9, 0x04, 0x5e, 0x0f, 0xd9, 0x00, 0x2e, 0x09, 0xfb, 0x0f, 0x39, - 0x0f, 0xf0, 0x02, 0x02, 0x0f, 0xc4, 0x01, 0x7a, 0x0f, 0xf9, 0x00, 0x42, - 0x0f, 0xf0, 0x02, 0x05, 0x0f, 0xc4, 0x01, 0x7b, 0x0f, 0xf9, 0x00, 0x42, - 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0x7c, 0x0f, 0xd4, 0x02, 0xa0, 0x1f, 0xf8, - 0x00, 0x53, 0x11, 0xa1, 0x0f, 0xd4, 0x00, 0xa0, 0x1f, 0xf8, 0x00, 0x53, - 0x11, 0xe1, 0x08, 0x15, 0x1f, 0x39, 0x0f, 0x38, 0x0c, 0x32, 0x0b, 0x13, - 0x00, 0x08, 0x10, 0x48, 0x0f, 0x38, 0x0c, 0x32, 0x00, 0x21, 0x07, 0x04, - 0x0b, 0x94, 0x1b, 0x94, 0x1f, 0x39, 0x06, 0x45, 0x0b, 0xd5, 0x1f, 0xf9, - 0x00, 0x65, 0x0b, 0xd5, 0x1f, 0xf9, 0x00, 0x65, 0x0f, 0x39, 0x07, 0x84, - 0x0b, 0x27, 0x09, 0xa1, 0x06, 0xc5, 0x0b, 0x67, 0x09, 0x94, 0x1f, 0xf9, - 0x00, 0x6f, 0x0f, 0x38, 0x09, 0x97, 0x06, 0x44, 0x07, 0x05, 0x0b, 0xa1, - 0x0b, 0xd8, 0x0b, 0xa1, 0x0f, 0x38, 0x0b, 0xd2, 0x0f, 0xcb, 0x01, 0x39, - 0x0f, 0xf5, 0x02, 0x47, 0x0f, 0xf4, 0x02, 0x5d, 0x06, 0x0b, 0x05, 0x85, - 0x0b, 0xea, 0x0b, 0xe1, 0x02, 0xde, 0x09, 0xeb, 0x0f, 0xee, 0x01, 0xc0, - 0x0f, 0xee, 0x0a, 0x01, 0x0f, 0xcf, 0x0c, 0x80, 0x0e, 0x2a, 0x0e, 0x6b, - 0x0f, 0xec, 0x06, 0xbe, 0x09, 0x2d, 0x0d, 0xee, 0x0f, 0xc4, 0x00, 0x95, - 0x12, 0x35, 0x0f, 0xe1, 0x0d, 0xbd, 0x0e, 0x18, 0x09, 0xec, 0x0f, 0xe1, - 0x00, 0xcd, 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x0b, 0xe1, 0x02, 0xdf, - 0x09, 0xe7, 0x0f, 0xe1, 0x05, 0x6d, 0x0e, 0x18, 0x09, 0xec, 0x0f, 0xe1, - 0x02, 0x26, 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x12, 0x35, 0x0f, 0xe1, - 0x0d, 0x03, 0x0f, 0xce, 0x03, 0xd2, 0x0e, 0x18, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xcb, 0x0a, 0xe3, 0x0f, 0xe1, - 0x08, 0x10, 0x0f, 0xce, 0x05, 0x88, 0x0e, 0x18, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xca, 0x09, 0x00, 0x0f, 0xe1, - 0x00, 0x38, 0x0f, 0xce, 0x07, 0xa7, 0x0e, 0x18, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xe2, 0x00, 0x3f, 0x0f, 0xe1, - 0x0a, 0xb1, 0x0f, 0xce, 0x0b, 0x89, 0x0e, 0x18, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x51, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xc9, 0x0a, 0x75, 0x0f, 0xe1, - 0x03, 0xf3, 0x0f, 0xce, 0x07, 0x15, 0x0e, 0x18, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x53, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xcf, 0x07, 0xc0, 0x0d, 0xce, - 0x1f, 0xce, 0x01, 0xc0, 0x00, 0x21, 0x0d, 0xae, 0x0c, 0x6e, 0x09, 0x96, - 0x0f, 0xce, 0x05, 0x00, 0x0f, 0xcd, 0x05, 0x40, 0x0f, 0xcc, 0x03, 0x00, - 0x09, 0xab, 0x00, 0x2a, 0x0d, 0xee, 0x1d, 0xae, 0x1e, 0x18, 0x19, 0xea, - 0x1e, 0x58, 0x19, 0xeb, 0x1d, 0x6e, 0x1e, 0x18, 0x19, 0xec, 0x1e, 0x51, - 0x19, 0xed, 0x1d, 0x2e, 0x0c, 0xee, 0x0f, 0xe1, 0x1f, 0xe7, 0x0c, 0xae, - 0x05, 0x84, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0xa4, 0x09, 0x99, 0x09, 0xe1, - 0x02, 0xdc, 0x12, 0xdd, 0x0f, 0x38, 0x09, 0xc2, 0x0f, 0xfb, 0x00, 0x7d, - 0x03, 0x37, 0x0f, 0xc4, 0x00, 0x9e, 0x05, 0xb7, 0x0f, 0xf5, 0x01, 0x39, - 0x0f, 0xfb, 0x00, 0x7d, 0x03, 0x37, 0x0f, 0xc4, 0x00, 0xa1, 0x05, 0xb7, - 0x0f, 0xf5, 0x01, 0x39, 0x05, 0x84, 0x0b, 0xaa, 0x0b, 0xab, 0x0f, 0xef, - 0x01, 0xc0, 0x0b, 0x88, 0x0c, 0x27, 0x09, 0xa6, 0x18, 0xb4, 0x05, 0x84, - 0x09, 0xa1, 0x0f, 0xd9, 0x0a, 0x69, 0x09, 0xee, 0x0f, 0xd7, 0x1f, 0xd0, - 0x18, 0xb5, 0x02, 0x97, 0x0c, 0x21, 0x1f, 0xf8, 0x01, 0x36, 0x02, 0xde, - 0x0f, 0xc4, 0x06, 0x28, 0x02, 0x34, 0x19, 0x44, 0x1f, 0xf9, 0x01, 0x45, - 0x0f, 0xee, 0x05, 0x00, 0x00, 0x21, 0x0e, 0x18, 0x09, 0xea, 0x0e, 0x58, - 0x09, 0xeb, 0x0f, 0xee, 0x05, 0x40, 0x0e, 0x18, 0x0f, 0xf8, 0x01, 0x4a, - 0x09, 0xc9, 0x0e, 0x09, 0x0f, 0xee, 0x05, 0x00, 0x0e, 0x2a, 0x0e, 0x6b, - 0x0f, 0xcf, 0x0c, 0x80, 0x0f, 0xec, 0x0e, 0x2d, 0x00, 0x2d, 0x0d, 0xee, - 0x0f, 0xe1, 0x01, 0xa4, 0x01, 0x4e, 0x0e, 0x20, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x59, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xe1, 0x0a, 0x35, 0x0f, 0xce, - 0x00, 0x27, 0x0e, 0x20, 0x09, 0xec, 0x0d, 0xa1, 0x0e, 0x59, 0x09, 0xed, - 0x0d, 0xee, 0x0f, 0xe1, 0x04, 0x34, 0x0f, 0xce, 0x00, 0xe3, 0x0e, 0x20, - 0x09, 0xec, 0x0d, 0xa1, 0x0e, 0x59, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xe1, - 0x0f, 0xb1, 0x0f, 0xce, 0x03, 0xd7, 0x0e, 0x20, 0x09, 0xec, 0x0d, 0xa1, - 0x0e, 0x59, 0x09, 0xed, 0x0d, 0xee, 0x0f, 0xe1, 0x02, 0x15, 0x0f, 0xce, - 0x0b, 0x17, 0x0e, 0x20, 0x09, 0xec, 0x0d, 0xa1, 0x0e, 0x59, 0x09, 0xed, - 0x0d, 0xee, 0x0f, 0xcf, 0x0a, 0xbf, 0x0f, 0xce, 0x09, 0x00, 0x0d, 0xee, - 0x0c, 0x61, 0x0d, 0xae, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0x99, 0x09, 0xe1, - 0x12, 0xdf, 0x12, 0x34, 0x19, 0x44, 0x02, 0xdc, 0x0f, 0x38, 0x09, 0xc2, - 0x09, 0x44, 0x0f, 0xc2, 0x0f, 0xff, 0x0f, 0xc2, 0x0f, 0xff, 0x0f, 0xc2, - 0x03, 0xff, 0x0f, 0xc4, 0x00, 0x95, 0x0f, 0xc2, 0x0f, 0xff, 0x0f, 0xc2, - 0x0f, 0xff, 0x0f, 0xc2, 0x0b, 0xff, 0x0f, 0xc4, 0x00, 0x98, 0x00, 0x02, - 0x0f, 0xc2, 0x08, 0x00, 0x0f, 0xc2, 0x04, 0x00, 0x0f, 0xc4, 0x00, 0x9b, - 0x00, 0x02, 0x0f, 0xc2, 0x08, 0x00, 0x0f, 0xc2, 0x0c, 0x00, 0x0f, 0xc4, - 0x00, 0x9e, 0x0f, 0xc2, 0x08, 0xc1, 0x0f, 0xc2, 0x0c, 0x0a, 0x00, 0x42, - 0x0f, 0xc4, 0x00, 0xa1, 0x0f, 0xc2, 0x08, 0xc1, 0x0f, 0xc2, 0x0c, 0x0a, - 0x00, 0x82, 0x0f, 0xc4, 0x00, 0xa4, 0x00, 0x02, 0x0f, 0xc2, 0x0a, 0x00, - 0x00, 0xc2, 0x0f, 0xc4, 0x00, 0xa7, 0x00, 0x02, 0x0f, 0xc2, 0x0c, 0x80, - 0x0f, 0xf4, 0x04, 0xe0, 0x01, 0x82, 0x0f, 0xc4, 0x01, 0x5d, 0x0b, 0x3b, - 0x0f, 0x39, 0x04, 0x04, 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0x5c, 0x0f, 0xc5, - 0x07, 0x3d, 0x0b, 0x01, 0x0b, 0x14, 0x1f, 0x39, 0x00, 0x14, 0x09, 0xc0, - 0x0f, 0xc8, 0x04, 0x18, 0x00, 0xd4, 0x1f, 0xc8, 0x1b, 0xb6, 0x00, 0x54, - 0x1f, 0xc8, 0x1c, 0xa7, 0x01, 0x14, 0x1f, 0xc8, 0x1c, 0x9f, 0x00, 0x94, - 0x1f, 0xc4, 0x01, 0x5e, 0x1b, 0x08, 0x02, 0x94, 0x1f, 0xc8, 0x04, 0x24, - 0x02, 0xd4, 0x1f, 0xc8, 0x04, 0x26, 0x03, 0x14, 0x1f, 0xc8, 0x04, 0x28, - 0x03, 0x54, 0x1f, 0xc8, 0x04, 0x2a, 0x03, 0x94, 0x1f, 0xc8, 0x04, 0x2c, - 0x03, 0xd4, 0x1f, 0xc8, 0x04, 0x2e, 0x05, 0x14, 0x1f, 0xc8, 0x04, 0x30, - 0x0f, 0xd4, 0x00, 0x28, 0x1f, 0xc8, 0x08, 0xeb, 0x0f, 0xd4, 0x00, 0x29, - 0x1f, 0xc8, 0x09, 0x49, 0x0f, 0xd4, 0x00, 0x2a, 0x1f, 0xc8, 0x09, 0xe6, - 0x0f, 0xd4, 0x00, 0x2b, 0x1f, 0xc8, 0x0b, 0x54, 0x0f, 0xd4, 0x00, 0x32, - 0x1f, 0xc8, 0x0b, 0x87, 0x0f, 0xd4, 0x00, 0x3c, 0x1f, 0xc8, 0x0b, 0xcf, - 0x0f, 0xd4, 0x00, 0x46, 0x1f, 0xc8, 0x11, 0x95, 0x0f, 0xd4, 0x00, 0x47, - 0x1f, 0xc8, 0x11, 0xb9, 0x0f, 0xd4, 0x00, 0x3f, 0x1f, 0xc8, 0x09, 0x85, - 0x0f, 0xd4, 0x00, 0x40, 0x1f, 0xc8, 0x09, 0x89, 0x0f, 0xd4, 0x00, 0x41, - 0x1f, 0xc8, 0x09, 0xcb, 0x02, 0x14, 0x1f, 0xc8, 0x04, 0x18, 0x0f, 0xd4, - 0x00, 0x50, 0x1f, 0xc8, 0x0e, 0xd9, 0x0f, 0xc4, 0x01, 0x5d, 0x0f, 0x38, - 0x0c, 0x00, 0x0f, 0xc8, 0x01, 0x00, 0x0f, 0xf7, 0x05, 0x82, 0x0f, 0xc4, - 0x01, 0x8b, 0x00, 0x00, 0x0f, 0xc4, 0x01, 0x8a, 0x00, 0x00, 0x0f, 0xf1, - 0x03, 0x39, 0x0f, 0xc5, 0x01, 0x89, 0x0f, 0xc4, 0x01, 0x8e, 0x0b, 0x21, - 0x0a, 0x48, 0x0c, 0x13, 0x1f, 0xf8, 0x02, 0x5f, 0x0c, 0x01, 0x0f, 0xc4, - 0x01, 0x90, 0x00, 0x21, 0x0b, 0x15, 0x1f, 0xc5, 0x01, 0x8b, 0x1c, 0x01, - 0x1f, 0xc5, 0x01, 0x8a, 0x1b, 0x3a, 0x1b, 0x01, 0x0f, 0xf1, 0x03, 0x39, - 0x0f, 0xc4, 0x01, 0x8e, 0x0b, 0x21, 0x0a, 0x48, 0x0c, 0x12, 0x1f, 0xf9, - 0x02, 0x4b, 0x0f, 0xc4, 0x01, 0x8c, 0x0f, 0xf6, 0x05, 0x9d, 0x0c, 0x00, - 0x0f, 0xf1, 0x03, 0x39, 0x0f, 0xc4, 0x01, 0x8d, 0x1f, 0x38, 0x0a, 0x40, - 0x0f, 0xc5, 0x01, 0x88, 0x0b, 0x01, 0x0f, 0xc4, 0x01, 0x89, 0x0f, 0xc5, - 0x07, 0x3b, 0x0b, 0x01, 0x0f, 0xc4, 0x01, 0x8b, 0x0f, 0xc5, 0x07, 0x3c, - 0x0b, 0x01, 0x0f, 0xc4, 0x01, 0x8c, 0x0f, 0xc5, 0x01, 0x87, 0x0b, 0x01, - 0x0f, 0xc4, 0x01, 0x8a, 0x0f, 0xc5, 0x01, 0x86, 0x0b, 0x01, 0x0f, 0xc4, - 0x01, 0x6a, 0x0f, 0xc5, 0x01, 0x85, 0x0b, 0x01, 0x0f, 0xc4, 0x01, 0x6b, - 0x0f, 0xc5, 0x01, 0x84, 0x0f, 0x38, 0x0b, 0x01, 0x0f, 0xc4, 0x01, 0x90, - 0x0c, 0x40, 0x0c, 0x21, 0x0f, 0xd9, 0x00, 0x67, 0x09, 0xc8, 0x00, 0xb6, - 0x00, 0x09, 0x0f, 0xf7, 0x04, 0x66, 0x0f, 0xc4, 0x06, 0x96, 0x0f, 0xc5, - 0x06, 0x2b, 0x0f, 0xf7, 0x02, 0x74, 0x07, 0x04, 0x00, 0x02, 0x0f, 0xc2, - 0x0c, 0x00, 0x0f, 0xf6, 0x02, 0x70, 0x01, 0x02, 0x05, 0x37, 0x0f, 0xf7, - 0x04, 0x6f, 0x0f, 0xf7, 0x02, 0x09, 0x0f, 0xf7, 0x01, 0x33, 0x0f, 0xc4, - 0x01, 0x8f, 0x07, 0x36, 0x0c, 0x00, 0x0f, 0xe1, 0x08, 0x0f, 0x1f, 0xe1, - 0x02, 0x0f, 0x0c, 0x11, 0x0f, 0xc4, 0x01, 0x8e, 0x0f, 0x38, 0x09, 0xc0, - 0x0f, 0xf0, 0x0f, 0x10, 0x0c, 0x32, 0x0f, 0xc4, 0x07, 0xc0, 0x0f, 0x38, - 0x0c, 0x00, 0x0f, 0xf1, 0x0f, 0x11, 0x00, 0x08, 0x0a, 0x61, 0x00, 0x15, - 0x11, 0x13, 0x10, 0x49, 0x10, 0x48, 0x0f, 0xf1, 0x0f, 0x13, 0x0f, 0xc4, - 0x07, 0xbc, 0x0b, 0x22, 0x00, 0x00, 0x0a, 0x65, 0x09, 0xb2, 0x00, 0x21, - 0x09, 0x95, 0x1c, 0x62, 0x10, 0xa5, 0x19, 0x89, 0x10, 0x48, 0x0c, 0x15, - 0x1f, 0xf0, 0x02, 0x04, 0x0c, 0x72, 0x0f, 0xf0, 0x02, 0x03, 0x00, 0x32, - 0x0f, 0x38, 0x00, 0x72, 0x0f, 0xfa, 0x02, 0xb8, 0x00, 0xc8, 0x0f, 0xf9, - 0x02, 0xbf, 0x0f, 0xc4, 0x07, 0xba, 0x0f, 0xc0, 0x00, 0x40, 0x0f, 0xc4, - 0x07, 0xbb, 0x00, 0x40, 0x0f, 0xc4, 0x07, 0xb8, 0x0f, 0xc0, 0x04, 0x00, - 0x0f, 0xc4, 0x07, 0xb9, 0x0f, 0xc0, 0x08, 0x00, 0x0f, 0xc4, 0x07, 0xbd, - 0x0f, 0xc0, 0x0f, 0xff, 0x0f, 0xc4, 0x07, 0xdd, 0x00, 0x00, 0x0f, 0xc4, - 0x07, 0xc2, 0x0f, 0x38, 0x00, 0x40, 0x0f, 0xf8, 0x03, 0x03, 0x0b, 0x22, - 0x0f, 0xf8, 0x03, 0x03, 0x0c, 0x22, 0x0f, 0xc4, 0x07, 0xbd, 0x0b, 0x24, - 0x09, 0xa2, 0x0f, 0xc4, 0x07, 0xbc, 0x0b, 0x25, 0x0f, 0x38, 0x09, 0x80, - 0x0f, 0xfa, 0x02, 0xb8, 0x00, 0x48, 0x0f, 0xf9, 0x02, 0xbf, 0x0f, 0xf0, - 0x0f, 0x10, 0x0c, 0x32, 0x0a, 0xe1, 0x0f, 0xc5, 0x07, 0xc3, 0x0f, 0xf1, - 0x0f, 0x10, 0x0a, 0x7f, 0x0f, 0xf0, 0x0f, 0x12, 0x0b, 0xf2, 0x0a, 0xd5, - 0x1f, 0xf9, 0x03, 0x1a, 0x03, 0xb9, 0x0a, 0x44, 0x0f, 0xf1, 0x0f, 0x12, - 0x0a, 0x7e, 0x0f, 0xfd, 0x03, 0x28, 0x00, 0x3f, 0x0b, 0x83, 0x07, 0x39, - 0x0a, 0x45, 0x0f, 0xf1, 0x0f, 0x12, 0x0a, 0x7e, 0x0f, 0xfd, 0x03, 0x33, - 0x00, 0x3f, 0x0f, 0xf1, 0x0f, 0x12, 0x0a, 0x43, 0x03, 0xb9, 0x0f, 0xc4, - 0x07, 0x6e, 0x00, 0x00, 0x04, 0x04, 0x00, 0x40, 0x0f, 0xc4, 0x07, 0xdd, - 0x00, 0x00, 0x0f, 0xc4, 0x07, 0xc2, 0x00, 0x40, 0x0f, 0xf0, 0x0f, 0x17, - 0x03, 0xb8, 0x00, 0x32, 0x04, 0x04, 0x00, 0xc0, 0x0f, 0xc4, 0x07, 0xdd, - 0x00, 0x40, 0x0f, 0xc4, 0x07, 0xc2, 0x03, 0xb8, 0x00, 0x40, 0x0f, 0xc4, - 0x07, 0x6e, 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x0f, 0xc4, 0x07, 0xdd, - 0x00, 0x00, 0x0f, 0xc4, 0x07, 0xc2, 0x03, 0xb8, 0x00, 0x40, 0x04, 0x04, - 0x02, 0x00, 0x0f, 0xc4, 0x07, 0x6e, 0x00, 0x00, 0x0f, 0xc4, 0x07, 0xdd, - 0x03, 0xb8, 0x00, 0x00, 0x0f, 0xc4, 0x01, 0x30, 0x0b, 0x83, 0x0b, 0x83, - 0x0f, 0xc4, 0x01, 0x32, 0x0b, 0x83, 0x07, 0x38, 0x0b, 0x83, 0x0f, 0xc4, - 0x06, 0x94, 0x0b, 0x83, 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x6c, 0x07, 0x38, - 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x38, 0x0b, 0x83, 0x0b, 0x83, 0x0f, 0xc4, - 0x01, 0x3a, 0x0b, 0x83, 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x3c, 0x0b, 0x83, - 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x76, 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x77, - 0x07, 0x38, 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x78, 0x0b, 0x83, 0x0f, 0xc4, - 0x01, 0x79, 0x07, 0x38, 0x0b, 0x83, 0x0f, 0xc4, 0x05, 0xe6, 0x0b, 0x83, - 0x0b, 0x83, 0x0f, 0xc4, 0x01, 0x40, 0x0b, 0x83, 0x07, 0x38, 0x0b, 0x83, - 0x0f, 0xf1, 0x0f, 0x11, 0x0a, 0x48, 0x0c, 0x21, 0x02, 0x13, 0x1f, 0xf9, - 0x02, 0xbf, 0x0f, 0xf1, 0x0f, 0x10, 0x00, 0x09, 0x02, 0x54, 0x13, 0x89, - 0x02, 0xd4, 0x1f, 0xc9, 0x03, 0x35, 0x03, 0x54, 0x1f, 0xc9, 0x03, 0x44, - 0x03, 0xd4, 0x1f, 0xc9, 0x03, 0x59, 0x0f, 0xd4, 0x00, 0x59, 0x1f, 0xc9, - 0x03, 0x4d, 0x0f, 0xd4, 0x00, 0x41, 0x1f, 0xc9, 0x03, 0x21, 0x0f, 0xd4, - 0x00, 0x49, 0x1f, 0xc9, 0x03, 0x2a, 0x0f, 0xd4, 0x00, 0x71, 0x1f, 0xc9, - 0x03, 0x62, 0x0f, 0xd4, 0x00, 0x73, 0x1f, 0xc9, 0x03, 0x6b, 0x0f, 0xd4, - 0x00, 0x80, 0x1f, 0xc9, 0x03, 0x73, 0x0f, 0xd4, 0x00, 0x81, 0x1f, 0xc9, - 0x03, 0x86, 0x0f, 0xd4, 0x00, 0x82, 0x1f, 0xc9, 0x03, 0x8d, 0x0c, 0x61, - 0x00, 0x15, 0x1f, 0xf1, 0x0f, 0x12, 0x1f, 0xc5, 0x07, 0xc3, 0x1c, 0x79, - 0x0f, 0xf9, 0x02, 0xde, 0x0f, 0xfb, 0x03, 0x96, 0x0f, 0xc4, 0x07, 0xdd, - 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xf9, 0x03, 0xd4, 0x0f, 0x39, 0x0f, 0xc8, - 0x00, 0x3c, 0x03, 0x3b, 0x1f, 0x39, 0x0f, 0xf1, 0x0a, 0x07, 0x00, 0x21, - 0x0f, 0xc4, 0x01, 0x91, 0x0b, 0x08, 0x0f, 0xc4, 0x01, 0x92, 0x0b, 0x15, - 0x1b, 0x08, 0x0c, 0x14, 0x1f, 0x39, 0x0c, 0x21, 0x0a, 0x55, 0x1f, 0xc4, - 0x01, 0x93, 0x1c, 0x00, 0x1f, 0xc8, 0x03, 0xf6, 0x15, 0x3b, 0x0f, 0x39, - 0x0f, 0xc4, 0x01, 0x93, 0x0b, 0x0f, 0x0d, 0xc8, 0x00, 0xb6, 0x00, 0x09, - 0x05, 0x37, 0x0d, 0xe1, 0x02, 0x19, 0x09, 0xc8, 0x00, 0xb6, 0x00, 0x09, - 0x0f, 0xf7, 0x02, 0x70, 0x0f, 0xc4, 0x00, 0xad, 0x00, 0x37, 0x01, 0xbb, - 0x0f, 0xc8, 0x00, 0x60, 0x00, 0xb6, 0x00, 0x09, 0x0f, 0xc4, 0x00, 0xad, - 0x0f, 0xf7, 0x02, 0x81, 0x0f, 0xf0, 0x0a, 0x07, 0x0f, 0xc4, 0x01, 0x93, - 0x0b, 0x32, 0x0f, 0xc4, 0x00, 0xaa, 0x00, 0x35, 0x02, 0x3a, 0x00, 0x08, - 0x02, 0xba, 0x00, 0x48, 0x0f, 0xfb, 0x03, 0xd4, 0x0f, 0xf6, 0x00, 0x3a, - 0x02, 0x08, 0x1f, 0xf9, 0x04, 0x1c, 0x0f, 0x39, 0x0f, 0xf5, 0x05, 0xc7, - 0x0f, 0xf5, 0x06, 0x07, 0x0f, 0xf5, 0x06, 0x14, 0x0f, 0xf5, 0x06, 0x79, - 0x0f, 0xf5, 0x06, 0x98, 0x0f, 0xf5, 0x06, 0xbc, 0x0f, 0xc4, 0x06, 0x8f, - 0x0b, 0x88, 0x00, 0xb6, 0x0b, 0x89, 0x03, 0x37, 0x02, 0x36, 0x04, 0xc4, - 0x06, 0xbb, 0x1f, 0xf7, 0x00, 0x8f, 0x0f, 0xfb, 0x00, 0x7a, 0x0f, 0xc4, - 0x00, 0xb0, 0x00, 0x37, 0x04, 0x44, 0x0f, 0xc5, 0x01, 0x94, 0x0b, 0x01, - 0x0f, 0xf7, 0x06, 0xdc, 0x0f, 0xc4, 0x00, 0xb0, 0x02, 0x37, 0x04, 0x44, - 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0x94, 0x0b, 0x11, 0x01, 0x36, 0x09, 0xc8, - 0x03, 0xb7, 0x0f, 0xc4, 0x06, 0x8f, 0x0c, 0x02, 0x03, 0x36, 0x0c, 0x42, - 0x00, 0xb7, 0x05, 0x37, 0x04, 0x37, 0x06, 0xbb, 0x1f, 0xf7, 0x00, 0x8f, - 0x00, 0x34, 0x04, 0xc4, 0x01, 0x38, 0x00, 0x48, 0x07, 0x37, 0x0f, 0xc4, - 0x02, 0x0e, 0x0f, 0xc0, 0x08, 0xe1, 0x1f, 0xc0, 0x02, 0x39, 0x0f, 0xc4, - 0x07, 0x38, 0x1f, 0xc4, 0x07, 0x37, 0x0f, 0xc5, 0x01, 0x95, 0x0b, 0x01, - 0x0f, 0xc4, 0x07, 0x2e, 0x1f, 0xc4, 0x07, 0x2b, 0x02, 0x37, 0x0f, 0xc4, - 0x05, 0xf2, 0x0b, 0x21, 0x01, 0x9e, 0x0f, 0xc5, 0x01, 0x96, 0x0f, 0xc4, - 0x05, 0xea, 0x0b, 0x01, 0x1f, 0xc4, 0x05, 0xf0, 0x1b, 0x01, 0x0b, 0x61, - 0x0f, 0xc4, 0x06, 0x2b, 0x00, 0x14, 0x1f, 0xc4, 0x07, 0x31, 0x00, 0x54, - 0x1f, 0xc4, 0x07, 0x34, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0xb3, 0x00, 0x37, - 0x0f, 0xc4, 0x07, 0x39, 0x0b, 0x2a, 0x1f, 0xc4, 0x06, 0x07, 0x1b, 0x21, - 0x10, 0xd3, 0x11, 0x2a, 0x0f, 0xec, 0x00, 0x2b, 0x0f, 0xee, 0x0d, 0x40, - 0x0e, 0x21, 0x0f, 0xd9, 0x02, 0xd3, 0x0f, 0xc4, 0x02, 0x0a, 0x0f, 0xc2, - 0x02, 0xd3, 0x09, 0xc2, 0x0f, 0xc2, 0x02, 0xc7, 0x0f, 0xc2, 0x02, 0xd3, - 0x0f, 0xfb, 0x08, 0x15, 0x0f, 0xc8, 0x04, 0x60, 0x0f, 0xc9, 0x04, 0xae, - 0x0f, 0xfb, 0x02, 0x8c, 0x0f, 0xf9, 0x07, 0xed, 0x00, 0x21, 0x0f, 0xc4, - 0x04, 0x2b, 0x0b, 0x14, 0x1f, 0xc0, 0x04, 0xb6, 0x0b, 0x3b, 0x0f, 0x39, - 0x0f, 0xc0, 0x04, 0xe6, 0x0f, 0xc4, 0x04, 0x40, 0x0f, 0xc8, 0x04, 0x50, - 0x0f, 0xf7, 0x00, 0x71, 0x0f, 0xc5, 0x04, 0x2d, 0x0f, 0xfb, 0x08, 0x4d, - 0x0f, 0xfb, 0x08, 0x4d, 0x0f, 0xc4, 0x02, 0x10, 0x0b, 0x21, 0x00, 0x15, - 0x1f, 0xfa, 0x08, 0x4d, 0x00, 0x01, 0x0f, 0xc4, 0x02, 0x0e, 0x0b, 0x8f, - 0x00, 0x61, 0x0f, 0xc5, 0x04, 0x2c, 0x0b, 0x01, 0x0b, 0x19, 0x09, 0xc0, - 0x0f, 0xc5, 0x02, 0x10, 0x00, 0x41, 0x09, 0xe1, 0x0d, 0xd3, 0x1f, 0xf9, - 0x08, 0x82, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x0f, 0xfa, 0x08, 0x3d, - 0x00, 0x00, 0x0f, 0xc4, 0x04, 0x2b, 0x0f, 0xc0, 0x06, 0x8f, 0x0f, 0x39, - 0x0f, 0xc0, 0x04, 0xf3, 0x0f, 0xc4, 0x01, 0x82, 0x0f, 0xc5, 0x04, 0x33, - 0x0b, 0x01, 0x0f, 0xcf, 0x04, 0x34, 0x0f, 0xfb, 0x08, 0x5f, 0x0f, 0xf9, - 0x08, 0x82, 0x0f, 0xc5, 0x01, 0x82, 0x0b, 0x4f, 0x0f, 0xc5, 0x04, 0x33, - 0x0b, 0x61, 0x0d, 0xd4, 0x1f, 0x38, 0x0d, 0xc1, 0x0f, 0xc0, 0x05, 0x04, - 0x0f, 0xcf, 0x04, 0x50, 0x0f, 0xfb, 0x08, 0x5f, 0x0f, 0xf9, 0x08, 0x82, - 0x0f, 0xc0, 0x05, 0x10, 0x0f, 0xc4, 0x04, 0x50, 0x0f, 0xcf, 0x04, 0x34, - 0x04, 0x0e, 0x0f, 0xcd, 0x04, 0x56, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, - 0x0f, 0xc0, 0x05, 0x1c, 0x0f, 0xc4, 0x04, 0x53, 0x0f, 0xcf, 0x04, 0x37, - 0x04, 0x0e, 0x0f, 0xcd, 0x04, 0x59, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, - 0x0f, 0xc0, 0x05, 0x27, 0x0f, 0xc4, 0x04, 0x50, 0x0a, 0x8f, 0x0f, 0xce, - 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x46, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc0, - 0x05, 0x32, 0x0f, 0xc4, 0x04, 0x53, 0x0a, 0x8f, 0x0f, 0xce, 0x01, 0x39, - 0x0f, 0xcd, 0x04, 0x46, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc0, 0x05, 0x3d, - 0x0f, 0xc4, 0x04, 0x56, 0x0a, 0x8f, 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, - 0x04, 0x49, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc0, 0x05, 0x56, 0x0f, 0xc5, - 0x04, 0x4f, 0x0b, 0x61, 0x00, 0x15, 0x1f, 0xc0, 0x05, 0x81, 0x0f, 0xc5, - 0x04, 0x2c, 0x0b, 0x62, 0x00, 0xe4, 0x09, 0xa1, 0x00, 0x15, 0x1f, 0xc0, - 0x05, 0x6b, 0x0f, 0xc4, 0x04, 0x59, 0x0a, 0x8f, 0x0f, 0xce, 0x01, 0x39, - 0x0f, 0xcd, 0x04, 0x49, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc5, 0x04, 0x4f, - 0x0b, 0x61, 0x00, 0x59, 0x09, 0xc1, 0x0f, 0xc5, 0x01, 0x95, 0x0b, 0x52, - 0x0f, 0xc0, 0x04, 0xf3, 0x1f, 0xc0, 0x05, 0xdf, 0x0f, 0xc4, 0x04, 0x50, - 0x0f, 0xc5, 0x04, 0x34, 0x03, 0x3e, 0x0f, 0xf7, 0x00, 0x2d, 0x0f, 0xf9, - 0x08, 0x82, 0x0f, 0xc0, 0x05, 0x76, 0x0f, 0xc4, 0x04, 0x56, 0x0f, 0xcf, - 0x04, 0x40, 0x06, 0x0e, 0x0d, 0xcd, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, - 0x0f, 0xc0, 0x05, 0x56, 0x0f, 0xc4, 0x04, 0x59, 0x0f, 0xcf, 0x04, 0x43, - 0x06, 0x0e, 0x0d, 0xcd, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, 0x0f, 0xc0, - 0x05, 0x8e, 0x0f, 0xc4, 0x04, 0x56, 0x0f, 0xcf, 0x04, 0x3a, 0x0f, 0xce, - 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x5c, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, - 0x0f, 0xc0, 0x05, 0x9a, 0x0f, 0xc4, 0x04, 0x59, 0x0f, 0xcf, 0x04, 0x3d, - 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x5c, 0x05, 0xb8, 0x06, 0x0c, - 0x0f, 0xc0, 0x05, 0xa7, 0x0f, 0xc4, 0x04, 0x59, 0x0f, 0xcf, 0x04, 0x3a, - 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x5f, 0x0f, 0xcc, 0x00, 0xc4, - 0x05, 0xb9, 0x0f, 0xc0, 0x05, 0xb3, 0x0f, 0xc4, 0x04, 0x56, 0x0f, 0xcf, - 0x04, 0x3d, 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x5f, 0x05, 0xb8, - 0x04, 0x0c, 0x0f, 0xc0, 0x05, 0xbe, 0x0f, 0xc4, 0x04, 0x5c, 0x0a, 0x8f, - 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x4c, 0x05, 0xb8, 0x06, 0x0c, - 0x0f, 0xc0, 0x05, 0xc9, 0x0f, 0xc4, 0x04, 0x5f, 0x0a, 0x8f, 0x0f, 0xce, - 0x01, 0x39, 0x0f, 0xcd, 0x04, 0x4c, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc0, - 0x05, 0xd4, 0x0f, 0xc4, 0x04, 0x5c, 0x0f, 0xcf, 0x04, 0x40, 0x06, 0x0e, - 0x0d, 0xcd, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, 0x0f, 0xc0, 0x05, 0x56, - 0x0f, 0xc4, 0x04, 0x5f, 0x0f, 0xcf, 0x04, 0x43, 0x06, 0x0e, 0x0d, 0xcd, - 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, 0x0f, 0xc0, 0x05, 0xec, 0x0f, 0xc4, - 0x04, 0x40, 0x0f, 0xcf, 0x04, 0x40, 0x0f, 0xce, 0x01, 0x39, 0x0f, 0xcd, - 0x04, 0x62, 0x0f, 0xcc, 0x00, 0xc4, 0x05, 0xb9, 0x0f, 0xc0, 0x05, 0xf8, - 0x0f, 0xc4, 0x04, 0x43, 0x0f, 0xcf, 0x04, 0x43, 0x0f, 0xce, 0x01, 0x39, - 0x0f, 0xcd, 0x04, 0x62, 0x05, 0xb8, 0x06, 0x0c, 0x0f, 0xc0, 0x06, 0x1a, - 0x0f, 0xc5, 0x04, 0x2c, 0x0b, 0x62, 0x00, 0xe4, 0x09, 0xa1, 0x00, 0x15, - 0x0f, 0xcf, 0x04, 0x6c, 0x0f, 0xce, 0x04, 0x62, 0x0f, 0xcd, 0x04, 0x6f, - 0x0f, 0xcc, 0x04, 0x4c, 0x1f, 0xcf, 0x04, 0x72, 0x1f, 0xcd, 0x04, 0x75, - 0x1f, 0xcc, 0x04, 0x49, 0x0d, 0xc4, 0x0f, 0xf6, 0x02, 0x74, 0x0d, 0x85, - 0x00, 0x36, 0x0d, 0xc4, 0x0d, 0x44, 0x0f, 0xf6, 0x02, 0x74, 0x0d, 0x05, - 0x00, 0x34, 0x0d, 0x44, 0x0f, 0xc0, 0x06, 0x2a, 0x0f, 0xc4, 0x04, 0x46, - 0x02, 0x37, 0x0f, 0xf7, 0x02, 0x99, 0x0f, 0xc4, 0x04, 0x65, 0x00, 0x37, - 0x0f, 0xc4, 0x04, 0x78, 0x02, 0xb7, 0x0f, 0xc4, 0x04, 0x78, 0x00, 0x35, - 0x0f, 0xc0, 0x06, 0x3a, 0x0f, 0xc4, 0x04, 0x49, 0x0f, 0xc5, 0x00, 0xaa, - 0x08, 0x3b, 0x0f, 0xc4, 0x04, 0x49, 0x00, 0x37, 0x0f, 0xc4, 0x04, 0x65, - 0x06, 0xb7, 0x0f, 0xc4, 0x04, 0x68, 0x00, 0x35, 0x0f, 0xc0, 0x06, 0x57, - 0x0f, 0xc4, 0x04, 0x68, 0x0f, 0xf7, 0x00, 0xbd, 0x0f, 0xc4, 0x00, 0xb3, - 0x05, 0xb7, 0x0f, 0xfb, 0x00, 0x58, 0x00, 0x08, 0x10, 0x48, 0x0f, 0xc4, - 0x04, 0x6b, 0x0c, 0x00, 0x0f, 0xc4, 0x04, 0x87, 0x0b, 0x21, 0x0c, 0x19, - 0x09, 0xc0, 0x0f, 0xc5, 0x04, 0x2d, 0x0f, 0xfb, 0x08, 0x1b, 0x0f, 0xfb, - 0x08, 0x1b, 0x0f, 0xf9, 0x08, 0x1b, 0x0f, 0xc0, 0x06, 0x7a, 0x0f, 0xc4, - 0x04, 0x6b, 0x0b, 0x21, 0x00, 0x14, 0x0f, 0xcf, 0x04, 0x7b, 0x1f, 0xcf, - 0x04, 0x7e, 0x0f, 0xce, 0x06, 0x28, 0x1f, 0xce, 0x04, 0x49, 0x0f, 0xc4, - 0x04, 0x46, 0x02, 0x37, 0x0f, 0xc4, 0x02, 0xc4, 0x00, 0x37, 0x0d, 0xc4, - 0x0f, 0xc5, 0x04, 0x46, 0x0f, 0xf7, 0x02, 0x74, 0x00, 0x36, 0x0d, 0xc4, - 0x0f, 0xcf, 0x04, 0x81, 0x0d, 0xc4, 0x0f, 0xf6, 0x02, 0x74, 0x0d, 0x85, - 0x00, 0x34, 0x0d, 0xc4, 0x0f, 0xc0, 0x04, 0xb6, 0x0f, 0xc4, 0x04, 0x6b, - 0x0b, 0x21, 0x00, 0x14, 0x1f, 0x39, 0x0f, 0xc4, 0x04, 0x84, 0x0f, 0xf7, - 0x00, 0xbd, 0x0f, 0xc4, 0x04, 0x46, 0x05, 0xb7, 0x0f, 0xfb, 0x00, 0x58, - 0x1f, 0x39, 0x0f, 0xc4, 0x04, 0x84, 0x0f, 0xf5, 0x00, 0xb2, 0x0f, 0xc0, - 0x04, 0xe6, 0x0f, 0xc4, 0x04, 0x6c, 0x0f, 0xc5, 0x04, 0x88, 0x07, 0x3e, - 0x0f, 0xf7, 0x00, 0x2d, 0x0f, 0xfb, 0x08, 0x15, 0x0f, 0xc8, 0x06, 0x9f, - 0x05, 0x3b, 0x0f, 0xf9, 0x08, 0x82, 0x0f, 0xc4, 0x02, 0x0e, 0x0b, 0x08, - 0x00, 0xb6, 0x00, 0x09, 0x0f, 0xf7, 0x02, 0x99, 0x0f, 0xc4, 0x00, 0xb6, - 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x04, 0x97, 0x0f, 0xc5, 0x04, 0x9a, - 0x0f, 0xf7, 0x02, 0x74, 0x0f, 0xc4, 0x00, 0xc2, 0x00, 0x37, 0x01, 0xbb, - 0x0f, 0xc4, 0x00, 0xc2, 0x0f, 0xc5, 0x04, 0x94, 0x08, 0x3b, 0x0f, 0xc4, - 0x00, 0xb6, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0xb6, 0x06, 0xb7, 0x0f, 0xc4, - 0x00, 0xc5, 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x04, 0xa3, 0x0b, 0x08, - 0x00, 0xb6, 0x00, 0x09, 0x0f, 0xc4, 0x00, 0xb9, 0x00, 0x37, 0x0f, 0xc4, - 0x00, 0xb6, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0xce, 0x00, 0x37, 0x01, 0xbb, - 0x0f, 0xc4, 0x02, 0x0e, 0x0b, 0x21, 0x0f, 0xc4, 0x04, 0xa3, 0x0b, 0x11, - 0x09, 0xc8, 0x00, 0xb6, 0x00, 0x09, 0x0f, 0xc4, 0x00, 0xbc, 0x00, 0x37, - 0x0f, 0xc4, 0x04, 0x97, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0xbf, 0x00, 0x37, - 0x01, 0xbb, 0x0f, 0xc4, 0x00, 0xbf, 0x0f, 0xc5, 0x04, 0x9a, 0x0f, 0xf7, - 0x02, 0x7d, 0x0f, 0xc4, 0x00, 0xb9, 0x0f, 0xf7, 0x02, 0x81, 0x0f, 0xc4, - 0x00, 0xc8, 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x04, 0xa0, 0x0f, 0xc5, - 0x04, 0x9a, 0x0f, 0xf7, 0x02, 0x7d, 0x0f, 0xc4, 0x00, 0xbc, 0x06, 0xb7, - 0x0f, 0xc4, 0x00, 0xcb, 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x04, 0x8e, - 0x0f, 0xc5, 0x04, 0x91, 0x0f, 0xf7, 0x02, 0x7d, 0x0f, 0xc4, 0x00, 0xd1, - 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x04, 0x88, 0x0f, 0xc5, 0x04, 0x8b, - 0x0f, 0xf7, 0x02, 0x7d, 0x0f, 0xc4, 0x00, 0xd4, 0x00, 0x37, 0x01, 0xbb, - 0x0f, 0xcf, 0x0f, 0xff, 0x0f, 0xce, 0x02, 0xbb, 0x0f, 0xc4, 0x00, 0xce, - 0x0f, 0xc9, 0x0f, 0x5c, 0x0f, 0xca, 0x07, 0xf8, 0x0f, 0xcc, 0x0f, 0xf8, - 0x06, 0x3a, 0x01, 0xcd, 0x0f, 0xc4, 0x00, 0xce, 0x0f, 0xc9, 0x09, 0x9a, - 0x0f, 0xca, 0x07, 0xfe, 0x07, 0xcc, 0x0f, 0xcd, 0x0f, 0xe7, 0x06, 0x3b, - 0x0f, 0xc4, 0x00, 0xcb, 0x0f, 0xc9, 0x0a, 0x66, 0x00, 0x0a, 0x0f, 0xcc, - 0x00, 0x3f, 0x0f, 0xcd, 0x0f, 0xdf, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xc8, - 0x0f, 0xc9, 0x0b, 0x33, 0x0f, 0xca, 0x07, 0xff, 0x0f, 0xcc, 0x00, 0x7f, - 0x0f, 0xcd, 0x0f, 0xbf, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xbf, 0x01, 0xba, - 0x0d, 0xc0, 0x0f, 0xc4, 0x00, 0xbf, 0x0b, 0x0f, 0x0f, 0xce, 0x02, 0xbf, - 0x0f, 0xc4, 0x00, 0xc5, 0x0f, 0xc9, 0x08, 0xcd, 0x00, 0x0a, 0x0f, 0xcc, - 0x0f, 0x7e, 0x0f, 0xcd, 0x00, 0xf9, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xc5, - 0x0f, 0xc9, 0x0b, 0x00, 0x00, 0xca, 0x0f, 0xcc, 0x07, 0xff, 0x0f, 0xcd, - 0x0b, 0xff, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xd1, 0x0f, 0xc9, 0x08, 0x00, - 0x00, 0x0a, 0x1f, 0xc9, 0x0c, 0x00, 0x1f, 0xca, 0x07, 0xff, 0x0f, 0xcc, - 0x01, 0xef, 0x0f, 0xcd, 0x0e, 0xff, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xd4, - 0x0f, 0xc9, 0x0c, 0x00, 0x00, 0x4a, 0x1f, 0xc9, 0x0e, 0x00, 0x0f, 0xcc, - 0x03, 0xef, 0x0f, 0xcd, 0x0d, 0xff, 0x06, 0x3b, 0x0f, 0xc4, 0x00, 0xd1, - 0x0f, 0xc9, 0x08, 0x00, 0x00, 0x0a, 0x1f, 0xca, 0x07, 0xff, 0x0f, 0xcc, - 0x0f, 0xfb, 0x0f, 0xcd, 0x0f, 0xfd, 0x06, 0x3b, 0x0f, 0xc4, 0x01, 0x99, - 0x0f, 0xc5, 0x01, 0x97, 0x0b, 0x61, 0x0d, 0xd5, 0x1f, 0xf8, 0x07, 0x93, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x00, 0xd2, 0x0f, 0xf8, 0x07, 0x99, - 0x10, 0xc0, 0x00, 0x00, 0x00, 0xd2, 0x1f, 0xf9, 0x07, 0x99, 0x0f, 0xfb, - 0x07, 0xab, 0x0f, 0xc4, 0x01, 0x98, 0x0f, 0xfa, 0x07, 0xa2, 0x0b, 0x0e, - 0x0f, 0xc5, 0x01, 0x81, 0x0f, 0x38, 0x00, 0x41, 0x0f, 0xf1, 0x0f, 0x15, - 0x0f, 0xe2, 0x0f, 0xf8, 0x0a, 0x64, 0x09, 0xa2, 0x0d, 0xa5, 0x0f, 0x38, - 0x09, 0xb2, 0x0f, 0xc4, 0x01, 0x97, 0x0d, 0xc0, 0x00, 0x0e, 0x07, 0x36, - 0x04, 0x0d, 0x01, 0x0a, 0x12, 0x8a, 0x02, 0x09, 0x12, 0x09, 0x00, 0x4c, - 0x00, 0x0b, 0x0d, 0xe2, 0x00, 0x21, 0x0f, 0xe4, 0x00, 0x81, 0x09, 0x95, - 0x10, 0x4e, 0x10, 0x8d, 0x10, 0x0c, 0x0f, 0xe4, 0x00, 0x50, 0x09, 0x95, - 0x10, 0x8e, 0x10, 0x8d, 0x10, 0x0c, 0x08, 0x24, 0x09, 0x95, 0x10, 0xce, - 0x1c, 0x8d, 0x10, 0x4b, 0x0f, 0xe4, 0x08, 0x04, 0x09, 0x95, 0x11, 0x0e, - 0x11, 0x0d, 0x10, 0x0c, 0x0f, 0xe4, 0x03, 0x0a, 0x09, 0x95, 0x14, 0x24, - 0x19, 0x94, 0x11, 0x4e, 0x0f, 0xe4, 0x04, 0x00, 0x09, 0x95, 0x11, 0x8e, - 0x1c, 0x4d, 0x10, 0x4c, 0x0f, 0xc4, 0x07, 0x3a, 0x0b, 0x21, 0x00, 0x15, - 0x0f, 0xc4, 0x01, 0x98, 0x0d, 0x80, 0x1f, 0xc4, 0x01, 0x91, 0x1d, 0x40, - 0x1f, 0xc4, 0x01, 0x83, 0x1d, 0x00, 0x1f, 0xf0, 0x0a, 0x11, 0x0f, 0x38, - 0x1c, 0xf2, 0x01, 0xe2, 0x0f, 0xc4, 0x07, 0xf5, 0x0b, 0x24, 0x09, 0xa1, - 0x00, 0x0f, 0x00, 0x54, 0x10, 0x4f, 0x00, 0x94, 0x14, 0x0f, 0x01, 0x14, - 0x11, 0x0f, 0x01, 0x54, 0x10, 0x8f, 0x01, 0x94, 0x1f, 0xcf, 0x04, 0x00, - 0x0d, 0xe1, 0x00, 0x14, 0x1f, 0x39, 0x0f, 0xf9, 0x07, 0xab, 0x0f, 0xf7, - 0x00, 0xbd, 0x07, 0x04, 0x00, 0x02, 0x0c, 0x42, 0x0f, 0xfa, 0x00, 0x58, - 0x0c, 0x82, 0x0d, 0xe2, 0x0d, 0x64, 0x1d, 0x24, 0x09, 0x8f, 0x0d, 0x84, - 0x0d, 0x48, 0x1d, 0x08, 0x0c, 0x02, 0x07, 0x34, 0x0a, 0x8e, 0x0f, 0xc4, - 0x04, 0x6c, 0x0f, 0xc8, 0x04, 0x88, 0x0f, 0xf5, 0x00, 0x71, 0x0b, 0xc9, - 0x0b, 0xca, 0x00, 0x21, 0x0c, 0x54, 0x1f, 0x39, 0x0c, 0x9f, 0x09, 0xe2, - 0x0c, 0x44, 0x0b, 0x8b, 0x0b, 0x25, 0x09, 0x82, 0x09, 0x8c, 0x0c, 0x14, - 0x10, 0x22, 0x0b, 0x25, 0x09, 0x82, 0x09, 0x8d, 0x0f, 0xc4, 0x01, 0x80, - 0x0b, 0x21, 0x00, 0x14, 0x0f, 0xe1, 0x0f, 0xff, 0x0c, 0xd9, 0x0c, 0xce, - 0x19, 0xce, 0x0d, 0x14, 0x1f, 0xf0, 0x0a, 0x12, 0x1d, 0xb2, 0x1f, 0xf0, - 0x0a, 0x13, 0x0f, 0x38, 0x1d, 0x72, 0x0b, 0x0f, 0x0f, 0xc4, 0x02, 0x0c, - 0x0b, 0x21, 0x00, 0xd9, 0x09, 0xc0, 0x09, 0xe1, 0x0f, 0xd2, 0x02, 0xd3, - 0x1f, 0xc0, 0x02, 0xc7, 0x0b, 0x04, 0x0d, 0xc2, 0x00, 0x02, 0x0f, 0x38, - 0x00, 0x02, 0x0f, 0xc4, 0x02, 0x0c, 0x0b, 0x03, 0x0f, 0xc4, 0x02, 0x11, - 0x0b, 0x03, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x03, 0x13, - 0x1f, 0x39, 0x00, 0x02, 0x0b, 0x21, 0x00, 0x59, 0x0f, 0xf8, 0x08, 0x3d, - 0x09, 0xc0, 0x0f, 0xf1, 0x0a, 0x1f, 0x0f, 0xc4, 0x04, 0x2c, 0x07, 0x36, - 0x0b, 0x08, 0x1a, 0x66, 0x19, 0xa6, 0x19, 0xa6, 0x19, 0xa6, 0x19, 0xaa, - 0x1f, 0xec, 0x02, 0x39, 0x1f, 0xee, 0x0d, 0x40, 0x1e, 0x21, 0x1c, 0x19, - 0x0f, 0xf6, 0x0b, 0x3f, 0x19, 0xc8, 0x0c, 0x4d, 0x0c, 0x8e, 0x0f, 0xfa, - 0x08, 0x7a, 0x0c, 0x09, 0x0a, 0x8f, 0x0d, 0x49, 0x00, 0xb6, 0x00, 0x08, - 0x0d, 0xa1, 0x05, 0xd1, 0x01, 0x36, 0x09, 0xc8, 0x00, 0x34, 0x0d, 0xc4, - 0x0f, 0xc4, 0x02, 0x0a, 0x0a, 0x8f, 0x0b, 0x85, 0x0b, 0x0e, 0x0f, 0xc4, - 0x04, 0x2b, 0x0f, 0xfb, 0x08, 0x94, 0x0d, 0xc4, 0x0a, 0xc0, 0x0a, 0xe1, - 0x0d, 0x92, 0x1f, 0xc0, 0x02, 0xd3, 0x0b, 0x04, 0x0f, 0xc5, 0x04, 0x2b, - 0x0f, 0xfe, 0x00, 0x2b, 0x0f, 0xf5, 0x00, 0x2d, 0x0f, 0xf7, 0x00, 0xbd, - 0x05, 0xb6, 0x0d, 0xc4, 0x0d, 0xb7, 0x05, 0x37, 0x0f, 0xf6, 0x00, 0xbd, - 0x0d, 0x44, 0x0d, 0x37, 0x00, 0x34, 0x0d, 0x44, 0x00, 0x0a, 0x00, 0x0b, - 0x01, 0x3e, 0x0f, 0xfd, 0x08, 0xb4, 0x00, 0x3f, 0x0b, 0x88, 0x00, 0x09, - 0x0c, 0x21, 0x02, 0xdf, 0x1f, 0xc9, 0x0f, 0xff, 0x0c, 0xa0, 0x09, 0xca, - 0x0c, 0x61, 0x0c, 0xd9, 0x09, 0xcb, 0x0c, 0xaa, 0x0c, 0xeb, 0x0f, 0xee, - 0x02, 0x40, 0x0c, 0xe1, 0x02, 0xdf, 0x0f, 0xc8, 0x0a, 0x7e, 0x1f, 0xc8, - 0x0a, 0xbe, 0x0c, 0x2e, 0x0f, 0x38, 0x0e, 0x01, 0x0f, 0xc4, 0x00, 0x5c, - 0x0f, 0xc5, 0x01, 0xb8, 0x0f, 0xfb, 0x08, 0xa4, 0x0f, 0xc4, 0x00, 0x60, - 0x0f, 0xc5, 0x01, 0xb9, 0x0f, 0xfb, 0x08, 0xa4, 0x0f, 0xc4, 0x00, 0x64, - 0x0f, 0xc5, 0x01, 0xba, 0x0f, 0xfb, 0x08, 0xa4, 0x0f, 0xc4, 0x00, 0x68, - 0x0f, 0xc5, 0x01, 0xbb, 0x0f, 0xfb, 0x08, 0xa4, 0x0f, 0xc5, 0x05, 0x60, - 0x0f, 0xc4, 0x01, 0xb8, 0x0b, 0x03, 0x0f, 0xc4, 0x01, 0xb9, 0x0b, 0x03, - 0x0f, 0xc5, 0x05, 0x62, 0x0f, 0xc4, 0x01, 0xba, 0x0b, 0x03, 0x0f, 0xc4, - 0x01, 0xbb, 0x0f, 0x38, 0x0b, 0x03, 0x07, 0xb7, 0x1f, 0xf9, 0x08, 0xf8, - 0x0f, 0xc4, 0x01, 0x34, 0x00, 0x02, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0x61, - 0x0f, 0xc0, 0x00, 0x2d, 0x0f, 0xf9, 0x09, 0x02, 0x0f, 0xc4, 0x01, 0x34, - 0x00, 0x02, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0x61, 0x0f, 0xc0, 0x00, 0xd9, - 0x0f, 0xf9, 0x09, 0x02, 0x0f, 0xc4, 0x01, 0x34, 0x0f, 0xf0, 0x05, 0x01, - 0x0b, 0xb2, 0x0f, 0xf0, 0x05, 0x00, 0x0b, 0xb2, 0x0f, 0xc4, 0x01, 0x60, - 0x0f, 0xf0, 0x05, 0x02, 0x00, 0x00, 0x00, 0x32, 0x0f, 0xf0, 0x0a, 0x03, - 0x00, 0x32, 0x0f, 0xc4, 0x01, 0x61, 0x0f, 0xf0, 0x0a, 0x02, 0x0b, 0x32, - 0x0f, 0xc4, 0x01, 0xb0, 0x0f, 0xc0, 0x00, 0x64, 0x00, 0x08, 0x0f, 0xfa, - 0x19, 0x6d, 0x00, 0x09, 0x0f, 0xf7, 0x02, 0xb6, 0x0f, 0xfb, 0x12, 0xcf, - 0x0f, 0xc4, 0x06, 0x8d, 0x0b, 0x21, 0x0f, 0xd3, 0x00, 0x30, 0x0f, 0xc4, - 0x06, 0x94, 0x09, 0xe1, 0x0b, 0xa0, 0x0f, 0xf0, 0x03, 0x2a, 0x09, 0xf2, - 0x00, 0x21, 0x1f, 0xe1, 0x1f, 0xff, 0x0b, 0x99, 0x0f, 0xf0, 0x03, 0x29, - 0x09, 0xf2, 0x0f, 0xc4, 0x01, 0x9e, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0xac, - 0x00, 0x00, 0x0f, 0xc4, 0x01, 0xaf, 0x00, 0x00, 0x0f, 0xc4, 0x05, 0x86, - 0x00, 0x00, 0x04, 0x05, 0x0f, 0xc1, 0x00, 0x29, 0x02, 0xba, 0x00, 0x08, - 0x0f, 0x39, 0x0f, 0xc4, 0x06, 0x94, 0x0f, 0xf0, 0x03, 0x2a, 0x0b, 0xb2, - 0x0f, 0xf0, 0x03, 0x29, 0x0b, 0xb2, 0x0f, 0xc4, 0x06, 0x8d, 0x0f, 0xf0, - 0x03, 0x2b, 0x0f, 0xc0, 0x00, 0x30, 0x0f, 0xf2, 0x00, 0x30, 0x0f, 0xf7, - 0x0b, 0xc3, 0x0f, 0xc8, 0x0a, 0xa1, 0x07, 0x36, 0x00, 0x49, 0x1f, 0xc8, - 0x06, 0xa9, 0x00, 0xb6, 0x10, 0x09, 0x0f, 0xc8, 0x1f, 0xe7, 0x01, 0x37, - 0x0f, 0xc4, 0x05, 0x5c, 0x00, 0x37, 0x09, 0x36, 0x04, 0x08, 0x0f, 0xc4, - 0x06, 0x01, 0x04, 0x00, 0x04, 0x04, 0x0f, 0xc0, 0x00, 0x2a, 0x0f, 0xc4, - 0x01, 0xa0, 0x00, 0x00, 0x08, 0xc4, 0x00, 0x80, 0x0f, 0xc8, 0x00, 0x3f, - 0x0f, 0xf7, 0x05, 0x23, 0x0f, 0x39, 0x08, 0xc4, 0x0b, 0x21, 0x00, 0x54, - 0x1f, 0xf9, 0x09, 0x82, 0x0f, 0x38, 0x09, 0xc0, 0x0f, 0xf7, 0x05, 0x37, - 0x0f, 0x39, 0x04, 0x3b, 0x0f, 0xfb, 0x09, 0x7b, 0x0f, 0x39, 0x04, 0x3b, - 0x07, 0x37, 0x1f, 0xf9, 0x09, 0x90, 0x0f, 0xf6, 0x05, 0x5c, 0x00, 0x48, - 0x0f, 0xfb, 0x0d, 0xb2, 0x0f, 0xc4, 0x01, 0xa2, 0x00, 0x21, 0x0b, 0x15, - 0x1f, 0xf9, 0x09, 0xae, 0x0f, 0xc4, 0x01, 0x5f, 0x0b, 0x21, 0x00, 0x59, - 0x09, 0xc0, 0x06, 0x53, 0x1f, 0xf9, 0x09, 0xa4, 0x0f, 0xf7, 0x05, 0x37, - 0x01, 0x38, 0x00, 0xc8, 0x09, 0xe2, 0x00, 0x64, 0x09, 0xa2, 0x00, 0x63, - 0x02, 0x3a, 0x09, 0x88, 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf5, 0x0c, 0x97, - 0x08, 0xc4, 0x0b, 0x21, 0x00, 0x54, 0x09, 0xe2, 0x00, 0x64, 0x09, 0xa2, - 0x00, 0x63, 0x02, 0x3a, 0x09, 0x88, 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf7, - 0x0c, 0x97, 0x0f, 0xfb, 0x09, 0x7b, 0x04, 0x04, 0x0f, 0xc5, 0x01, 0x5f, - 0x0b, 0x21, 0x0f, 0xd5, 0x00, 0x40, 0x1f, 0xf8, 0x09, 0xc7, 0x10, 0x01, - 0x0f, 0x39, 0x0f, 0xf0, 0x05, 0x0c, 0x0f, 0x38, 0x00, 0xb2, 0x04, 0x3b, - 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf7, 0x0c, 0x97, 0x07, 0x37, 0x1f, 0xf9, - 0x09, 0xd6, 0x0f, 0xf6, 0x05, 0x5c, 0x00, 0x48, 0x09, 0x3b, 0x04, 0x04, - 0x0f, 0xc0, 0x00, 0x41, 0x08, 0xc4, 0x0b, 0x21, 0x00, 0x54, 0x09, 0xe2, - 0x00, 0x64, 0x09, 0xa2, 0x00, 0x63, 0x02, 0x3a, 0x09, 0x88, 0x0f, 0xfb, - 0x09, 0x7b, 0x0f, 0x39, 0x04, 0x3b, 0x07, 0xba, 0x00, 0xc8, 0x0f, 0xc4, - 0x01, 0xa0, 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xf9, 0x09, 0xf3, 0x0f, 0xfb, - 0x0d, 0xf5, 0x0f, 0xf9, 0x0a, 0x38, 0x0f, 0xfb, 0x0d, 0xb2, 0x0f, 0xfb, - 0x0e, 0x05, 0x0f, 0xc4, 0x01, 0xb6, 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xf9, - 0x0a, 0xc3, 0x0f, 0xc4, 0x01, 0x9f, 0x0b, 0x22, 0x0f, 0xe4, 0x00, 0xc0, - 0x09, 0xa1, 0x0f, 0xd4, 0x00, 0xc0, 0x1f, 0xf9, 0x0a, 0x15, 0x0f, 0xd4, - 0x00, 0x40, 0x1f, 0xf9, 0x0a, 0x15, 0x00, 0x14, 0x1f, 0x39, 0x0f, 0xc4, - 0x01, 0xa1, 0x0b, 0x21, 0x00, 0x55, 0x1f, 0x38, 0x09, 0xc0, 0x0f, 0xf9, - 0x0a, 0x38, 0x0f, 0xc4, 0x01, 0x42, 0x0b, 0x08, 0x0f, 0xc4, 0x01, 0xa7, - 0x0b, 0x21, 0x0f, 0xd4, 0x08, 0x00, 0x1f, 0xf9, 0x0a, 0x2c, 0x0c, 0x14, - 0x1f, 0xf9, 0x0a, 0x99, 0x0f, 0xc4, 0x01, 0xa8, 0x0b, 0x21, 0x0f, 0xd4, - 0x08, 0x00, 0x1f, 0xf9, 0x0a, 0x2c, 0x0c, 0x14, 0x1f, 0xf9, 0x0a, 0x99, - 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0xa6, 0x0b, 0x21, 0x00, 0x54, 0x1f, 0xf8, - 0x0a, 0x38, 0x09, 0xc0, 0x0f, 0xc4, 0x01, 0xa1, 0x0f, 0x38, 0x01, 0x00, - 0x0f, 0xc4, 0x01, 0xa3, 0x00, 0x00, 0x0f, 0xc4, 0x01, 0xa2, 0x00, 0x00, - 0x0f, 0xc4, 0x01, 0xa1, 0x01, 0x00, 0x0f, 0xc4, 0x01, 0xa6, 0x00, 0xc0, - 0x0f, 0xc4, 0x01, 0xa7, 0x0f, 0xc0, 0x08, 0x00, 0x0f, 0xc4, 0x01, 0xa8, - 0x0f, 0xc0, 0x08, 0x00, 0x07, 0xb7, 0x01, 0x48, 0x13, 0xc8, 0x0f, 0xc4, - 0x01, 0xa0, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xcb, 0x09, 0xe1, - 0x0c, 0x16, 0x1f, 0xf9, 0x0a, 0xda, 0x00, 0x54, 0x10, 0x08, 0x00, 0x94, - 0x16, 0x48, 0x00, 0xd4, 0x1f, 0xc8, 0x1f, 0xe7, 0x01, 0x14, 0x1f, 0xc8, - 0x00, 0x32, 0x01, 0x54, 0x1f, 0xc8, 0x1f, 0xce, 0x01, 0x94, 0x1f, 0xc8, - 0x00, 0x4b, 0x01, 0xd4, 0x1f, 0xc8, 0x1f, 0xb5, 0x02, 0x14, 0x1f, 0xc8, - 0x00, 0x64, 0x02, 0x54, 0x1f, 0xc8, 0x1f, 0x9c, 0x02, 0x94, 0x1f, 0xc8, - 0x00, 0x7d, 0x02, 0xd4, 0x1f, 0xc8, 0x1f, 0x83, 0x03, 0x14, 0x1f, 0xc8, - 0x00, 0x96, 0x03, 0x54, 0x1f, 0xc8, 0x1f, 0x6a, 0x03, 0x94, 0x1f, 0xc8, - 0x00, 0xaf, 0x03, 0xd4, 0x1f, 0xc8, 0x1f, 0x51, 0x0f, 0xfb, 0x19, 0x85, - 0x07, 0xb7, 0x0f, 0xc9, 0x00, 0x2d, 0x1f, 0xc9, 0x00, 0xd9, 0x0c, 0x61, - 0x0c, 0x1b, 0x09, 0xc8, 0x0c, 0x21, 0x0f, 0xd7, 0x1f, 0xff, 0x10, 0x08, - 0x0f, 0xf0, 0x0a, 0x02, 0x0c, 0x32, 0x0f, 0xc4, 0x01, 0x61, 0x0f, 0x38, - 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0x42, 0x0f, 0xf6, 0x0d, 0x7c, 0x00, 0x48, - 0x04, 0xc4, 0x0f, 0xc5, 0x05, 0x6e, 0x04, 0xb7, 0x0f, 0xc4, 0x01, 0xaa, - 0x00, 0x00, 0x0f, 0xc4, 0x06, 0x96, 0x0f, 0xc5, 0x05, 0x71, 0x04, 0xb7, - 0x0f, 0xc4, 0x01, 0xab, 0x00, 0x00, 0x04, 0x04, 0x0f, 0xc0, 0x00, 0x2b, - 0x08, 0xc4, 0x02, 0x80, 0x0f, 0xc8, 0x00, 0x40, 0x0f, 0xf7, 0x05, 0x23, - 0x07, 0xb7, 0x0f, 0xc8, 0x00, 0x2d, 0x1f, 0xc8, 0x00, 0xd9, 0x0f, 0xf0, - 0x0a, 0x02, 0x0c, 0x32, 0x00, 0x08, 0x0f, 0xfa, 0x19, 0x6d, 0x00, 0x09, - 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0x62, 0x0b, 0x22, 0x00, 0x63, 0x09, 0x80, - 0x02, 0x36, 0x04, 0xc4, 0x0f, 0xf7, 0x00, 0x8f, 0x05, 0x84, 0x0f, 0xf6, - 0x0d, 0x7c, 0x00, 0x08, 0x08, 0xc4, 0x01, 0x80, 0x0f, 0xc8, 0x00, 0x3f, - 0x0f, 0xf7, 0x05, 0x23, 0x0f, 0xc4, 0x01, 0xa0, 0x0f, 0x38, 0x00, 0x00, - 0x01, 0x3a, 0x00, 0x88, 0x0f, 0x39, 0x00, 0x44, 0x0c, 0x00, 0x04, 0x3b, - 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf7, 0x0c, 0x97, 0x0f, 0xc8, 0x00, 0x2b, - 0x03, 0x3b, 0x1f, 0xf9, 0x0b, 0x02, 0x0f, 0xf0, 0x0a, 0x07, 0x01, 0x32, - 0x0f, 0xc4, 0x06, 0xe9, 0x0f, 0xc5, 0x07, 0x51, 0x04, 0xb7, 0x0f, 0xc4, - 0x06, 0xec, 0x0f, 0xc5, 0x07, 0x54, 0x04, 0xb7, 0x0f, 0xc4, 0x06, 0xe3, - 0x0f, 0xc5, 0x07, 0x3f, 0x04, 0xb7, 0x0f, 0xc4, 0x06, 0xe6, 0x0f, 0xc5, - 0x07, 0x42, 0x04, 0xb7, 0x0f, 0xf9, 0x0b, 0x16, 0x0f, 0xc4, 0x06, 0xf5, - 0x0f, 0xc5, 0x07, 0x51, 0x04, 0xb7, 0x0f, 0xc4, 0x06, 0xf8, 0x0f, 0xc5, - 0x07, 0x54, 0x04, 0xb7, 0x0f, 0xc4, 0x06, 0xef, 0x0f, 0xc5, 0x07, 0x3f, - 0x04, 0xb7, 0x0f, 0xc4, 0x06, 0xf2, 0x0f, 0xc5, 0x07, 0x42, 0x04, 0xb7, - 0x07, 0x37, 0x1f, 0xf9, 0x0b, 0x2d, 0x0f, 0xf7, 0x09, 0x2f, 0x0f, 0xf7, - 0x09, 0x62, 0x0f, 0xf7, 0x09, 0xc3, 0x0f, 0xc8, 0x00, 0x2b, 0x03, 0x3b, - 0x1f, 0xfb, 0x0b, 0xfa, 0x00, 0x44, 0x0f, 0xfa, 0x0c, 0x0e, 0x0b, 0x08, - 0x0f, 0xf6, 0x05, 0x5c, 0x00, 0x48, 0x0f, 0xf9, 0x0b, 0x4b, 0x0f, 0xf7, - 0x09, 0x2f, 0x0f, 0xf7, 0x09, 0xc3, 0x0f, 0xfb, 0x0b, 0xff, 0x0f, 0xf7, - 0x09, 0x2f, 0x0f, 0xf7, 0x09, 0x62, 0x0f, 0xc8, 0x00, 0x2b, 0x03, 0x3b, - 0x1f, 0xf7, 0x0a, 0x0a, 0x0f, 0xfb, 0x0b, 0xff, 0x0f, 0xc8, 0x00, 0x2b, - 0x03, 0x3b, 0x1f, 0xf7, 0x0a, 0x72, 0x0f, 0xfb, 0x17, 0x68, 0x0f, 0xfb, - 0x0b, 0xff, 0x00, 0x44, 0x0f, 0xfa, 0x0c, 0x0e, 0x0b, 0x08, 0x0f, 0xc4, - 0x01, 0x9e, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x00, 0x14, - 0x10, 0x40, 0x0f, 0x39, 0x0f, 0xc8, 0x00, 0x50, 0x08, 0xbb, 0x07, 0xba, - 0x01, 0x08, 0x0f, 0xc8, 0x00, 0x50, 0x03, 0x3b, 0x1f, 0xf9, 0x0b, 0x7e, - 0x0f, 0xc4, 0x01, 0x9e, 0x07, 0x36, 0x0b, 0x08, 0x1c, 0x27, 0x19, 0xa7, - 0x19, 0x88, 0x0c, 0x21, 0x0f, 0xd2, 0x00, 0x6f, 0x1f, 0xc4, 0x01, 0xb7, - 0x1f, 0xf8, 0x0b, 0x75, 0x10, 0x40, 0x0f, 0xc4, 0x05, 0xe8, 0x0b, 0x21, - 0x00, 0x14, 0x14, 0x04, 0x1f, 0xc0, 0x00, 0x2b, 0x1f, 0x39, 0x07, 0xba, - 0x01, 0x48, 0x0f, 0xf0, 0x0a, 0x07, 0x00, 0x32, 0x0f, 0xc4, 0x01, 0x9e, - 0x0f, 0x38, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0x9e, 0x0f, 0xc5, 0x07, 0x5e, - 0x0b, 0xa1, 0x0b, 0xd2, 0x11, 0x3a, 0x11, 0x08, 0x0f, 0x39, 0x0f, 0xc8, - 0x00, 0x3c, 0x08, 0xbb, 0x0f, 0xc4, 0x01, 0x7f, 0x0b, 0x21, 0x00, 0x00, - 0x00, 0x15, 0x0f, 0xc4, 0x01, 0xbc, 0x0b, 0x21, 0x00, 0x19, 0x10, 0x59, - 0x09, 0xc0, 0x09, 0xe1, 0x00, 0x93, 0x14, 0x04, 0x1f, 0xc0, 0x00, 0x32, - 0x0f, 0xc8, 0x00, 0x3c, 0x03, 0x3b, 0x1f, 0x39, 0x0f, 0xc4, 0x01, 0xb0, - 0x0f, 0xc0, 0x00, 0x96, 0x0f, 0xc4, 0x01, 0x80, 0x0b, 0x21, 0x00, 0x15, - 0x1f, 0xc4, 0x01, 0x83, 0x1f, 0xfa, 0x04, 0x60, 0x10, 0x40, 0x0f, 0x39, - 0x0f, 0xc4, 0x01, 0x78, 0x0b, 0x21, 0x0f, 0xd3, 0x00, 0xfa, 0x1f, 0xf9, - 0x0b, 0xcd, 0x0f, 0xc5, 0x01, 0xbe, 0x0f, 0xc4, 0x01, 0xbd, 0x0b, 0x21, - 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x0f, 0xd3, 0x00, 0x28, 0x1f, 0x39, - 0x0f, 0xd2, 0x00, 0x3c, 0x1f, 0xc0, 0x00, 0x28, 0x10, 0x01, 0x0f, 0xc4, - 0x05, 0x86, 0x0b, 0x21, 0x00, 0x93, 0x1b, 0x61, 0x10, 0x59, 0x19, 0xc1, - 0x0b, 0x61, 0x02, 0x93, 0x1f, 0x39, 0x01, 0x38, 0x01, 0x08, 0x0f, 0xc8, - 0x00, 0x3c, 0x08, 0xbb, 0x0f, 0xfb, 0x0b, 0xac, 0x07, 0x37, 0x00, 0x48, - 0x11, 0x08, 0x0c, 0x21, 0x0f, 0xc4, 0x01, 0x2e, 0x0b, 0x20, 0x09, 0xc2, - 0x09, 0xc8, 0x00, 0x21, 0x0b, 0x19, 0x09, 0xc2, 0x07, 0xba, 0x00, 0x88, - 0x0f, 0xc8, 0x00, 0x3c, 0x0f, 0xf7, 0x00, 0x3a, 0x1f, 0xf9, 0x0b, 0xed, - 0x0f, 0xd5, 0x00, 0x32, 0x1f, 0xf9, 0x0b, 0xf2, 0x0f, 0x39, 0x02, 0x3a, - 0x00, 0x48, 0x0f, 0xfb, 0x11, 0x41, 0x0f, 0x39, 0x01, 0x3a, 0x01, 0x08, - 0x02, 0x3a, 0x00, 0x08, 0x02, 0xba, 0x00, 0x08, 0x0f, 0x39, 0x0f, 0x39, - 0x0f, 0xf7, 0x0a, 0x0a, 0x0f, 0xf7, 0x0a, 0x72, 0x0f, 0x39, 0x09, 0x3b, - 0x0f, 0xfb, 0x0c, 0xf8, 0x0f, 0xfb, 0x13, 0xae, 0x0f, 0xfb, 0x0c, 0x26, - 0x0f, 0xfb, 0x02, 0x35, 0x04, 0x3b, 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf7, - 0x0c, 0x97, 0x0f, 0x39, 0x00, 0x04, 0x0c, 0x00, 0x09, 0x3b, 0x0f, 0xf6, - 0x00, 0x3a, 0x00, 0x48, 0x1f, 0x39, 0x02, 0x14, 0x1f, 0x39, 0x0f, 0xc4, - 0x05, 0x86, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc8, 0x09, 0xe1, 0x02, 0x96, - 0x1f, 0xf9, 0x0c, 0x22, 0x0f, 0x38, 0x0c, 0x00, 0x04, 0x04, 0x00, 0x05, - 0x0b, 0x40, 0x0f, 0x39, 0x0f, 0xc4, 0x00, 0x56, 0x02, 0x37, 0x0f, 0xc5, - 0x00, 0x56, 0x04, 0xb7, 0x0f, 0xc4, 0x00, 0x74, 0x04, 0xb7, 0x0f, 0xc4, - 0x00, 0xe9, 0x00, 0x37, 0x03, 0x37, 0x0f, 0xc4, 0x05, 0xd7, 0x07, 0x05, - 0x0b, 0x83, 0x0b, 0x83, 0x0f, 0xc3, 0x07, 0xff, 0x0f, 0xf7, 0x01, 0x39, - 0x0f, 0xc4, 0x00, 0xec, 0x0f, 0xc5, 0x00, 0xef, 0x04, 0xb7, 0x0f, 0xc4, - 0x00, 0xec, 0x00, 0x37, 0x0f, 0xc4, 0x00, 0xe9, 0x0f, 0xf7, 0x02, 0x93, - 0x0f, 0xc4, 0x00, 0xef, 0x02, 0xb7, 0x0f, 0xc4, 0x00, 0xf2, 0x00, 0x37, - 0x0f, 0xc4, 0x06, 0xb1, 0x0f, 0xf7, 0x02, 0x81, 0x01, 0x36, 0x03, 0x08, - 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x64, 0x0f, 0x38, 0x0c, 0x00, 0x0f, 0xc4, - 0x01, 0xa2, 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xfb, 0x0d, 0xa1, 0x0f, 0xf1, - 0x05, 0x0d, 0x0a, 0x48, 0x0f, 0xf1, 0x05, 0x0e, 0x0f, 0xc4, 0x01, 0x9a, - 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0x9b, 0x0a, 0x49, 0x0f, 0xf6, 0x03, 0xcd, - 0x0c, 0x40, 0x0f, 0xc4, 0x01, 0x72, 0x0c, 0x00, 0x0f, 0xc4, 0x07, 0x06, - 0x0b, 0x21, 0x00, 0x14, 0x1f, 0xc4, 0x01, 0x64, 0x1c, 0x21, 0x1b, 0x19, - 0x19, 0xc8, 0x0f, 0xf6, 0x00, 0x47, 0x00, 0x4b, 0x10, 0x08, 0x0f, 0xc4, - 0x01, 0x83, 0x0b, 0x21, 0x00, 0x15, 0x10, 0x08, 0x0f, 0xc4, 0x01, 0x9c, - 0x0c, 0x21, 0x0b, 0x19, 0x09, 0xc0, 0x09, 0xc9, 0x0f, 0xc4, 0x01, 0x9d, - 0x0b, 0x21, 0x0c, 0x51, 0x09, 0xca, 0x0f, 0xc4, 0x01, 0xbf, 0x0b, 0x21, - 0x0c, 0x91, 0x09, 0xc8, 0x0c, 0x80, 0x0f, 0xf0, 0x05, 0x02, 0x07, 0x36, - 0x0c, 0xb2, 0x1f, 0xc4, 0x00, 0xd7, 0x1a, 0x85, 0x1b, 0x8b, 0x1b, 0x83, - 0x1b, 0x83, 0x1c, 0x83, 0x1c, 0xca, 0x0f, 0xf6, 0x00, 0x42, 0x01, 0xcb, - 0x1f, 0xf0, 0x05, 0x08, 0x1c, 0xb2, 0x0f, 0xc4, 0x01, 0xa9, 0x0c, 0x00, - 0x0f, 0xfb, 0x0e, 0x59, 0x0f, 0xfb, 0x13, 0x6f, 0x0f, 0xfb, 0x0e, 0xd8, - 0x0f, 0xc4, 0x01, 0xa9, 0x0b, 0x09, 0x00, 0xb6, 0x00, 0x08, 0x0f, 0xc8, - 0x1f, 0xe8, 0x01, 0x37, 0x0f, 0xc4, 0x00, 0xda, 0x00, 0x37, 0x03, 0x37, - 0x0f, 0xc4, 0x06, 0xb1, 0x05, 0xb7, 0x0f, 0xf7, 0x01, 0x39, 0x0f, 0xc4, - 0x00, 0xdd, 0x00, 0x37, 0x0f, 0xc4, 0x01, 0xa9, 0x0b, 0x08, 0x00, 0x09, - 0x0c, 0x21, 0x02, 0xdf, 0x1f, 0xc9, 0x0f, 0xff, 0x0f, 0xc4, 0x01, 0x44, - 0x0b, 0x21, 0x0c, 0x20, 0x09, 0xc2, 0x09, 0xc8, 0x0b, 0x21, 0x0c, 0x59, - 0x09, 0xc0, 0x09, 0xc9, 0x0f, 0xc5, 0x02, 0x1f, 0x0f, 0xc4, 0x02, 0x21, - 0x02, 0xbe, 0x0f, 0xf7, 0x00, 0x2d, 0x0c, 0x03, 0x0c, 0x43, 0x0f, 0xf1, - 0x0a, 0x18, 0x08, 0x84, 0x0b, 0x21, 0x0f, 0xd9, 0x00, 0x5c, 0x09, 0xc4, - 0x0a, 0x40, 0x0f, 0xf1, 0x0a, 0x19, 0x0f, 0xd9, 0x00, 0x60, 0x09, 0xc4, - 0x0a, 0x40, 0x0f, 0xf1, 0x0a, 0x1a, 0x0f, 0xd9, 0x00, 0x64, 0x09, 0xc4, - 0x0a, 0x40, 0x0f, 0xf1, 0x0a, 0x1b, 0x0f, 0xd9, 0x00, 0x68, 0x09, 0xc4, - 0x0a, 0x40, 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0x34, 0x0b, 0x88, 0x0b, 0x89, - 0x0f, 0xf0, 0x05, 0x01, 0x0c, 0x32, 0x0f, 0xf0, 0x05, 0x00, 0x0c, 0x72, - 0x0f, 0xf0, 0x05, 0x07, 0x0c, 0x32, 0x0f, 0xf0, 0x05, 0x06, 0x0f, 0x38, - 0x0c, 0x72, 0x0f, 0xfb, 0x0d, 0xb2, 0x0f, 0xc4, 0x01, 0x9f, 0x0b, 0x22, - 0x0f, 0xf1, 0x0a, 0x04, 0x0a, 0x62, 0x00, 0x48, 0x00, 0x09, 0x00, 0x21, - 0x0f, 0xc4, 0x01, 0xad, 0x0b, 0x21, 0x00, 0xd9, 0x09, 0xca, 0x09, 0xe1, - 0x03, 0x12, 0x19, 0xca, 0x0c, 0x80, 0x04, 0x24, 0x00, 0x21, 0x09, 0x94, - 0x1f, 0xf9, 0x0d, 0x46, 0x00, 0x49, 0x08, 0x24, 0x09, 0x94, 0x1f, 0xf8, - 0x0d, 0x46, 0x10, 0x08, 0x03, 0xe4, 0x0c, 0xa1, 0x09, 0x95, 0x09, 0x80, - 0x10, 0x08, 0x0f, 0xe4, 0x00, 0x80, 0x09, 0x94, 0x1f, 0xf9, 0x0d, 0x46, - 0x00, 0x49, 0x0f, 0xe4, 0x00, 0x40, 0x09, 0x94, 0x10, 0x08, 0x0f, 0xc4, - 0x01, 0x42, 0x00, 0x21, 0x0b, 0x95, 0x10, 0x08, 0x0b, 0x95, 0x10, 0x08, - 0x08, 0x84, 0x0b, 0x0a, 0x0c, 0xa1, 0x0f, 0xc4, 0x01, 0xa4, 0x0b, 0x15, - 0x10, 0x08, 0x1c, 0x80, 0x00, 0x21, 0x0c, 0x54, 0x1f, 0xf9, 0x0d, 0x57, - 0x0c, 0x14, 0x1f, 0xf9, 0x0d, 0x57, 0x0f, 0xc4, 0x01, 0xaf, 0x0b, 0x0b, - 0x0f, 0xc5, 0x01, 0xae, 0x0c, 0xe1, 0x0b, 0x56, 0x00, 0x00, 0x0f, 0x38, - 0x1c, 0xc1, 0x0f, 0xc4, 0x01, 0xaf, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, - 0x09, 0xe1, 0x0f, 0xc4, 0x01, 0xb0, 0x0b, 0x17, 0x1f, 0x39, 0x0f, 0xc4, - 0x01, 0xa5, 0x0c, 0x67, 0x09, 0xa2, 0x0c, 0x25, 0x09, 0x80, 0x0c, 0x21, - 0x00, 0x15, 0x01, 0x48, 0x01, 0x38, 0x11, 0x88, 0x0f, 0x39, 0x0f, 0xc5, - 0x01, 0xa4, 0x08, 0x84, 0x00, 0x61, 0x00, 0xe2, 0x0b, 0x19, 0x09, 0xe4, - 0x09, 0x80, 0x09, 0x81, 0x0f, 0xf1, 0x0a, 0x04, 0x0f, 0xc5, 0x01, 0xb1, - 0x0f, 0xc4, 0x01, 0x9f, 0x0a, 0x48, 0x0c, 0x01, 0x0c, 0x22, 0x0f, 0xe4, - 0x00, 0xf0, 0x09, 0x80, 0x0f, 0xe4, 0x00, 0xc0, 0x09, 0xa1, 0x0f, 0xd5, - 0x00, 0xc0, 0x0f, 0x39, 0x00, 0x61, 0x00, 0xe2, 0x0f, 0xc4, 0x01, 0xa4, - 0x0b, 0x19, 0x09, 0xe4, 0x09, 0x80, 0x08, 0x84, 0x09, 0x80, 0x09, 0xa1, - 0x00, 0x14, 0x1f, 0xc4, 0x01, 0x82, 0x1b, 0x21, 0x10, 0x59, 0x19, 0xc0, - 0x0f, 0xc4, 0x01, 0x9f, 0x0f, 0xc0, 0x00, 0xf0, 0x0f, 0xe1, 0x00, 0xc0, - 0x0f, 0xd5, 0x00, 0xc0, 0x0f, 0x39, 0x00, 0x61, 0x00, 0xe2, 0x08, 0x84, - 0x0b, 0x19, 0x09, 0xe4, 0x0f, 0xf0, 0x0a, 0x05, 0x07, 0xb6, 0x09, 0xb2, - 0x0f, 0xcb, 0x00, 0xac, 0x1f, 0xcb, 0x02, 0xb0, 0x0f, 0xf0, 0x0a, 0x06, - 0x0c, 0xf2, 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0xa2, 0x0b, 0x21, 0x00, 0x15, - 0x1f, 0xf9, 0x0d, 0xf2, 0x0f, 0xfb, 0x0d, 0x6d, 0x1f, 0x39, 0x07, 0xb7, - 0x0f, 0xcb, 0x00, 0xac, 0x1f, 0xcb, 0x02, 0xb0, 0x0f, 0xf1, 0x0a, 0x06, - 0x0a, 0x4a, 0x0c, 0xa1, 0x0c, 0xd8, 0x09, 0xca, 0x00, 0x21, 0x00, 0x11, - 0x09, 0xcb, 0x0f, 0xc4, 0x01, 0x42, 0x0c, 0x82, 0x0c, 0xc2, 0x00, 0x21, - 0x0c, 0x95, 0x1f, 0xf9, 0x0d, 0xe1, 0x0c, 0xd5, 0x1f, 0xf9, 0x0d, 0xe1, - 0x0f, 0xf1, 0x0a, 0x05, 0x08, 0x84, 0x00, 0xe2, 0x0a, 0x64, 0x09, 0x80, - 0x08, 0x84, 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0xa4, 0x0b, 0x14, 0x1f, 0xf9, - 0x0d, 0xe5, 0x0f, 0xc4, 0x01, 0xa3, 0x0f, 0x38, 0x00, 0x00, 0x0f, 0xc4, - 0x01, 0xa3, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x00, 0xd5, - 0x1f, 0x39, 0x0f, 0xc4, 0x01, 0xa2, 0x0f, 0xc0, 0x0a, 0xaa, 0x0f, 0x39, - 0x0f, 0xf9, 0x0d, 0x88, 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0xb6, 0x00, 0x00, - 0x0f, 0xc4, 0x01, 0xb3, 0x00, 0x00, 0x0f, 0xc4, 0x01, 0xb5, 0x00, 0x00, - 0x0f, 0xc4, 0x01, 0xb2, 0x08, 0x00, 0x0f, 0xc4, 0x01, 0xb4, 0x0f, 0x38, - 0x08, 0x00, 0x0f, 0xc4, 0x01, 0xb2, 0x0b, 0x21, 0x00, 0xd9, 0x09, 0xc8, - 0x09, 0xe1, 0x03, 0x12, 0x19, 0xc8, 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0xb4, - 0x0b, 0x21, 0x00, 0xd3, 0x09, 0xc8, 0x19, 0xe1, 0x13, 0x19, 0x19, 0xc8, - 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0xb1, 0x0b, 0x22, 0x0f, 0xe4, 0x00, 0x30, - 0x09, 0xa1, 0x0f, 0xd5, 0x00, 0x30, 0x1f, 0x39, 0x03, 0xe4, 0x09, 0xa1, - 0x0f, 0xc4, 0x01, 0xb2, 0x0b, 0x14, 0x09, 0x80, 0x0f, 0xc4, 0x01, 0xb3, - 0x0b, 0x21, 0x00, 0x19, 0x10, 0x59, 0x09, 0xc0, 0x09, 0xc8, 0x09, 0xa1, - 0x0f, 0xc4, 0x01, 0xb4, 0x0b, 0x14, 0x09, 0x80, 0x0f, 0xc4, 0x01, 0xb5, - 0x0b, 0x21, 0x00, 0x19, 0x10, 0x59, 0x09, 0xc0, 0x09, 0xc9, 0x0c, 0x61, - 0x03, 0x17, 0x1f, 0x39, 0x0c, 0x17, 0x1f, 0xf9, 0x0d, 0xf5, 0x0f, 0xc4, - 0x01, 0xb6, 0x00, 0x40, 0x0f, 0x39, 0x00, 0xe2, 0x0c, 0x24, 0x09, 0xa1, - 0x09, 0xa7, 0x09, 0x99, 0x09, 0xc9, 0x0f, 0xc4, 0x01, 0xa4, 0x0b, 0x19, - 0x09, 0xe4, 0x09, 0x80, 0x08, 0x84, 0x09, 0x80, 0x0f, 0xc4, 0x01, 0xad, - 0x0b, 0x21, 0x0c, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x03, 0x12, 0x0f, 0x38, - 0x19, 0xc0, 0x0f, 0xf1, 0x05, 0x0f, 0x00, 0x04, 0x0f, 0x00, 0x05, 0x84, - 0x0f, 0xc2, 0x08, 0x00, 0x0a, 0x42, 0x0f, 0xf1, 0x05, 0x10, 0x0f, 0xf6, - 0x02, 0x99, 0x00, 0x02, 0x05, 0x21, 0x0a, 0x51, 0x01, 0x36, 0x09, 0xc8, - 0x0f, 0xc4, 0x00, 0xe6, 0x00, 0x37, 0x05, 0x84, 0x0b, 0xaa, 0x0b, 0xab, - 0x0f, 0xee, 0x01, 0xc0, 0x0b, 0xa7, 0x09, 0xa6, 0x09, 0x89, 0x0c, 0x21, - 0x0f, 0xd7, 0x1f, 0xff, 0x0f, 0xd0, 0x1f, 0xfd, 0x1f, 0xd9, 0x1f, 0xfd, - 0x10, 0x09, 0x09, 0xe1, 0x0f, 0xd7, 0x1f, 0xf4, 0x1f, 0xe1, 0x1f, 0xf5, - 0x0f, 0xd9, 0x0a, 0x80, 0x09, 0xee, 0x0e, 0x48, 0x0c, 0x2a, 0x00, 0x2b, - 0x0f, 0xee, 0x01, 0xc0, 0x03, 0x61, 0x0c, 0x57, 0x1c, 0x61, 0x0f, 0xd9, - 0x0a, 0x00, 0x09, 0xee, 0x0f, 0xc4, 0x01, 0x46, 0x0b, 0xa1, 0x0e, 0x18, - 0x09, 0xca, 0x0b, 0xa1, 0x0e, 0x53, 0x09, 0xcb, 0x10, 0x21, 0x1c, 0x98, - 0x19, 0xca, 0x1c, 0xd1, 0x19, 0xcb, 0x0f, 0xc4, 0x01, 0x48, 0x0c, 0xa1, - 0x0b, 0x98, 0x0c, 0xe1, 0x0b, 0x93, 0x1f, 0xf9, 0x0e, 0xd6, 0x0f, 0xc4, - 0x01, 0x46, 0x0e, 0x02, 0x0e, 0x42, 0x0f, 0xf6, 0x00, 0x47, 0x01, 0x8b, - 0x1f, 0xf9, 0x0e, 0xd5, 0x0f, 0xf0, 0x05, 0x04, 0x0c, 0x32, 0x0f, 0xf0, - 0x05, 0x05, 0x07, 0x36, 0x0c, 0x72, 0x1f, 0xf9, 0x0e, 0xc2, 0x0f, 0xf6, - 0x00, 0x42, 0x01, 0xcb, 0x1f, 0xf0, 0x05, 0x0a, 0x1c, 0x32, 0x1f, 0xf0, - 0x05, 0x0b, 0x0f, 0xf8, 0x0e, 0xd5, 0x1c, 0x72, 0x0f, 0xf6, 0x00, 0x42, - 0x01, 0xcb, 0x0f, 0xc4, 0x00, 0x50, 0x0a, 0x85, 0x1f, 0xf0, 0x05, 0x0a, - 0x1b, 0xb2, 0x1f, 0xf0, 0x05, 0x0b, 0x1b, 0xb2, 0x0f, 0xc4, 0x00, 0x52, - 0x01, 0x3e, 0x0f, 0xf7, 0x00, 0x2d, 0x0c, 0x03, 0x0c, 0x43, 0x00, 0x3f, - 0x00, 0x04, 0x0b, 0x39, 0x0f, 0x39, 0x0f, 0xc8, 0x00, 0x80, 0x0f, 0xf7, - 0x05, 0x5c, 0x0f, 0xf1, 0x03, 0x2d, 0x0f, 0xc4, 0x01, 0xc2, 0x0a, 0x40, - 0x0f, 0xf0, 0x04, 0x00, 0x00, 0x72, 0x0f, 0xf0, 0x05, 0x11, 0x00, 0x72, - 0x0f, 0xf1, 0x0a, 0x1f, 0x03, 0xe2, 0x00, 0x61, 0x0f, 0xc4, 0x01, 0xc0, - 0x0a, 0x64, 0x09, 0x80, 0x0f, 0xc4, 0x05, 0x46, 0x09, 0x99, 0x09, 0xc0, - 0x0f, 0xc4, 0x05, 0x47, 0x00, 0x00, 0x02, 0xba, 0x00, 0x08, 0x0f, 0xf7, - 0x0a, 0xc2, 0x0f, 0xf7, 0x0b, 0xb6, 0x0f, 0xfb, 0x0f, 0x73, 0x09, 0x36, - 0x00, 0x48, 0x0f, 0xf6, 0x05, 0x5c, 0x00, 0x48, 0x0f, 0xf1, 0x03, 0x2d, - 0x0f, 0xc4, 0x01, 0xc3, 0x0a, 0x40, 0x0f, 0xc4, 0x06, 0x94, 0x0f, 0xc5, - 0x01, 0x4a, 0x0b, 0xa1, 0x0b, 0xe0, 0x0f, 0xf0, 0x03, 0x2a, 0x09, 0xf2, - 0x0b, 0xa1, 0x0b, 0xd9, 0x0f, 0xf0, 0x03, 0x29, 0x09, 0xf2, 0x0f, 0xf6, - 0x05, 0x5c, 0x00, 0x48, 0x0f, 0xc4, 0x06, 0x94, 0x0f, 0xf0, 0x03, 0x2a, - 0x0b, 0xb2, 0x0f, 0xf0, 0x03, 0x29, 0x0b, 0xb2, 0x0f, 0xf0, 0x05, 0x11, - 0x00, 0x32, 0x0f, 0xf0, 0x04, 0x00, 0x00, 0x32, 0x0f, 0xf6, 0x05, 0x5c, - 0x00, 0x48, 0x0f, 0xf0, 0x0a, 0x07, 0x04, 0x32, 0x0f, 0xc4, 0x04, 0xc8, - 0x0b, 0x21, 0x00, 0x92, 0x1f, 0xf9, 0x0f, 0x3b, 0x04, 0x04, 0x0f, 0xc0, - 0x00, 0x32, 0x09, 0x04, 0x0f, 0xf8, 0x0f, 0x42, 0x00, 0x80, 0x04, 0x04, - 0x0f, 0xc0, 0x00, 0x47, 0x0f, 0xfb, 0x16, 0x6c, 0x09, 0x04, 0x00, 0x00, - 0x08, 0xc4, 0x02, 0xc0, 0x0f, 0xc8, 0x00, 0x41, 0x0f, 0xf7, 0x05, 0x23, - 0x0f, 0xc4, 0x01, 0xaf, 0x00, 0x00, 0x0f, 0xc4, 0x05, 0x86, 0x00, 0x00, - 0x02, 0x36, 0x04, 0xc4, 0x0f, 0xc4, 0x05, 0x6e, 0x00, 0x37, 0x0f, 0xc4, - 0x06, 0x96, 0x02, 0x37, 0x0f, 0xc4, 0x05, 0x71, 0x00, 0x37, 0x0f, 0xf7, - 0x0b, 0xdb, 0x0f, 0xf7, 0x05, 0xb9, 0x0f, 0xc4, 0x01, 0x71, 0x0b, 0x21, - 0x00, 0x15, 0x0f, 0xc4, 0x01, 0xc3, 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0xc2, - 0x0b, 0x11, 0x09, 0xe1, 0x00, 0xd9, 0x10, 0xd9, 0x0f, 0xc4, 0x01, 0xc4, - 0x09, 0xc0, 0x0f, 0xfa, 0x0e, 0x43, 0x09, 0xc8, 0x07, 0xba, 0x01, 0x88, - 0x0f, 0x39, 0x0f, 0xfb, 0x10, 0x36, 0x0f, 0xc4, 0x01, 0x07, 0x0f, 0xc5, - 0x01, 0x04, 0x0b, 0x83, 0x0b, 0x83, 0x0f, 0xfa, 0x10, 0xa4, 0x0b, 0x83, - 0x0f, 0xfb, 0x10, 0x36, 0x0f, 0xc4, 0x05, 0x49, 0x0b, 0x08, 0x0f, 0xe1, - 0x07, 0x55, 0x0c, 0x17, 0x1f, 0xf9, 0x0f, 0x8f, 0x00, 0x21, 0x0c, 0x18, - 0x09, 0xc8, 0x00, 0x11, 0x0f, 0xf8, 0x0f, 0x94, 0x09, 0xc9, 0x00, 0x09, - 0x0f, 0xe1, 0x08, 0x00, 0x0c, 0x11, 0x09, 0xc8, 0x00, 0xb7, 0x0f, 0xc4, - 0x01, 0x0a, 0x02, 0xb7, 0x07, 0x04, 0x0f, 0xc2, 0x0a, 0xab, 0x0f, 0xc2, - 0x0a, 0xaa, 0x0f, 0xc2, 0x07, 0xfe, 0x0f, 0xf7, 0x02, 0x70, 0x04, 0x44, - 0x00, 0x08, 0x03, 0x61, 0x0b, 0x14, 0x01, 0x36, 0x10, 0x88, 0x0f, 0xc4, - 0x00, 0xf5, 0x00, 0x37, 0x01, 0x08, 0x00, 0xb6, 0x00, 0x09, 0x0f, 0xc4, - 0x00, 0xf5, 0x0f, 0xf7, 0x02, 0x93, 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x4a, - 0x0c, 0x02, 0x00, 0xb6, 0x0c, 0x42, 0x05, 0x37, 0x04, 0x37, 0x03, 0x37, - 0x00, 0x08, 0x00, 0xb6, 0x00, 0x09, 0x05, 0x37, 0x04, 0x37, 0x0f, 0xc4, - 0x00, 0xf8, 0x00, 0x37, 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x63, 0x03, 0x36, - 0x0c, 0x00, 0x00, 0xb7, 0x05, 0x37, 0x04, 0x37, 0x0f, 0xc4, 0x06, 0xae, - 0x00, 0x37, 0x0f, 0xc4, 0x05, 0x74, 0x00, 0x37, 0x0f, 0xc4, 0x00, 0xf8, - 0x02, 0x37, 0x04, 0x44, 0x02, 0xc8, 0x02, 0xe1, 0x0b, 0x14, 0x01, 0x36, - 0x13, 0x48, 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x34, 0x0c, 0x02, 0x0f, 0xfa, - 0x0c, 0xf8, 0x0c, 0x42, 0x0f, 0x39, 0x0f, 0xf0, 0x05, 0x16, 0x0c, 0x32, - 0x0f, 0xc4, 0x01, 0x4c, 0x0c, 0x00, 0x0f, 0xf0, 0x05, 0x18, 0x0f, 0x38, - 0x0c, 0x32, 0x0f, 0xf0, 0x05, 0x32, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x33, - 0x0f, 0x38, 0x00, 0x32, 0x0f, 0xf1, 0x05, 0x12, 0x0f, 0xc4, 0x01, 0x4c, - 0x0c, 0x21, 0x0b, 0x15, 0x1f, 0xfb, 0x0f, 0xe1, 0x1f, 0xf1, 0x05, 0x12, - 0x0a, 0x49, 0x0f, 0xf1, 0x05, 0x13, 0x0f, 0xc4, 0x01, 0x4c, 0x00, 0x61, - 0x0b, 0x19, 0x09, 0xc0, 0x0f, 0x38, 0x0a, 0x48, 0x0f, 0xfb, 0x0f, 0xf2, - 0x0c, 0x2a, 0x0c, 0x6b, 0x0f, 0xee, 0x02, 0x40, 0x03, 0xe2, 0x0c, 0x24, - 0x0f, 0xee, 0x0a, 0x7e, 0x09, 0x8a, 0x0f, 0xe2, 0x0f, 0xfc, 0x0c, 0x64, - 0x09, 0x89, 0x0e, 0x24, 0x09, 0x88, 0x00, 0x2a, 0x00, 0x2c, 0x0c, 0x0b, - 0x00, 0x21, 0x0c, 0x16, 0x19, 0xcb, 0x0c, 0xeb, 0x0c, 0xed, 0x0f, 0xee, - 0x0c, 0x80, 0x0f, 0xc4, 0x01, 0x0d, 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0x56, - 0x19, 0xc9, 0x0c, 0x6b, 0x0c, 0x6d, 0x0f, 0xee, 0x0c, 0xc0, 0x0c, 0x82, - 0x0c, 0xa7, 0x09, 0xa1, 0x05, 0x84, 0x0f, 0xee, 0x0a, 0xd3, 0x0e, 0x02, - 0x0e, 0x42, 0x0e, 0x99, 0x0f, 0x38, 0x09, 0xc2, 0x04, 0x44, 0x0b, 0x21, - 0x03, 0x54, 0x1f, 0xf9, 0x10, 0x54, 0x0f, 0xc4, 0x05, 0x49, 0x0b, 0x08, - 0x0f, 0xfa, 0x0f, 0xe1, 0x00, 0x09, 0x0f, 0xfb, 0x10, 0x06, 0x0f, 0xc4, - 0x01, 0x07, 0x00, 0x37, 0x0f, 0xc4, 0x00, 0xfe, 0x00, 0x37, 0x0f, 0xc4, - 0x00, 0xfb, 0x01, 0xb7, 0x0f, 0xc4, 0x01, 0x01, 0x01, 0xb7, 0x0f, 0xc4, - 0x01, 0x0a, 0x01, 0xb7, 0x0f, 0xf9, 0x0f, 0xeb, 0x0f, 0xc4, 0x05, 0x49, - 0x0b, 0x21, 0x00, 0x51, 0x09, 0xc8, 0x00, 0x09, 0x00, 0x04, 0x0c, 0x00, - 0x0f, 0xfb, 0x0f, 0xe1, 0x0f, 0xfb, 0x10, 0x06, 0x0f, 0xf7, 0x04, 0x66, - 0x03, 0x37, 0x0f, 0xc4, 0x00, 0xfb, 0x00, 0x37, 0x0f, 0xfb, 0x10, 0x99, - 0x0f, 0xfb, 0x10, 0x06, 0x05, 0x37, 0x0f, 0xc4, 0x00, 0xfe, 0x00, 0x37, - 0x06, 0x37, 0x03, 0x37, 0x0f, 0xfb, 0x10, 0x99, 0x0f, 0xfb, 0x10, 0x06, - 0x0f, 0xf7, 0x04, 0x66, 0x05, 0x37, 0x0f, 0xc4, 0x01, 0x01, 0x00, 0x37, - 0x06, 0x37, 0x0f, 0xc4, 0x01, 0x07, 0x00, 0x37, 0x0f, 0xf7, 0x04, 0x72, - 0x0f, 0xf7, 0x04, 0x6f, 0x04, 0x37, 0x0f, 0xc4, 0x01, 0x07, 0x0f, 0xf7, - 0x02, 0x81, 0x07, 0x04, 0x0f, 0xc2, 0x05, 0x3b, 0x0f, 0xc2, 0x08, 0x8c, - 0x0f, 0xf6, 0x02, 0x70, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0x0a, 0x00, 0x37, - 0x0f, 0xc4, 0x00, 0xfe, 0x0f, 0xc5, 0x01, 0x07, 0x04, 0xb7, 0x0f, 0xf9, - 0x0f, 0xeb, 0x00, 0x04, 0x00, 0x61, 0x0f, 0xe2, 0x07, 0xff, 0x0b, 0x19, - 0x09, 0xe4, 0x09, 0x88, 0x0c, 0x00, 0x0f, 0x38, 0x00, 0x09, 0x0f, 0x39, - 0x01, 0x84, 0x0f, 0xc0, 0x07, 0xf8, 0x0f, 0xc4, 0x01, 0x04, 0x02, 0x37, - 0x00, 0x21, 0x0f, 0xc4, 0x07, 0x26, 0x0b, 0x11, 0x01, 0x36, 0x09, 0xc8, - 0x0f, 0xc4, 0x01, 0x10, 0x00, 0x37, 0x00, 0x36, 0x00, 0x44, 0x0f, 0xc4, - 0x05, 0x48, 0x0b, 0x08, 0x01, 0xc4, 0x0c, 0x00, 0x01, 0x44, 0x0c, 0x00, - 0x00, 0x04, 0x00, 0x40, 0x0c, 0x21, 0x01, 0x19, 0x09, 0xe2, 0x0f, 0xe4, - 0x07, 0xff, 0x09, 0x88, 0x00, 0x09, 0x01, 0x04, 0x0f, 0xfa, 0x0f, 0xe1, - 0x0c, 0x00, 0x0f, 0xc4, 0x04, 0xc8, 0x00, 0x00, 0x0f, 0xc4, 0x04, 0xc9, - 0x00, 0x00, 0x0f, 0xc4, 0x04, 0xca, 0x00, 0x00, 0x0f, 0xfb, 0x10, 0x06, - 0x03, 0x37, 0x0f, 0xc4, 0x01, 0x10, 0x05, 0xb7, 0x0f, 0xfb, 0x00, 0x58, - 0x1f, 0xf9, 0x10, 0xe7, 0x00, 0x04, 0x0b, 0x21, 0x00, 0x14, 0x1f, 0xf9, - 0x10, 0xe3, 0x0f, 0xfb, 0x11, 0x1b, 0x01, 0xb6, 0x00, 0x44, 0x0f, 0xf9, - 0x10, 0xfc, 0x05, 0xb6, 0x00, 0x44, 0x0f, 0xfb, 0x00, 0x58, 0x1f, 0xf9, - 0x10, 0xf4, 0x00, 0x04, 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xfb, 0x11, 0x1b, - 0x0f, 0xf9, 0x10, 0xf9, 0x01, 0x04, 0x01, 0x45, 0x0b, 0x01, 0x00, 0x04, - 0x00, 0x40, 0x0f, 0xf6, 0x00, 0xaf, 0x00, 0x44, 0x01, 0x84, 0x0b, 0x21, - 0x00, 0x54, 0x1f, 0xf8, 0x11, 0x0d, 0x09, 0xc0, 0x01, 0x04, 0x00, 0x61, - 0x0f, 0xe2, 0x07, 0xff, 0x0b, 0x19, 0x09, 0xe4, 0x09, 0x88, 0x0c, 0x00, - 0x0f, 0xf8, 0x10, 0xd2, 0x00, 0x09, 0x0f, 0xfb, 0x0f, 0xeb, 0x0f, 0xc4, - 0x04, 0xca, 0x0b, 0x21, 0x0f, 0xd9, 0x04, 0xb8, 0x09, 0xc4, 0x0f, 0xc5, - 0x05, 0x49, 0x0f, 0xfa, 0x12, 0x20, 0x0b, 0x01, 0x0f, 0x39, 0x01, 0x44, - 0x01, 0xc5, 0x0b, 0x0f, 0x0d, 0xc1, 0x00, 0x04, 0x00, 0x00, 0x0f, 0xc4, - 0x04, 0xc8, 0x0b, 0x21, 0x04, 0x12, 0x1f, 0x39, 0x0f, 0xd9, 0x04, 0xb8, - 0x09, 0xc5, 0x0d, 0xc1, 0x00, 0x59, 0x09, 0xc0, 0x00, 0x14, 0x1f, 0x38, - 0x09, 0xce, 0x0f, 0xc4, 0x04, 0xb8, 0x0d, 0xe1, 0x0b, 0x13, 0x19, 0xe1, - 0x1f, 0xd9, 0x08, 0x00, 0x09, 0xe1, 0x0f, 0xd2, 0x02, 0x00, 0x1f, 0x39, - 0x0f, 0xc4, 0x04, 0xc9, 0x0d, 0x80, 0x0f, 0xc4, 0x04, 0xca, 0x0f, 0x38, - 0x0d, 0x80, 0x0f, 0xc4, 0x04, 0xc9, 0x0b, 0x08, 0x0c, 0x21, 0x0f, 0xc5, - 0x04, 0xca, 0x0b, 0x49, 0x0c, 0x54, 0x1f, 0x39, 0x0c, 0x40, 0x0c, 0x53, - 0x0f, 0xe1, 0x04, 0xb8, 0x0c, 0x19, 0x09, 0xc4, 0x0b, 0x0a, 0x0c, 0x59, - 0x09, 0xc4, 0x0b, 0x0b, 0x0c, 0xa1, 0x1c, 0xe1, 0x0c, 0xd1, 0x1c, 0x91, - 0x09, 0xcc, 0x09, 0xe1, 0x0f, 0xd2, 0x08, 0x00, 0x19, 0xcc, 0x00, 0x0d, - 0x0c, 0x21, 0x0c, 0x53, 0x10, 0x21, 0x1d, 0x11, 0x19, 0xcc, 0x1f, 0xcd, - 0x0f, 0xff, 0x0f, 0xc4, 0x04, 0xcb, 0x0d, 0x00, 0x0d, 0x08, 0x00, 0xb6, - 0x0d, 0x49, 0x07, 0x04, 0x0f, 0xc2, 0x0a, 0xab, 0x0f, 0xc2, 0x0a, 0xaa, - 0x0f, 0xc2, 0x07, 0xfe, 0x0f, 0xf7, 0x02, 0x70, 0x07, 0xb7, 0x11, 0x36, - 0x10, 0x88, 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x4e, 0x0c, 0x02, 0x0c, 0x42, - 0x0f, 0xc4, 0x06, 0x94, 0x0b, 0x8a, 0x0b, 0x8b, 0x0f, 0xc4, 0x01, 0x50, - 0x0c, 0x82, 0x0c, 0xc2, 0x0c, 0xa1, 0x0c, 0x20, 0x09, 0xc8, 0x0c, 0xe1, - 0x0c, 0x59, 0x0f, 0xf6, 0x09, 0x24, 0x09, 0xc9, 0x04, 0x04, 0x0f, 0xc0, - 0x00, 0x46, 0x0f, 0xc4, 0x06, 0x01, 0x0f, 0xf6, 0x05, 0xb9, 0x00, 0x40, - 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0x50, 0x0b, 0x88, 0x0f, 0xf6, 0x09, 0x24, - 0x0b, 0x89, 0x0f, 0xf6, 0x05, 0x5c, 0x00, 0x48, 0x09, 0x04, 0x00, 0xa1, - 0x0b, 0x16, 0x04, 0x04, 0x0f, 0xc0, 0x00, 0x32, 0x1f, 0xc0, 0x00, 0x47, - 0x1f, 0xfb, 0x16, 0x6c, 0x07, 0x37, 0x02, 0xc8, 0x12, 0xc8, 0x08, 0xc4, - 0x0c, 0x00, 0x0f, 0xc8, 0x00, 0x41, 0x0f, 0xf7, 0x05, 0x23, 0x0f, 0xfa, - 0x0e, 0x43, 0x00, 0x88, 0x0f, 0xc4, 0x06, 0x01, 0x0f, 0xf4, 0x05, 0xb9, - 0x04, 0x00, 0x00, 0x21, 0x0f, 0xc4, 0x06, 0xfe, 0x0b, 0x15, 0x1f, 0xf9, - 0x11, 0xdb, 0x0f, 0xc4, 0x01, 0xb7, 0x0b, 0x15, 0x1f, 0xf9, 0x11, 0xdb, - 0x0f, 0xc4, 0x01, 0xc5, 0x0f, 0xc0, 0x00, 0x47, 0x0f, 0xc4, 0x05, 0xe8, - 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xf9, 0x11, 0xe2, 0x0f, 0xc4, 0x01, 0xc6, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x0f, 0xd2, 0x00, 0x46, 0x11, 0x38, - 0x11, 0x88, 0x0f, 0xc8, 0x00, 0x47, 0x08, 0xbb, 0x0f, 0x39, 0x04, 0x04, - 0x0f, 0xc0, 0x00, 0x32, 0x0f, 0xc8, 0x00, 0x3c, 0x08, 0xbb, 0x0f, 0x39, - 0x04, 0x3b, 0x0f, 0xf7, 0x0c, 0x13, 0x0f, 0xf7, 0x0c, 0x97, 0x0f, 0xc4, - 0x01, 0x70, 0x0b, 0x21, 0x0f, 0xc4, 0x06, 0xff, 0x0b, 0x12, 0x1f, 0xf9, - 0x12, 0x07, 0x0f, 0xc4, 0x01, 0xc5, 0x0b, 0x21, 0x0f, 0xd5, 0x00, 0x47, - 0x1f, 0xf9, 0x12, 0x07, 0x0f, 0xfb, 0x16, 0x8b, 0x09, 0x3b, 0x0f, 0xfb, - 0x0c, 0xf8, 0x0f, 0xfb, 0x13, 0xae, 0x04, 0x04, 0x0f, 0xc5, 0x01, 0xc5, - 0x0b, 0x01, 0x0f, 0xc0, 0x00, 0x47, 0x0f, 0xf7, 0x05, 0x9d, 0x0f, 0xf9, - 0x11, 0xe2, 0x07, 0xb7, 0x1f, 0xf6, 0x05, 0x5c, 0x10, 0x48, 0x09, 0x3b, - 0x0f, 0xe1, 0x00, 0x47, 0x04, 0x04, 0x0b, 0x15, 0x19, 0x45, 0x1f, 0xc4, - 0x00, 0x80, 0x1f, 0xf7, 0x00, 0xa4, 0x0f, 0xc4, 0x01, 0xc5, 0x0b, 0x15, - 0x19, 0x45, 0x1f, 0xc4, 0x00, 0x80, 0x1f, 0xf7, 0x00, 0xa4, 0x0f, 0xfb, - 0x12, 0x46, 0x0f, 0x39, 0x09, 0x04, 0x00, 0x00, 0x0f, 0xc4, 0x04, 0xc9, - 0x0f, 0xc5, 0x04, 0xce, 0x0b, 0x01, 0x0f, 0xc5, 0x04, 0xcf, 0x0b, 0x01, - 0x0f, 0xc5, 0x04, 0xcc, 0x00, 0x01, 0x0f, 0xc5, 0x04, 0xcd, 0x00, 0x01, - 0x0f, 0xc5, 0x04, 0xd0, 0x00, 0x41, 0x0f, 0xc5, 0x04, 0xd5, 0x00, 0x01, - 0x0f, 0xc5, 0x04, 0xd1, 0x0b, 0x01, 0x02, 0x36, 0x09, 0x44, 0x0f, 0xc8, - 0x1f, 0xfc, 0x01, 0x37, 0x0f, 0xc4, 0x04, 0xd2, 0x00, 0x37, 0x0f, 0xfb, - 0x16, 0x2f, 0x0f, 0xfb, 0x16, 0x6c, 0x0f, 0x39, 0x0f, 0xc4, 0x00, 0x80, - 0x02, 0x37, 0x0f, 0xc4, 0x04, 0xc9, 0x0b, 0x21, 0x0b, 0x27, 0x09, 0x99, - 0x09, 0xe1, 0x0f, 0xd9, 0x04, 0xd6, 0x00, 0x36, 0x09, 0xc4, 0x0f, 0xc4, - 0x04, 0xd2, 0x0f, 0xf7, 0x00, 0xbd, 0x0f, 0xc4, 0x00, 0x80, 0x05, 0xb7, - 0x0f, 0xfb, 0x00, 0x58, 0x0f, 0xc4, 0x04, 0xd0, 0x0b, 0x0c, 0x1f, 0xf8, - 0x12, 0x65, 0x00, 0x21, 0x0f, 0xf8, 0x12, 0x75, 0x0d, 0x15, 0x0f, 0xc4, - 0x04, 0xd2, 0x0f, 0xf7, 0x00, 0xb2, 0x0f, 0xc4, 0x04, 0xd1, 0x0f, 0xc5, - 0x04, 0xc9, 0x0b, 0x40, 0x0f, 0xc4, 0x04, 0xd5, 0x0b, 0x14, 0x1f, 0xf8, - 0x12, 0x7a, 0x10, 0x40, 0x0d, 0x14, 0x0f, 0xc5, 0x04, 0xcc, 0x1f, 0xc5, - 0x04, 0xcd, 0x00, 0x41, 0x0f, 0xc4, 0x04, 0xcc, 0x0b, 0x88, 0x0b, 0x89, - 0x0b, 0x8a, 0x0b, 0x8b, 0x00, 0x21, 0x0c, 0x15, 0x1f, 0xf9, 0x12, 0x8c, - 0x0c, 0xa1, 0x00, 0x51, 0x09, 0xca, 0x09, 0xe1, 0x0f, 0xd7, 0x1f, 0xff, - 0x10, 0x0a, 0x10, 0x48, 0x00, 0x21, 0x0c, 0x55, 0x1f, 0xf9, 0x12, 0x9a, - 0x0c, 0xe1, 0x00, 0x59, 0x09, 0xe1, 0x0f, 0xc4, 0x04, 0xc8, 0x0b, 0x12, - 0x00, 0x11, 0x10, 0x51, 0x09, 0xcb, 0x10, 0x49, 0x00, 0x21, 0x0d, 0x15, - 0x1f, 0xf9, 0x12, 0xa4, 0x0c, 0x0d, 0x0d, 0x55, 0x10, 0x4c, 0x0f, 0xf8, - 0x12, 0xa8, 0x1c, 0x4d, 0x0c, 0x4d, 0x0d, 0x55, 0x10, 0x0c, 0x1c, 0x0d, - 0x0f, 0xc4, 0x04, 0xcc, 0x0c, 0x02, 0x0c, 0x42, 0x0f, 0xc4, 0x04, 0xd0, - 0x0d, 0x00, 0x0d, 0x54, 0x1f, 0xf9, 0x12, 0xbf, 0x0f, 0xc4, 0x04, 0xca, - 0x0f, 0xc5, 0x04, 0xd1, 0x0b, 0x40, 0x04, 0x04, 0x0f, 0xc0, 0x00, 0x32, - 0x09, 0x04, 0x0f, 0xfa, 0x11, 0x41, 0x00, 0x80, 0x0f, 0x39, 0x0d, 0x15, - 0x0c, 0x8d, 0x0f, 0xc4, 0x04, 0xce, 0x1c, 0xcd, 0x1f, 0xc4, 0x04, 0xcf, - 0x0d, 0x40, 0x0f, 0xc4, 0x04, 0xca, 0x0f, 0xfa, 0x11, 0x41, 0x0d, 0x40, - 0x09, 0x04, 0x0f, 0x38, 0x00, 0x00, 0x07, 0x37, 0x0f, 0xf0, 0x0b, 0x08, - 0x0f, 0xf2, 0x00, 0x3f, 0x0f, 0xf0, 0x0b, 0x09, 0x0f, 0xf2, 0x0d, 0x59, - 0x1f, 0xf2, 0x07, 0x4d, 0x0f, 0xf0, 0x0b, 0x10, 0x0f, 0xf2, 0x00, 0x87, - 0x1f, 0xf2, 0x01, 0x47, 0x0f, 0xf0, 0x0b, 0x11, 0x0f, 0xf2, 0x03, 0x12, - 0x0f, 0xf0, 0x0b, 0x12, 0x0f, 0xf2, 0x06, 0x61, 0x0f, 0xf0, 0x0b, 0x13, - 0x0f, 0xf2, 0x0b, 0xbf, 0x0f, 0xc4, 0x06, 0xc9, 0x0f, 0xc2, 0x0c, 0xcd, - 0x0f, 0xc2, 0x07, 0xfe, 0x1f, 0xc4, 0x06, 0xc9, 0x1f, 0xc2, 0x0a, 0xe1, - 0x1f, 0xc2, 0x07, 0xfd, 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0xd9, 0x08, 0x02, - 0x00, 0x02, 0x0f, 0xc2, 0x0b, 0xb5, 0x0f, 0xc2, 0x07, 0xf0, 0x00, 0x02, - 0x0f, 0xc2, 0x0b, 0xb4, 0x0f, 0xc2, 0x07, 0xf0, 0x00, 0x02, 0x0f, 0xc2, - 0x0a, 0x5b, 0x0f, 0xc2, 0x07, 0xff, 0x00, 0x02, 0x0f, 0xc2, 0x0d, 0xdf, - 0x0f, 0xc2, 0x07, 0xfa, 0x00, 0x02, 0x0f, 0xc2, 0x0a, 0x22, 0x00, 0x02, - 0x00, 0x02, 0x0f, 0xc2, 0x0a, 0x2f, 0x0f, 0xc2, 0x07, 0xf2, 0x00, 0x02, - 0x0f, 0xc2, 0x0b, 0x40, 0x0f, 0xc2, 0x07, 0xfa, 0x00, 0x02, 0x0f, 0xc2, - 0x0c, 0x52, 0x00, 0x02, 0x00, 0x02, 0x0f, 0xc2, 0x08, 0x00, 0x00, 0x42, - 0x00, 0x02, 0x0f, 0xc2, 0x0c, 0xb5, 0x0f, 0xc2, 0x07, 0xfb, 0x00, 0x02, - 0x0f, 0xc2, 0x0f, 0x98, 0x0f, 0xc2, 0x07, 0xfa, 0x00, 0x02, 0x0f, 0xc2, - 0x0b, 0x23, 0x0f, 0xc2, 0x07, 0xfa, 0x00, 0x02, 0x0f, 0xc2, 0x08, 0x40, - 0x0f, 0xc2, 0x07, 0xfa, 0x00, 0x02, 0x0f, 0xc2, 0x0d, 0x4e, 0x0f, 0xc2, - 0x07, 0xf9, 0x00, 0x02, 0x0f, 0xc2, 0x0d, 0xc5, 0x00, 0x02, 0x00, 0x02, - 0x0f, 0xc2, 0x09, 0x06, 0x00, 0x42, 0x07, 0xc4, 0x0a, 0x8f, 0x0f, 0xc2, - 0x04, 0xf3, 0x0f, 0xc2, 0x0b, 0x50, 0x00, 0x02, 0x0d, 0xc4, 0x08, 0x3a, - 0x0d, 0xc5, 0x0f, 0xc4, 0x06, 0xcb, 0x00, 0x37, 0x06, 0xb6, 0x0d, 0xc4, - 0x0f, 0xc4, 0x06, 0xce, 0x00, 0x37, 0x06, 0xb6, 0x0d, 0xc4, 0x0f, 0xc4, - 0x06, 0xd1, 0x00, 0x37, 0x06, 0xb6, 0x0d, 0xc4, 0x0f, 0xc4, 0x06, 0xd4, - 0x00, 0x37, 0x06, 0xb6, 0x0d, 0xc4, 0x0f, 0xc4, 0x06, 0xd7, 0x00, 0x37, - 0x0f, 0xc4, 0x06, 0xda, 0x00, 0x02, 0x0f, 0xc2, 0x0c, 0x00, 0x0f, 0x38, - 0x00, 0xc2, 0x0f, 0xf1, 0x0a, 0x36, 0x0f, 0xc5, 0x01, 0x52, 0x0f, 0xc4, - 0x01, 0xd9, 0x07, 0x36, 0x0b, 0x0a, 0x0a, 0x43, 0x0f, 0xf1, 0x0a, 0x37, - 0x1c, 0xa7, 0x19, 0xa7, 0x19, 0x8a, 0x0f, 0xc4, 0x01, 0xc9, 0x0c, 0x80, - 0x0f, 0xc4, 0x01, 0xc8, 0x0b, 0x0c, 0x0d, 0x21, 0x00, 0x59, 0x09, 0xcb, - 0x0a, 0x43, 0x0c, 0x97, 0x1c, 0xcc, 0x0f, 0xc5, 0x01, 0xc7, 0x00, 0x21, - 0x0b, 0x55, 0x00, 0x01, 0x10, 0x0c, 0x0f, 0xc5, 0x01, 0x6e, 0x0b, 0x55, - 0x00, 0x01, 0x1f, 0xc5, 0x01, 0xcb, 0x10, 0x41, 0x1f, 0xc5, 0x01, 0xcc, - 0x10, 0x01, 0x1f, 0xc5, 0x01, 0xc7, 0x10, 0x41, 0x1c, 0xa1, 0x10, 0x59, - 0x19, 0xcc, 0x0d, 0x00, 0x0d, 0x21, 0x0c, 0x92, 0x0f, 0xc4, 0x01, 0xca, - 0x0b, 0x22, 0x00, 0xe4, 0x09, 0xa2, 0x03, 0x25, 0x10, 0x25, 0x0f, 0xf0, - 0x0a, 0x2f, 0x09, 0xb2, 0x0f, 0x38, 0x09, 0x80, 0x0f, 0xc4, 0x01, 0xc8, - 0x0b, 0x21, 0x0f, 0xc4, 0x01, 0xc9, 0x0b, 0x16, 0x1f, 0xf9, 0x14, 0x13, - 0x0f, 0xc4, 0x01, 0xcb, 0x00, 0x00, 0x00, 0x15, 0x0f, 0xc5, 0x01, 0x52, - 0x1f, 0xf9, 0x13, 0xc2, 0x0f, 0xc4, 0x05, 0x0b, 0x0f, 0x38, 0x0b, 0xc0, - 0x0b, 0xc8, 0x0c, 0x2a, 0x00, 0x2b, 0x0c, 0x2c, 0x00, 0x2d, 0x0f, 0xee, - 0x0c, 0x80, 0x0f, 0xe1, 0x0a, 0x00, 0x0b, 0xc9, 0x0c, 0x59, 0x09, 0xe1, - 0x0c, 0x59, 0x09, 0xee, 0x0f, 0xce, 0x05, 0x06, 0x0d, 0x84, 0x0b, 0xaa, - 0x0b, 0xab, 0x0b, 0xac, 0x0b, 0xad, 0x0f, 0xef, 0x08, 0x40, 0x1f, 0xf9, - 0x13, 0xe9, 0x0d, 0x84, 0x0f, 0xee, 0x05, 0x00, 0x0e, 0x02, 0x0e, 0x42, - 0x0f, 0xee, 0x05, 0x40, 0x0e, 0x02, 0x0e, 0x42, 0x0f, 0xc4, 0x05, 0x0a, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x0f, 0xf1, 0x05, 0x0f, 0x0f, 0xee, - 0x03, 0x80, 0x0f, 0xe1, 0x09, 0xf8, 0x00, 0x2d, 0x0f, 0xcf, 0x05, 0x10, - 0x0a, 0x6c, 0x0d, 0xf1, 0x0f, 0xee, 0x02, 0x80, 0x0f, 0xce, 0x05, 0x0c, - 0x0d, 0x84, 0x0b, 0xaa, 0x0b, 0xab, 0x0b, 0xac, 0x0b, 0xad, 0x0a, 0x59, - 0x09, 0xee, 0x0f, 0xcd, 0x05, 0x00, 0x0f, 0xcc, 0x05, 0x40, 0x0f, 0xef, - 0x08, 0x40, 0x1f, 0x39, 0x0d, 0x84, 0x0d, 0x6e, 0x0e, 0x02, 0x0e, 0x42, - 0x0d, 0x2e, 0x0e, 0x02, 0x0e, 0x42, 0x0f, 0xc4, 0x05, 0x10, 0x0b, 0x21, - 0x00, 0x59, 0x0f, 0x38, 0x09, 0xc0, 0x0f, 0xc4, 0x01, 0xcb, 0x0b, 0x21, - 0x00, 0x15, 0x1f, 0x39, 0x00, 0x40, 0x0f, 0xc4, 0x01, 0xcc, 0x0b, 0x21, - 0x00, 0x59, 0x09, 0xc8, 0x09, 0xe1, 0x01, 0x17, 0x1c, 0x00, 0x00, 0x54, - 0x1f, 0xc8, 0x14, 0x26, 0x15, 0x3b, 0x0f, 0x39, 0x0f, 0xc4, 0x01, 0xcc, - 0x0b, 0x21, 0x00, 0x14, 0x1f, 0x39, 0x0f, 0xc4, 0x05, 0x06, 0x0f, 0xc5, - 0x05, 0x11, 0x0b, 0x03, 0x00, 0x02, 0x0b, 0x03, 0x00, 0x02, 0x0b, 0x03, - 0x00, 0x02, 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xc4, 0x05, 0x0a, 0x0f, 0xc5, - 0x05, 0x15, 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xc4, 0x05, 0x0b, 0x0f, 0xc5, - 0x05, 0x16, 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xc4, 0x05, 0x0c, 0x0f, 0xc5, - 0x05, 0x17, 0x0b, 0x03, 0x00, 0x02, 0x0b, 0x03, 0x00, 0x02, 0x0b, 0x03, - 0x00, 0x02, 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xc4, 0x05, 0x10, 0x0f, 0xc5, - 0x05, 0x1b, 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0xc7, 0x00, 0x40, - 0x0f, 0xc4, 0x05, 0x11, 0x0b, 0xaa, 0x0b, 0xab, 0x0b, 0xac, 0x0b, 0xad, - 0x0f, 0xee, 0x03, 0x00, 0x0f, 0xee, 0x0a, 0xef, 0x0f, 0xee, 0x09, 0x00, - 0x07, 0xc4, 0x0f, 0xe2, 0x00, 0x3f, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0xa4, - 0x09, 0x82, 0x0f, 0xf6, 0x00, 0xbd, 0x07, 0xc4, 0x0f, 0xc4, 0x05, 0x16, - 0x0b, 0x2a, 0x0b, 0x2c, 0x0f, 0xee, 0x0d, 0x40, 0x0e, 0x2a, 0x0e, 0x6b, - 0x0f, 0xc4, 0x05, 0x15, 0x0b, 0x2c, 0x00, 0x2d, 0x0f, 0xee, 0x0c, 0x80, - 0x0f, 0xee, 0x0a, 0xef, 0x0f, 0xee, 0x09, 0x00, 0x07, 0x04, 0x0f, 0xe2, - 0x00, 0x3f, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0xa4, 0x0f, 0xf6, 0x02, 0x09, - 0x09, 0x82, 0x0f, 0xc4, 0x00, 0x7d, 0x00, 0x37, 0x0f, 0xc4, 0x01, 0xcc, - 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xc4, 0x01, 0x6f, 0x01, 0xba, 0x10, 0x40, - 0x0f, 0xc4, 0x00, 0x7d, 0x02, 0x37, 0x07, 0x37, 0x0f, 0xc4, 0x01, 0xda, - 0x1f, 0xc4, 0x01, 0xdd, 0x06, 0xb7, 0x0f, 0xc4, 0x01, 0x1c, 0x00, 0x37, - 0x0f, 0xf1, 0x0a, 0x07, 0x0f, 0xc4, 0x01, 0xe0, 0x06, 0xb7, 0x04, 0x21, - 0x0a, 0x54, 0x0f, 0xf1, 0x0a, 0x10, 0x1f, 0xf9, 0x14, 0xc6, 0x00, 0x2a, - 0x09, 0xeb, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0xcb, 0x0f, 0xf7, - 0x04, 0x66, 0x05, 0x84, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0x82, 0x0f, 0xc4, - 0x01, 0xe3, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0x9e, 0x0f, 0xf7, 0x02, 0x81, - 0x0f, 0xfb, 0x01, 0x1c, 0x0f, 0xf7, 0x04, 0x72, 0x0f, 0xf7, 0x02, 0x70, - 0x0f, 0xf9, 0x14, 0xd0, 0x07, 0xb7, 0x1f, 0xf9, 0x14, 0xd0, 0x0a, 0x61, - 0x00, 0x14, 0x1f, 0xf9, 0x14, 0xd0, 0x0f, 0xc4, 0x01, 0xe6, 0x06, 0xb7, - 0x0f, 0xc4, 0x01, 0x13, 0x00, 0x37, 0x0f, 0xc4, 0x01, 0x1c, 0x02, 0x37, - 0x0f, 0xfb, 0x01, 0x0c, 0x0f, 0xf7, 0x00, 0x8f, 0x0f, 0xc4, 0x01, 0x22, - 0x00, 0x37, 0x0f, 0xc4, 0x01, 0x22, 0x0f, 0xc5, 0x00, 0xa7, 0x08, 0x3b, - 0x03, 0xb7, 0x00, 0x21, 0x0c, 0x55, 0x1f, 0xc8, 0x0f, 0xff, 0x0f, 0xc4, - 0x01, 0x79, 0x01, 0xba, 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0x13, 0x02, 0x37, - 0x0f, 0xfb, 0x01, 0x0c, 0x0f, 0xf7, 0x00, 0x8f, 0x0f, 0xc4, 0x06, 0xdd, - 0x00, 0x37, 0x0f, 0xc4, 0x00, 0xa7, 0x06, 0xb7, 0x03, 0xb7, 0x00, 0x21, - 0x0c, 0x55, 0x1f, 0xc8, 0x0f, 0xff, 0x0f, 0xf0, 0x0f, 0x16, 0x0c, 0x32, - 0x0f, 0xc4, 0x01, 0x78, 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0x7f, 0x0c, 0x00, - 0x0f, 0xfa, 0x03, 0x00, 0x08, 0x08, 0x0f, 0xc4, 0x05, 0x17, 0x0b, 0xaa, - 0x0b, 0xab, 0x0b, 0xac, 0x0b, 0xad, 0x0f, 0xee, 0x03, 0x00, 0x0f, 0xee, - 0x0a, 0xef, 0x0f, 0xf1, 0x05, 0x04, 0x0a, 0x4f, 0x0f, 0xf1, 0x05, 0x05, - 0x0f, 0xee, 0x09, 0x00, 0x0f, 0xe2, 0x00, 0x3f, 0x02, 0x21, 0x07, 0xc4, - 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0xa4, 0x09, 0x99, 0x09, 0xc2, 0x0f, 0xc4, - 0x01, 0x54, 0x0d, 0xc2, 0x01, 0xba, 0x0a, 0x42, 0x0f, 0xf6, 0x00, 0xbd, - 0x07, 0xc4, 0x0f, 0xc4, 0x05, 0x1b, 0x00, 0x2a, 0x0b, 0x2b, 0x0f, 0xee, - 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0xcb, 0x07, 0x04, 0x00, 0x02, 0x0e, 0x42, - 0x0f, 0xf6, 0x02, 0x09, 0x0e, 0x82, 0x0f, 0xc4, 0x01, 0x54, 0x0b, 0x8f, - 0x00, 0x21, 0x0d, 0xd5, 0x10, 0x2a, 0x1d, 0xeb, 0x1f, 0xee, 0x01, 0xc0, - 0x1f, 0xe1, 0x0b, 0x02, 0x1b, 0x99, 0x19, 0xee, 0x17, 0x04, 0x1e, 0x02, - 0x1e, 0x42, 0x1e, 0xa2, 0x1f, 0xe4, 0x07, 0xff, 0x1f, 0xf6, 0x02, 0x70, - 0x19, 0x82, 0x0f, 0xc4, 0x01, 0xe9, 0x06, 0xb7, 0x07, 0x37, 0x11, 0x36, - 0x10, 0x88, 0x0f, 0xc4, 0x01, 0x1f, 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, - 0x06, 0xc7, 0x0b, 0x21, 0x00, 0x15, 0x1f, 0xf9, 0x15, 0x68, 0x0f, 0xc4, - 0x01, 0x1f, 0x02, 0x37, 0x0f, 0xfb, 0x01, 0x14, 0x0f, 0xc4, 0x06, 0xe0, - 0x0f, 0xf9, 0x15, 0x72, 0x0f, 0xc4, 0x06, 0xe0, 0x0f, 0xc5, 0x00, 0xa1, - 0x0f, 0xf7, 0x02, 0x7d, 0x0f, 0xfb, 0x01, 0x1c, 0x0f, 0xc4, 0x01, 0x1f, - 0x00, 0x37, 0x0f, 0xc4, 0x01, 0x1f, 0x02, 0x37, 0x0f, 0xc4, 0x01, 0xec, - 0x06, 0xb7, 0x0f, 0xc4, 0x01, 0x16, 0x00, 0x37, 0x0f, 0xc4, 0x01, 0x6c, - 0x0b, 0x22, 0x0f, 0xe4, 0x00, 0x38, 0x09, 0xa1, 0x0f, 0xc4, 0x01, 0xf5, - 0x02, 0x14, 0x1f, 0xc4, 0x01, 0xf8, 0x04, 0x14, 0x1f, 0xc4, 0x01, 0xfb, - 0x06, 0x14, 0x1f, 0xc4, 0x01, 0xfe, 0x08, 0x14, 0x1f, 0xc4, 0x02, 0x01, - 0x0f, 0xe4, 0x0c, 0x00, 0x09, 0xa1, 0x0f, 0xc5, 0x06, 0x2b, 0x0f, 0xd4, - 0x04, 0x00, 0x1f, 0xc5, 0x02, 0x04, 0x00, 0x14, 0x1f, 0xc5, 0x02, 0x07, - 0x08, 0x3b, 0x0f, 0xc4, 0x01, 0x7e, 0x0b, 0x21, 0x00, 0x15, 0x11, 0x36, - 0x10, 0x88, 0x0f, 0xc4, 0x01, 0x25, 0x00, 0x37, 0x0f, 0xc4, 0x01, 0x16, - 0x06, 0xb7, 0x0f, 0xc4, 0x01, 0x16, 0x06, 0xb7, 0x07, 0x37, 0x0f, 0xc4, - 0x01, 0xef, 0x1f, 0xc4, 0x01, 0xf2, 0x06, 0xb7, 0x0f, 0xc4, 0x01, 0x19, - 0x00, 0x37, 0x01, 0xbb, 0x0f, 0xc4, 0x01, 0xcd, 0x0f, 0xc0, 0x06, 0xc8, - 0x0f, 0xc4, 0x01, 0xce, 0x0f, 0xc0, 0x05, 0x1c, 0x04, 0xbb, 0x04, 0xbb, - 0x04, 0xbb, 0x04, 0xbb, 0x04, 0xbb, 0x04, 0xbb, 0x01, 0xbb, 0x04, 0xbb, - 0x0f, 0xc4, 0x05, 0x1c, 0x0f, 0xc8, 0x0a, 0x24, 0x00, 0x61, 0x01, 0xfe, - 0x0f, 0xfd, 0x15, 0xd2, 0x00, 0x3f, 0x0c, 0x30, 0x0b, 0xb2, 0x0c, 0x19, - 0x09, 0xc8, 0x0f, 0xc4, 0x01, 0xcc, 0x0b, 0x21, 0x00, 0x14, 0x1f, 0x39, - 0x00, 0x54, 0x1f, 0x38, 0x09, 0xc0, 0x01, 0xbb, 0x0f, 0xf8, 0x14, 0x26, - 0x0f, 0xc4, 0x01, 0xcd, 0x0b, 0x05, 0x0b, 0xc8, 0x0b, 0xc9, 0x0b, 0xca, - 0x0a, 0xc0, 0x05, 0x84, 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0x82, 0x0f, 0xc4, - 0x01, 0x19, 0x06, 0xb7, 0x0f, 0xc4, 0x01, 0x80, 0x0b, 0x21, 0x00, 0x15, - 0x1f, 0xf9, 0x16, 0x0c, 0x05, 0x84, 0x0b, 0xaa, 0x0b, 0xab, 0x0f, 0xee, - 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0x7a, 0x0b, 0xa7, 0x09, 0xa6, 0x09, 0xa1, - 0x01, 0x51, 0x09, 0xc8, 0x01, 0x16, 0x1f, 0xf9, 0x16, 0x24, 0x0c, 0x21, - 0x0f, 0xd7, 0x1f, 0xfa, 0x1f, 0xe1, 0x1f, 0xfa, 0x0f, 0xd9, 0x0a, 0x80, - 0x09, 0xee, 0x0f, 0xf8, 0x16, 0x24, 0x00, 0x08, 0x05, 0x84, 0x0b, 0xaa, - 0x0b, 0xab, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0x7b, 0x0b, 0xa7, - 0x09, 0xa6, 0x09, 0xa1, 0x01, 0x91, 0x09, 0xc8, 0x01, 0x56, 0x1f, 0xf9, - 0x16, 0x24, 0x0c, 0x21, 0x0f, 0xd7, 0x1f, 0xf9, 0x1f, 0xe1, 0x1f, 0xf9, - 0x0f, 0xd9, 0x0a, 0x80, 0x09, 0xee, 0x00, 0x08, 0x0f, 0xf6, 0x00, 0x26, - 0x0c, 0x27, 0x09, 0xa2, 0x0e, 0x65, 0x0f, 0xc4, 0x01, 0xce, 0x0b, 0x05, - 0x09, 0x83, 0x0f, 0x38, 0x0a, 0xc0, 0x0f, 0xc4, 0x01, 0xcf, 0x00, 0x00, - 0x02, 0x04, 0x0f, 0xc2, 0x00, 0x28, 0x0f, 0xc2, 0x00, 0x29, 0x0f, 0xc2, - 0x00, 0x2b, 0x0f, 0xc2, 0x00, 0x2d, 0x0f, 0xc2, 0x00, 0x2f, 0x0f, 0xc2, - 0x00, 0x33, 0x0f, 0xc2, 0x00, 0x37, 0x0f, 0xc2, 0x00, 0x3b, 0x0f, 0xc4, - 0x00, 0x28, 0x0f, 0xc2, 0x00, 0x88, 0x0f, 0xc2, 0x00, 0x3d, 0x0f, 0xc2, - 0x00, 0xb6, 0x0f, 0xc2, 0x00, 0x56, 0x0f, 0xc2, 0x00, 0xac, 0x0f, 0xc2, - 0x00, 0x6b, 0x0f, 0xc2, 0x00, 0xa0, 0x07, 0x82, 0x0f, 0xc2, 0x00, 0x59, - 0x0f, 0xc2, 0x00, 0x94, 0x0f, 0xc2, 0x00, 0xcf, 0x0f, 0xc2, 0x00, 0x32, - 0x0f, 0xc2, 0x00, 0x63, 0x0f, 0xc2, 0x00, 0x95, 0x0f, 0xc2, 0x00, 0xc6, - 0x0f, 0xc2, 0x00, 0x4a, 0x0f, 0xc2, 0x00, 0x6f, 0x0f, 0xc2, 0x00, 0x94, - 0x0f, 0xc2, 0x00, 0xb9, 0x0f, 0x38, 0x00, 0x02, 0x0f, 0xc4, 0x01, 0x70, - 0x00, 0x00, 0x0f, 0xc4, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, - 0x0f, 0x38, 0x00, 0x02, 0x00, 0x21, 0x0c, 0x16, 0x19, 0xc8, 0x0d, 0x09, - 0x0c, 0x84, 0x0c, 0xfe, 0x0f, 0xfd, 0x16, 0x86, 0x0b, 0xa1, 0x0c, 0x17, - 0x09, 0xce, 0x10, 0x21, 0x1d, 0x91, 0x19, 0xce, 0x0c, 0x61, 0x0d, 0x96, - 0x1d, 0x89, 0x0c, 0x6a, 0x0c, 0x6c, 0x0f, 0x38, 0x0d, 0x6e, 0x0f, 0xc4, - 0x05, 0xeb, 0x0b, 0x08, 0x0f, 0xc4, 0x05, 0xea, 0x0b, 0x09, 0x00, 0x0a, - 0x0c, 0x61, 0x00, 0x14, 0x1f, 0xf9, 0x16, 0xaa, 0x00, 0x55, 0x1f, 0xf9, - 0x16, 0xa1, 0x00, 0x4a, 0x0c, 0x21, 0x00, 0x94, 0x10, 0x8a, 0x00, 0xd4, - 0x0f, 0xf8, 0x16, 0xaa, 0x10, 0xca, 0x00, 0x55, 0x1f, 0xf9, 0x16, 0xaa, - 0x01, 0x0a, 0x0c, 0x21, 0x00, 0x94, 0x11, 0x4a, 0x00, 0xd4, 0x11, 0x8a, - 0x0f, 0xc4, 0x01, 0xcf, 0x0c, 0x80, 0x0f, 0xf0, 0x0a, 0x0d, 0x00, 0x32, - 0x0f, 0xf6, 0x05, 0x5c, 0x01, 0x08, 0x0f, 0xc4, 0x00, 0x6c, 0x0b, 0xaa, - 0x0b, 0xab, 0x0b, 0xac, 0x0b, 0xad, 0x0f, 0xee, 0x03, 0x00, 0x0f, 0xc5, - 0x01, 0xcf, 0x0b, 0x61, 0x02, 0x19, 0x09, 0xc5, 0x0b, 0xca, 0x0b, 0xe1, - 0x0c, 0x91, 0x09, 0xcb, 0x0f, 0xcc, 0x07, 0xff, 0x0f, 0xcd, 0x0c, 0xc0, - 0x00, 0x2b, 0x00, 0x2d, 0x0f, 0xf1, 0x03, 0x39, 0x0f, 0xce, 0x00, 0xc2, - 0x0f, 0xcf, 0x07, 0x68, 0x07, 0x37, 0x1f, 0xce, 0x00, 0x41, 0x1f, 0xcf, - 0x01, 0xe9, 0x0a, 0x61, 0x0f, 0xf1, 0x03, 0x39, 0x0d, 0x93, 0x1f, 0xf9, - 0x16, 0xd5, 0x0f, 0xf1, 0x0a, 0x22, 0x0a, 0x48, 0x0f, 0xf1, 0x0a, 0x23, - 0x0f, 0xfb, 0x16, 0x76, 0x0a, 0x48, 0x0f, 0xf1, 0x0a, 0x22, 0x0f, 0xfb, - 0x16, 0x76, 0x0a, 0x48, 0x0f, 0xf1, 0x03, 0x39, 0x0f, 0xc4, 0x01, 0x70, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x0f, 0xc4, 0x06, 0xff, - 0x0b, 0x12, 0x1f, 0xf9, 0x17, 0x04, 0x0a, 0x61, 0x0d, 0xd3, 0x1f, 0xf9, - 0x16, 0xde, 0x0f, 0xc4, 0x00, 0x6c, 0x0f, 0xee, 0x05, 0x00, 0x0e, 0x02, - 0x0e, 0x42, 0x0f, 0xee, 0x05, 0x40, 0x0e, 0x02, 0x0f, 0x38, 0x0e, 0x42, - 0x0f, 0xee, 0x0a, 0xef, 0x0f, 0xe2, 0x00, 0x3f, 0x0f, 0xe1, 0x1f, 0xf6, - 0x0f, 0xc4, 0x00, 0x80, 0x0f, 0xee, 0x09, 0x00, 0x0e, 0x02, 0x0e, 0x42, - 0x0e, 0xa4, 0x09, 0x99, 0x09, 0xe2, 0x0f, 0xe4, 0x07, 0xff, 0x0f, 0x38, - 0x09, 0x82, 0x0f, 0xfb, 0x17, 0x68, 0x0f, 0xfb, 0x0c, 0x26, 0x0f, 0xfb, - 0x0c, 0xf8, 0x0f, 0xf7, 0x0d, 0xa3, 0x0f, 0xfb, 0x18, 0x62, 0x0f, 0xfb, - 0x1f, 0x0b, 0x0f, 0xfb, 0x13, 0xae, 0x0f, 0xf7, 0x05, 0x92, 0x0f, 0xf6, - 0x05, 0x8d, 0x02, 0x48, 0x1f, 0xf9, 0x17, 0x33, 0x0f, 0xc4, 0x01, 0x69, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x0f, 0xfb, 0x1a, 0xa8, 0x0f, 0xf6, - 0x05, 0x8d, 0x02, 0x88, 0x1f, 0xf9, 0x17, 0x45, 0x00, 0x61, 0x0f, 0xc4, - 0x01, 0x67, 0x0b, 0x19, 0x09, 0xc0, 0x0f, 0xc4, 0x01, 0x66, 0x0b, 0x19, - 0x09, 0xc0, 0x02, 0xba, 0x00, 0x48, 0x0f, 0xf9, 0x17, 0x47, 0x02, 0xba, - 0x00, 0x08, 0x0f, 0xf6, 0x05, 0x8d, 0x02, 0xc8, 0x1f, 0xf9, 0x17, 0x54, - 0x0f, 0xc4, 0x01, 0x68, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x0f, 0xc4, - 0x07, 0xbf, 0x00, 0x40, 0x0f, 0xc8, 0x0e, 0x00, 0x0f, 0xf7, 0x05, 0x94, - 0x0f, 0xfb, 0x03, 0x96, 0x0f, 0xc4, 0x01, 0x6c, 0x0f, 0xf1, 0x0c, 0x06, - 0x0a, 0x40, 0x0f, 0xfb, 0x1a, 0xcf, 0x0f, 0xfb, 0x03, 0xdd, 0x0f, 0xfb, - 0x18, 0xd3, 0x0f, 0xfb, 0x19, 0x16, 0x0f, 0x39, 0x0f, 0xc4, 0x06, 0x92, - 0x0b, 0x22, 0x0f, 0xe4, 0x0d, 0xff, 0x09, 0x80, 0x07, 0x64, 0x09, 0xa2, - 0x0f, 0xe5, 0x1f, 0xe2, 0x09, 0xa2, 0x04, 0x84, 0x0b, 0x24, 0x09, 0x80, - 0x04, 0x84, 0x0b, 0x21, 0x01, 0x1e, 0x1f, 0xf9, 0x17, 0x87, 0x0f, 0xc4, - 0x06, 0xa7, 0x02, 0x37, 0x0f, 0xc4, 0x06, 0xae, 0x00, 0x37, 0x0f, 0xc4, - 0x07, 0x5a, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0x77, 0x00, 0x37, 0x04, 0x84, - 0x0b, 0x21, 0x00, 0xde, 0x1f, 0xf9, 0x17, 0x9b, 0x0f, 0xc4, 0x06, 0xaa, - 0x02, 0x37, 0x0f, 0xc4, 0x07, 0x0b, 0x02, 0xb7, 0x0f, 0xc4, 0x00, 0x7a, - 0x00, 0x37, 0x0f, 0xc4, 0x07, 0x57, 0x06, 0xb7, 0x0f, 0xc4, 0x00, 0x74, - 0x00, 0x37, 0x04, 0x84, 0x0b, 0x21, 0x00, 0x9e, 0x1f, 0xf9, 0x17, 0xb5, - 0x00, 0x21, 0x01, 0x9f, 0x09, 0xe1, 0x01, 0xdf, 0x09, 0xe2, 0x0b, 0x25, - 0x09, 0x80, 0x0f, 0xc4, 0x06, 0xa4, 0x02, 0x37, 0x0f, 0xc4, 0x00, 0x77, - 0x02, 0xb7, 0x0f, 0xc4, 0x07, 0x67, 0x02, 0xb7, 0x0f, 0xc4, 0x06, 0x96, - 0x00, 0x37, 0x0f, 0xf7, 0x0d, 0x69, 0x04, 0x84, 0x0b, 0x21, 0x00, 0x1e, - 0x1f, 0xf9, 0x17, 0xef, 0x00, 0x21, 0x01, 0x9f, 0x09, 0xe2, 0x0b, 0x25, - 0x09, 0x80, 0x0f, 0xc4, 0x01, 0x5c, 0x0b, 0x21, 0x03, 0x95, 0x1f, 0xf9, - 0x17, 0xd3, 0x04, 0xc4, 0x0f, 0xc5, 0x06, 0x9f, 0x0f, 0xf7, 0x02, 0x74, - 0x03, 0x37, 0x03, 0xb7, 0x00, 0xb7, 0x05, 0x37, 0x04, 0x37, 0x00, 0x36, - 0x04, 0xc4, 0x0f, 0xf9, 0x17, 0xef, 0x0f, 0xc4, 0x01, 0x44, 0x0b, 0x88, - 0x00, 0xb6, 0x0b, 0x89, 0x0f, 0xc8, 0x1f, 0xf4, 0x01, 0x37, 0x0f, 0xc4, - 0x00, 0x86, 0x00, 0x37, 0x0f, 0xc4, 0x07, 0x08, 0x06, 0xb7, 0x0f, 0xc4, - 0x00, 0x89, 0x00, 0x37, 0x0f, 0xc4, 0x06, 0x9f, 0x02, 0xb7, 0x0f, 0xc4, - 0x00, 0x74, 0x02, 0xb7, 0x0f, 0xc4, 0x07, 0x64, 0x02, 0xb7, 0x00, 0x36, - 0x04, 0xc4, 0x04, 0x84, 0x0b, 0x21, 0x00, 0x5e, 0x1f, 0xf9, 0x18, 0x0c, - 0x00, 0x21, 0x01, 0x9f, 0x09, 0xe2, 0x0b, 0x25, 0x09, 0x80, 0x00, 0x21, - 0x0f, 0xc4, 0x06, 0xa2, 0x0b, 0x88, 0x06, 0xba, 0x0b, 0x89, 0x10, 0x21, - 0x1c, 0x18, 0x19, 0xc8, 0x1c, 0x51, 0x19, 0xc9, 0x0f, 0xc4, 0x06, 0x8f, - 0x0b, 0x21, 0x0c, 0x20, 0x09, 0xc2, 0x0b, 0x21, 0x0c, 0x59, 0x09, 0xc2, - 0x04, 0x84, 0x0b, 0x21, 0x01, 0x9e, 0x1f, 0xf9, 0x18, 0x33, 0x0f, 0xc4, - 0x06, 0x8f, 0x0b, 0x88, 0x00, 0xb6, 0x0b, 0x89, 0x06, 0xbb, 0x1f, 0xf7, - 0x00, 0x8f, 0x02, 0xb6, 0x04, 0xc4, 0x0f, 0xc4, 0x00, 0x92, 0x00, 0x37, - 0x0f, 0xc4, 0x06, 0x9c, 0x06, 0xb7, 0x03, 0xb7, 0x06, 0xbb, 0x0f, 0xe1, - 0x04, 0x00, 0x1f, 0xe1, 0x0c, 0x00, 0x0c, 0x59, 0x09, 0xc9, 0x0f, 0xc4, - 0x01, 0x30, 0x0c, 0x02, 0x0c, 0x42, 0x0f, 0xf0, 0x03, 0x0a, 0x0c, 0x32, - 0x0f, 0xf0, 0x03, 0x09, 0x0c, 0x72, 0x04, 0x84, 0x0b, 0x21, 0x01, 0xde, - 0x1f, 0xf9, 0x18, 0x4e, 0x0f, 0xc4, 0x06, 0x96, 0x02, 0x37, 0x01, 0x36, - 0x06, 0x08, 0x03, 0xb7, 0x0f, 0xc4, 0x01, 0x32, 0x0c, 0x02, 0x0c, 0x42, - 0x0c, 0x61, 0x02, 0xdf, 0x11, 0x3a, 0x12, 0x08, 0x1f, 0xf9, 0x18, 0x50, - 0x0f, 0xf0, 0x03, 0x1a, 0x0c, 0x32, 0x0f, 0xf0, 0x03, 0x19, 0x0c, 0x72, - 0x04, 0x84, 0x00, 0x00, 0x0f, 0x39, 0x0f, 0xf1, 0x03, 0x3a, 0x0f, 0xc4, - 0x07, 0xe2, 0x0a, 0x48, 0x0c, 0x00, 0x0f, 0xc4, 0x07, 0xe3, 0x0c, 0x00, - 0x0f, 0xf1, 0x03, 0x03, 0x0f, 0xc4, 0x07, 0xe4, 0x0f, 0xc2, 0x08, 0x00, - 0x0f, 0x38, 0x0a, 0x42, 0x0f, 0xc4, 0x07, 0xe1, 0x00, 0x21, 0x0b, 0x14, - 0x1f, 0xf9, 0x18, 0xd2, 0x0f, 0xc4, 0x07, 0xea, 0x0b, 0x15, 0x0f, 0xc8, - 0x03, 0x03, 0x1f, 0xc8, 0x03, 0x06, 0x0c, 0x31, 0x0f, 0xc4, 0x07, 0xe5, - 0x0a, 0x40, 0x0f, 0xf1, 0x03, 0x3a, 0x0f, 0xc4, 0x07, 0xe2, 0x0a, 0x48, - 0x0c, 0x00, 0x0f, 0xc4, 0x07, 0xe3, 0x0c, 0x00, 0x0f, 0xc4, 0x07, 0xe1, - 0x0b, 0x09, 0x0c, 0x61, 0x0c, 0x13, 0x1c, 0x21, 0x1c, 0x51, 0x09, 0xe1, - 0x0f, 0xc4, 0x07, 0xf1, 0x0b, 0x13, 0x1f, 0xf9, 0x18, 0xd2, 0x0c, 0x61, - 0x0c, 0x12, 0x0f, 0xcb, 0x18, 0xa4, 0x1f, 0xcb, 0x18, 0xb2, 0x09, 0xca, - 0x00, 0x21, 0x0c, 0x91, 0x19, 0xd1, 0x09, 0xe1, 0x0f, 0xc4, 0x07, 0xf2, - 0x0b, 0x16, 0x0f, 0xc4, 0x07, 0xe6, 0x1f, 0xc4, 0x07, 0xe8, 0x0b, 0x88, - 0x0b, 0x89, 0x0f, 0xc4, 0x07, 0xf0, 0x0b, 0x21, 0x0f, 0xc4, 0x07, 0xe4, - 0x0c, 0xf8, 0x00, 0x15, 0x1f, 0xf9, 0x18, 0xb4, 0x0b, 0xa1, 0x0c, 0x18, - 0x09, 0xc8, 0x0b, 0xa1, 0x0c, 0x51, 0x09, 0xc9, 0x00, 0x21, 0x0c, 0x56, - 0x10, 0x08, 0x0f, 0xf8, 0x18, 0xc1, 0x10, 0x09, 0x1f, 0xf9, 0x18, 0xa6, - 0x0b, 0xa1, 0x0c, 0x20, 0x09, 0xc8, 0x0b, 0xa1, 0x0c, 0x59, 0x09, 0xc9, - 0x0c, 0x61, 0x0f, 0xd6, 0x00, 0xff, 0x1f, 0xc8, 0x0f, 0xff, 0x1f, 0xc9, - 0x00, 0xff, 0x0f, 0xc4, 0x07, 0xe4, 0x0c, 0x02, 0x0c, 0x42, 0x00, 0x21, - 0x0f, 0xc4, 0x07, 0xea, 0x0b, 0x14, 0x1f, 0xf0, 0x03, 0x03, 0x1c, 0x72, - 0x0f, 0xc4, 0x07, 0xeb, 0x0b, 0x14, 0x1f, 0xf0, 0x03, 0x06, 0x1c, 0x72, - 0x0f, 0x39, 0x0f, 0xc5, 0x01, 0x65, 0x0f, 0xc4, 0x01, 0x81, 0x0b, 0x21, - 0x00, 0x14, 0x1f, 0x38, 0x10, 0x01, 0x0f, 0xc4, 0x07, 0x6c, 0x0b, 0x21, - 0x00, 0x14, 0x09, 0xcb, 0x1f, 0x38, 0x10, 0x01, 0x0f, 0xc4, 0x07, 0x6a, - 0x0b, 0x08, 0x0f, 0xc4, 0x07, 0x6b, 0x0b, 0x09, 0x0c, 0x21, 0x0c, 0x59, - 0x09, 0xc9, 0x0f, 0xc4, 0x01, 0x65, 0x0f, 0xc5, 0x01, 0x66, 0x0b, 0x21, - 0x00, 0x59, 0x09, 0xc0, 0x0c, 0x14, 0x1f, 0x38, 0x10, 0x01, 0x0c, 0x55, - 0x1f, 0x39, 0x0c, 0x00, 0x0b, 0x61, 0x0c, 0xd6, 0x1f, 0x39, 0x01, 0x38, - 0x01, 0xc8, 0x0b, 0x21, 0x0f, 0xd9, 0x00, 0x48, 0x09, 0xc5, 0x00, 0x59, - 0x09, 0xc0, 0x09, 0xe1, 0x02, 0x12, 0x0f, 0x38, 0x10, 0x00, 0x0f, 0xc4, - 0x01, 0xd2, 0x0b, 0x21, 0x02, 0x13, 0x1f, 0xf9, 0x19, 0x0e, 0x0f, 0x39, - 0x00, 0x59, 0x09, 0xc0, 0x0f, 0xc4, 0x01, 0xd0, 0x0f, 0xfb, 0x18, 0xfd, - 0x0f, 0x38, 0x0c, 0x01, 0x0f, 0xf1, 0x03, 0x39, 0x0f, 0xc4, 0x01, 0x6a, - 0x00, 0x00, 0x0f, 0xc4, 0x01, 0x6b, 0x0a, 0x40, 0x0f, 0xc4, 0x01, 0xd2, - 0x0b, 0x21, 0x00, 0x17, 0x1f, 0x38, 0x10, 0x00, 0x00, 0x51, 0x09, 0xc0, - 0x0f, 0xc4, 0x01, 0xd1, 0x0f, 0xfb, 0x18, 0xfd, 0x0b, 0x48, 0x0f, 0xc4, - 0x01, 0x6a, 0x0c, 0x38, 0x0c, 0x00, 0x05, 0x38, 0x0f, 0x08, 0x0d, 0x30, - 0x0d, 0x21, 0x00, 0x59, 0x09, 0xcc, 0x0d, 0x61, 0x0c, 0xa0, 0x09, 0xea, - 0x00, 0x21, 0x0c, 0xd9, 0x09, 0xeb, 0x0f, 0xee, 0x01, 0xc0, 0x1f, 0xee, - 0x0a, 0x7e, 0x0c, 0x21, 0x0e, 0x20, 0x09, 0xce, 0x0c, 0x61, 0x0e, 0x59, - 0x09, 0xf2, 0x0d, 0x83, 0x09, 0xc3, 0x0d, 0x21, 0x00, 0x59, 0x0d, 0x30, - 0x0d, 0xb2, 0x0f, 0x38, 0x09, 0xcc, 0x0f, 0xc4, 0x07, 0x0e, 0x00, 0x02, - 0x00, 0x02, 0x0f, 0xc2, 0x01, 0x90, 0x00, 0x02, 0x0f, 0xc2, 0x00, 0x83, - 0x00, 0x42, 0x0f, 0xc2, 0x04, 0x07, 0x0f, 0x38, 0x00, 0x42, 0x0f, 0xc4, - 0x07, 0x0e, 0x0b, 0x8a, 0x0f, 0xfa, 0x19, 0x31, 0x0b, 0x8b, 0x0b, 0x8a, - 0x0f, 0xfa, 0x19, 0x31, 0x0b, 0x8b, 0x0b, 0x8a, 0x0f, 0xfa, 0x19, 0x31, - 0x0b, 0x8b, 0x0b, 0x8a, 0x0f, 0xf8, 0x19, 0x31, 0x0b, 0x8b, 0x0f, 0xc5, - 0x04, 0xa8, 0x07, 0x37, 0x0f, 0xcc, 0x05, 0x38, 0x0f, 0xcd, 0x02, 0xb0, - 0x0f, 0xfb, 0x19, 0x5b, 0x0f, 0xcc, 0x0a, 0x38, 0x0f, 0xfa, 0x19, 0x5b, - 0x00, 0x0d, 0x0f, 0xf0, 0x05, 0x2e, 0x00, 0xf2, 0x0f, 0xf1, 0x0a, 0x2e, - 0x0a, 0x62, 0x0f, 0xe5, 0x01, 0xc0, 0x0f, 0x38, 0x09, 0xb2, 0x00, 0x04, - 0x0c, 0x00, 0x0c, 0x21, 0x02, 0xdf, 0x1f, 0xf9, 0x19, 0x94, 0x0c, 0x27, - 0x0c, 0x21, 0x09, 0x99, 0x09, 0xc8, 0x0f, 0xfa, 0x19, 0x6d, 0x00, 0x09, - 0x0f, 0xf9, 0x19, 0xa1, 0x00, 0x21, 0x0c, 0x11, 0x09, 0xe7, 0x09, 0xe1, - 0x09, 0x99, 0x09, 0xc8, 0x00, 0x21, 0x0c, 0x11, 0x09, 0xc8, 0x0f, 0xc9, - 0x1f, 0xff, 0x0f, 0xfb, 0x19, 0x6d, 0x00, 0x04, 0x0f, 0x38, 0x0b, 0x08, - 0x0f, 0xf1, 0x0a, 0x33, 0x0f, 0xc4, 0x05, 0xe8, 0x0a, 0x48, 0x0c, 0x02, - 0x0f, 0xee, 0x0a, 0x7f, 0x0c, 0x2a, 0x00, 0x2b, 0x0f, 0xee, 0x02, 0x40, - 0x0f, 0xee, 0x0a, 0x01, 0x08, 0x36, 0x02, 0x08, 0x0f, 0xc4, 0x05, 0xe9, - 0x0b, 0x21, 0x00, 0x14, 0x1f, 0xc4, 0x01, 0x41, 0x1b, 0x22, 0x1f, 0xe4, - 0x0f, 0xef, 0x19, 0x80, 0x00, 0x1e, 0x0f, 0xc5, 0x01, 0x7d, 0x1f, 0x38, - 0x1c, 0x41, 0x00, 0x2b, 0x0c, 0x6a, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, - 0x0a, 0x78, 0x0b, 0x6a, 0x0f, 0xee, 0x02, 0x40, 0x0f, 0xee, 0x0a, 0x08, - 0x0f, 0xc4, 0x01, 0x40, 0x04, 0x22, 0x0e, 0x02, 0x0e, 0x65, 0x0f, 0x38, - 0x09, 0x82, 0x0f, 0xc4, 0x05, 0xf3, 0x0f, 0xc2, 0x0f, 0x80, 0x0f, 0xc2, - 0x0f, 0xff, 0x0f, 0xc2, 0x07, 0xff, 0x00, 0x02, 0x0f, 0xc2, 0x0f, 0xc0, - 0x01, 0x82, 0x0f, 0xc4, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x82, 0x01, 0x82, - 0x03, 0x02, 0x07, 0x02, 0x0f, 0xc2, 0x00, 0x30, 0x0f, 0xc2, 0x00, 0x70, - 0x0f, 0xc2, 0x00, 0xf0, 0x0f, 0xc2, 0x01, 0xd0, 0x0f, 0xc2, 0x03, 0x50, - 0x0f, 0xc2, 0x07, 0x50, 0x0f, 0xc2, 0x0e, 0x50, 0x0f, 0x39, 0x0f, 0xc4, - 0x01, 0xd4, 0x0f, 0x00, 0x0f, 0xc4, 0x05, 0xf9, 0x02, 0x37, 0x0f, 0xc4, - 0x06, 0x34, 0x02, 0xb7, 0x0f, 0xc4, 0x05, 0xf6, 0x0f, 0xf7, 0x02, 0x85, - 0x0f, 0xf7, 0x01, 0x33, 0x0f, 0xc4, 0x01, 0xd3, 0x0c, 0x21, 0x00, 0x51, - 0x09, 0xc0, 0x09, 0xcf, 0x09, 0xe1, 0x0f, 0xd9, 0x00, 0x3c, 0x09, 0xc4, - 0x0b, 0x0d, 0x0f, 0xc4, 0x05, 0xf9, 0x0f, 0xc5, 0x01, 0x2b, 0x04, 0xb7, - 0x0f, 0xc4, 0x00, 0x70, 0x0d, 0x42, 0x0d, 0x82, 0x01, 0xba, 0x0d, 0xc2, - 0x0f, 0xc4, 0x00, 0x70, 0x0b, 0x8d, 0x0b, 0x8e, 0x0b, 0x8f, 0x0d, 0xe1, - 0x00, 0x2a, 0x00, 0x59, 0x09, 0xeb, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, - 0x0a, 0xcb, 0x05, 0x84, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0x82, 0x0f, 0xc4, - 0x01, 0x28, 0x00, 0x37, 0x0f, 0xc4, 0x05, 0xf9, 0x06, 0xb7, 0x0f, 0xf7, - 0x01, 0x33, 0x0c, 0x21, 0x0d, 0xe7, 0x09, 0x91, 0x09, 0xce, 0x0d, 0xa1, - 0x0f, 0xd2, 0x00, 0x80, 0x1f, 0xce, 0x00, 0x7f, 0x0d, 0xa1, 0x09, 0x99, - 0x00, 0x2a, 0x09, 0xeb, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0xcb, - 0x05, 0x84, 0x0e, 0x02, 0x0e, 0x42, 0x0e, 0x82, 0x0f, 0xc4, 0x01, 0x28, - 0x0f, 0xf7, 0x02, 0x81, 0x0f, 0xc4, 0x05, 0xf9, 0x0f, 0xf7, 0x02, 0x93, - 0x0f, 0xf7, 0x02, 0x05, 0x02, 0xdf, 0x1f, 0xf9, 0x1a, 0x69, 0x0f, 0xc4, - 0x01, 0x2b, 0x0f, 0xf7, 0x02, 0x90, 0x0f, 0xf7, 0x02, 0x05, 0x1f, 0xf9, - 0x1a, 0x69, 0x06, 0x44, 0x0f, 0xc5, 0x01, 0x2b, 0x04, 0xb7, 0x0f, 0xc4, - 0x05, 0xfc, 0x0d, 0x80, 0x0f, 0xc4, 0x05, 0xfd, 0x0d, 0xc0, 0x0d, 0xe1, - 0x00, 0x53, 0x1f, 0xf9, 0x1a, 0x74, 0x09, 0xcf, 0x0d, 0x61, 0x0d, 0xdf, - 0x1f, 0xf9, 0x1a, 0x16, 0x0f, 0xf9, 0x1a, 0x69, 0x0f, 0xc4, 0x01, 0xd4, - 0x0b, 0x39, 0x0f, 0xc4, 0x05, 0xff, 0x0b, 0x0f, 0x0d, 0xe1, 0x00, 0x2b, - 0x0f, 0xea, 0x0c, 0xe5, 0x00, 0xd4, 0x1f, 0xea, 0x05, 0xac, 0x01, 0x14, - 0x1f, 0xea, 0x03, 0x44, 0x01, 0x94, 0x1f, 0xea, 0x01, 0x56, 0x02, 0x14, - 0x1f, 0xea, 0x00, 0x83, 0x0f, 0xc4, 0x05, 0xf0, 0x0b, 0x0e, 0x0d, 0xa1, - 0x00, 0x2c, 0x03, 0x2d, 0x00, 0x54, 0x11, 0xad, 0x00, 0x94, 0x11, 0x2d, - 0x0f, 0xee, 0x0c, 0x80, 0x00, 0xa1, 0x0f, 0xc4, 0x01, 0x73, 0x0e, 0x19, - 0x09, 0xc0, 0x0f, 0xc4, 0x01, 0x75, 0x00, 0x40, 0x0d, 0xa1, 0x00, 0x94, - 0x10, 0x00, 0x00, 0x54, 0x0d, 0xe1, 0x10, 0x96, 0x10, 0x00, 0x00, 0xd6, - 0x0f, 0x38, 0x10, 0x00, 0x0f, 0xc8, 0x1a, 0xab, 0x05, 0x39, 0x0f, 0xf7, - 0x0d, 0xf7, 0x0f, 0xfb, 0x19, 0xa4, 0x0f, 0xc4, 0x07, 0xba, 0x0f, 0xfb, - 0x02, 0xfd, 0x01, 0xbb, 0x0f, 0xf7, 0x0e, 0x2a, 0x01, 0xbb, 0x0f, 0xf7, - 0x0e, 0x7e, 0x01, 0xbb, 0x0f, 0xfb, 0x19, 0xf7, 0x01, 0xbb, 0x0f, 0xf7, - 0x0e, 0xff, 0x01, 0xbb, 0x0f, 0xfb, 0x1a, 0x77, 0x0f, 0xf6, 0x00, 0x42, - 0x02, 0x8b, 0x1f, 0xf7, 0x0f, 0x52, 0x0f, 0xc4, 0x06, 0x00, 0x0f, 0xf0, - 0x0a, 0x01, 0x0f, 0x38, 0x0b, 0x32, 0x0f, 0xff, 0x0f, 0x6b, 0x0f, 0xf1, - 0x0c, 0x0a, 0x0f, 0xc4, 0x01, 0x6d, 0x0a, 0x49, 0x0c, 0x61, 0x00, 0x1e, - 0x1f, 0xf8, 0x1b, 0x0b, 0x0c, 0x40, 0x0f, 0xf1, 0x0c, 0x07, 0x0f, 0xc4, - 0x01, 0x36, 0x0f, 0xc5, 0x07, 0x60, 0x00, 0x2b, 0x0a, 0x6a, 0x0f, 0xf1, - 0x0c, 0x08, 0x0f, 0xee, 0x01, 0xc0, 0x0f, 0xee, 0x0a, 0x06, 0x0e, 0x22, - 0x0a, 0x65, 0x09, 0x82, 0x0e, 0x42, 0x0f, 0xc4, 0x01, 0x56, 0x0b, 0x21, - 0x09, 0xa0, 0x09, 0xc2, 0x0b, 0x21, 0x0e, 0x59, 0x09, 0xc2, 0x0f, 0xc4, - 0x01, 0xd5, 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x0b, 0x53, - 0x1f, 0xf9, 0x1b, 0x0b, 0x0f, 0xc5, 0x01, 0x76, 0x0b, 0x01, 0x00, 0x00, - 0x0f, 0xc5, 0x01, 0x38, 0x0f, 0xc4, 0x01, 0x56, 0x0b, 0x03, 0x00, 0x02, - 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xfa, 0x03, 0x00, 0x02, 0x08, 0x0c, 0x61, - 0x00, 0x5e, 0x1f, 0x39, 0x0f, 0xf1, 0x0c, 0x0c, 0x0f, 0xc4, 0x01, 0x5a, - 0x0b, 0x21, 0x0a, 0x60, 0x09, 0xc2, 0x0b, 0x21, 0x00, 0x19, 0x09, 0xc0, - 0x0f, 0xf1, 0x0c, 0x09, 0x0f, 0xc4, 0x01, 0x58, 0x0b, 0x21, 0x0f, 0xc5, - 0x07, 0x61, 0x0a, 0x48, 0x0c, 0x20, 0x09, 0xc2, 0x0b, 0x21, 0x00, 0x19, - 0x09, 0xc2, 0x0f, 0xc4, 0x01, 0x74, 0x0c, 0x00, 0x0f, 0xc4, 0x01, 0xd6, - 0x0b, 0x21, 0x00, 0x59, 0x09, 0xc0, 0x09, 0xe1, 0x0b, 0x53, 0x1f, 0x39, - 0x0f, 0xc5, 0x01, 0x77, 0x0b, 0x01, 0x00, 0x00, 0x0f, 0xc5, 0x01, 0x3a, - 0x0f, 0xc4, 0x01, 0x58, 0x0b, 0x03, 0x00, 0x02, 0x0b, 0x03, 0x00, 0x02, - 0x0f, 0xc5, 0x01, 0x3c, 0x0f, 0xc4, 0x01, 0x5a, 0x0b, 0x03, 0x00, 0x02, - 0x0b, 0x03, 0x00, 0x02, 0x0f, 0xf8, 0x03, 0x00, 0x04, 0x08, 0x0f, 0xf1, - 0x0f, 0x17, 0x0f, 0xe2, 0x0f, 0xf0, 0x0c, 0x21, 0x0a, 0x64, 0x09, 0x99, - 0x09, 0xf2, 0x00, 0x09, 0x0c, 0x22, 0x03, 0xe4, 0x09, 0xa1, 0x00, 0x94, - 0x11, 0x09, 0x00, 0x54, 0x10, 0x49, 0x01, 0x54, 0x10, 0x89, 0x0c, 0x61, - 0x00, 0x14, 0x1f, 0x39, 0x01, 0x14, 0x0f, 0xc4, 0x01, 0xd7, 0x0b, 0x21, - 0x10, 0x40, 0x10, 0x15, 0x1f, 0x39, 0x0f, 0xf8, 0x03, 0x00, 0x0c, 0x48, - 0x0f, 0x39, 0x00, 0x04, 0x0f, 0x00, 0x04, 0x04, 0x0b, 0x27, 0x09, 0xa7, - 0x09, 0xa7, 0x09, 0xa7, 0x09, 0xa2, 0x0c, 0x25, 0x0f, 0xc5, 0x07, 0x6e, - 0x09, 0x81, 0x07, 0xba, 0x04, 0x48, 0x0f, 0xc4, 0x07, 0x6d, 0x0b, 0x21, - 0x00, 0x15, 0x00, 0x48, 0x12, 0x08, 0x04, 0x04, 0x0c, 0x00, 0x00, 0x04, - 0x0b, 0x39, 0x00, 0x04, 0x0f, 0x00, 0x0f, 0xc4, 0x07, 0x80, 0x0f, 0xc8, - 0x08, 0x00, 0x0f, 0xf7, 0x00, 0x71, 0x0f, 0xf1, 0x0f, 0x14, 0x0f, 0xc4, - 0x07, 0xf4, 0x0a, 0x42, 0x0f, 0xf1, 0x0f, 0x15, 0x0a, 0x42, 0x0f, 0xf1, - 0x03, 0x0a, 0x0f, 0xc4, 0x07, 0xf6, 0x0a, 0x42, 0x0f, 0xf1, 0x03, 0x09, - 0x0a, 0x42, 0x0f, 0xf1, 0x03, 0x1a, 0x0f, 0xc4, 0x07, 0xf8, 0x0a, 0x42, - 0x0f, 0xf1, 0x03, 0x19, 0x0a, 0x42, 0x0f, 0xf1, 0x03, 0x38, 0x0f, 0xc4, - 0x07, 0xfa, 0x0a, 0x40, 0x0f, 0xf1, 0x03, 0x11, 0x0f, 0xc4, 0x07, 0xfb, - 0x0a, 0x40, 0x0f, 0xf1, 0x0c, 0x05, 0x0f, 0xc4, 0x07, 0xfc, 0x0a, 0x40, - 0x0f, 0xc4, 0x07, 0xfe, 0x0f, 0xc2, 0x07, 0x06, 0x0f, 0xc2, 0x00, 0x80, - 0x00, 0x04, 0x0b, 0x39, 0x0f, 0xc4, 0x07, 0xf3, 0x0f, 0x00, 0x0f, 0xc4, - 0x06, 0xc0, 0x0f, 0xc8, 0x07, 0x80, 0x0f, 0xf7, 0x00, 0x71, 0x0f, 0xf0, - 0x0f, 0x17, 0x00, 0x32, 0x0f, 0xf0, 0x02, 0x04, 0x00, 0x32, 0x0f, 0xfa, - 0x01, 0x92, 0x0e, 0xff, 0x0f, 0xc8, 0x08, 0x89, 0x0f, 0xc9, 0x08, 0x88, - 0x0f, 0xca, 0x07, 0xfc, 0x0f, 0xcb, 0x07, 0xfe, 0x0f, 0xc4, 0x06, 0xe3, - 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0xc2, 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0xc2, - 0x0f, 0xc2, 0x07, 0x0a, 0x0f, 0xc2, 0x0a, 0x3d, 0x0f, 0xc2, 0x07, 0xf6, - 0x0f, 0xc2, 0x03, 0x9c, 0x0f, 0xc2, 0x0c, 0x95, 0x0f, 0xc2, 0x07, 0xed, - 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0x82, 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0x82, - 0x0f, 0xc2, 0x07, 0x0a, 0x0f, 0xc2, 0x0a, 0x3d, 0x0f, 0xc2, 0x07, 0xf3, - 0x0f, 0xc2, 0x03, 0x9c, 0x0f, 0xc2, 0x0c, 0x95, 0x0f, 0xc2, 0x07, 0xea, - 0x0f, 0xc9, 0x08, 0x00, 0x0f, 0xca, 0x07, 0xfe, 0x0f, 0xc4, 0x07, 0x45, - 0x00, 0x02, 0x0f, 0xc2, 0x0c, 0x00, 0x0c, 0x82, 0x00, 0x02, 0x0c, 0x42, - 0x0c, 0x82, 0x0f, 0xc2, 0x0a, 0xab, 0x0f, 0xc2, 0x0a, 0xaa, 0x0f, 0xc2, - 0x07, 0xfd, 0x0f, 0xc2, 0x07, 0x0a, 0x0f, 0xc2, 0x0a, 0x3d, 0x0f, 0xc2, - 0x07, 0xf8, 0x0f, 0xc4, 0x07, 0x57, 0x00, 0x02, 0x0c, 0x42, 0x0f, 0xc2, - 0x07, 0xfa, 0x00, 0x02, 0x0c, 0x42, 0x0f, 0xc2, 0x07, 0xf0, 0x0f, 0xc4, - 0x07, 0x08, 0x0f, 0xc2, 0x09, 0x51, 0x0f, 0xc2, 0x09, 0xd4, 0x0f, 0xc2, - 0x07, 0xf5, 0x0f, 0xc4, 0x07, 0x0b, 0x00, 0x02, 0x0f, 0xc2, 0x08, 0x00, - 0x0f, 0xc2, 0x07, 0xfd, 0x0f, 0xc4, 0x07, 0x5d, 0x0f, 0xc0, 0x0d, 0xbf, - 0x0f, 0xc4, 0x07, 0xe1, 0x0f, 0xc0, 0x00, 0x50, 0x0f, 0xc4, 0x07, 0xe6, - 0x0f, 0xc2, 0x00, 0x40, 0x00, 0x02, 0x0f, 0xc2, 0x08, 0x00, 0x00, 0x02, - 0x0f, 0xc4, 0x07, 0xf0, 0x00, 0x02, 0x02, 0x02, 0x04, 0x02, 0x0f, 0xc4, - 0x07, 0x5e, 0x0f, 0xc2, 0x00, 0x64, 0x00, 0x02, 0x0f, 0xc4, 0x07, 0x26, - 0x02, 0x40, 0x0f, 0xf1, 0x03, 0x04, 0x0f, 0xc4, 0x07, 0x62, 0x02, 0x82, - 0x00, 0x02, 0x0f, 0xc4, 0x07, 0x6a, 0x0f, 0xc2, 0x0d, 0xac, 0x0f, 0xc2, - 0x00, 0x64, 0x0f, 0xc2, 0x00, 0x55, 0x0f, 0xc4, 0x07, 0x6d, 0x00, 0x00, - 0x0f, 0xc4, 0x07, 0xef, 0x02, 0x00, 0x0a, 0x72, 0x0f, 0xc4, 0x07, 0x70, - 0x0f, 0xc0, 0x0f, 0xff, 0x0f, 0xc4, 0x07, 0x72, 0x03, 0xc2, 0x0f, 0xc2, - 0x02, 0x80, 0x0f, 0xc2, 0x01, 0xe0, 0x02, 0x82, 0x0f, 0xc4, 0x06, 0xff, - 0x0f, 0xc2, 0x08, 0x00, 0x0f, 0xfb, 0x19, 0x4d, 0x0f, 0xf1, 0x0d, 0x00, - 0x0a, 0x61, 0x02, 0xde, 0x0f, 0xc4, 0x07, 0x22, 0x00, 0x40, 0x0f, 0xc4, - 0x07, 0x2b, 0x0f, 0xc2, 0x02, 0x6e, 0x0f, 0xc2, 0x08, 0x31, 0x0f, 0xc2, - 0x07, 0xf9, 0x0f, 0xc2, 0x0b, 0xa5, 0x0f, 0xc2, 0x0c, 0x49, 0x0f, 0xc2, - 0x07, 0xf7, 0x0f, 0xc2, 0x00, 0x38, 0x0f, 0xc2, 0x0e, 0xc3, 0x00, 0xc2, - 0x0f, 0xc2, 0x05, 0x0d, 0x0f, 0xc2, 0x0f, 0x5e, 0x00, 0x42, 0x02, 0x82, - 0x01, 0x82, 0x02, 0x02, 0x00, 0x42, 0x1f, 0xc4, 0x07, 0x5d, 0x1f, 0xe2, - 0x0e, 0xff, 0x1b, 0x24, 0x19, 0x80, 0x0f, 0xc4, 0x07, 0x60, 0x02, 0x00, - 0x12, 0x00, 0x0f, 0xc4, 0x07, 0x61, 0x01, 0x00, 0x0f, 0xf8, 0x1c, 0xa7, - 0x11, 0x00, 0x0f, 0xc4, 0x06, 0xfc, 0x00, 0x40, 0x0f, 0xc4, 0x07, 0xf3, - 0x0f, 0xf8, 0x1c, 0xa7, 0x00, 0x00, 0x0f, 0xc4, 0x07, 0xf3, 0x00, 0x21, - 0x0b, 0x14, 0x1f, 0x00, 0x0f, 0xc4, 0x07, 0xfd, 0x0b, 0x08, 0x00, 0x61, - 0x0c, 0x19, 0x09, 0xc0, 0x09, 0xe1, 0x00, 0x14, 0x10, 0x80, 0x0f, 0xfb, - 0x1e, 0xe0, 0x0f, 0xc4, 0x07, 0xf6, 0x0f, 0xf0, 0x03, 0x0a, 0x0b, 0xb2, - 0x0f, 0xf0, 0x03, 0x09, 0x0b, 0xb2, 0x0f, 0xc4, 0x07, 0xf8, 0x0b, 0x88, - 0x0b, 0x89, 0x0f, 0xf0, 0x03, 0x1a, 0x0c, 0x32, 0x0f, 0xf0, 0x03, 0x19, - 0x0c, 0x72, 0x0f, 0xc4, 0x07, 0xfa, 0x0f, 0xf0, 0x03, 0x38, 0x0b, 0x32, - 0x0f, 0xc4, 0x07, 0xfb, 0x0f, 0xf0, 0x03, 0x11, 0x0b, 0x32, 0x0f, 0xc4, - 0x07, 0xfc, 0x0f, 0xf0, 0x0c, 0x05, 0x0f, 0xf8, 0x1c, 0xdd, 0x0b, 0x32, - 0x0f, 0xc4, 0x07, 0xf3, 0x0f, 0x00, 0x00, 0x04, 0x0f, 0xc8, 0x06, 0xc0, - 0x0f, 0xf7, 0x00, 0x71, 0x0f, 0xc4, 0x07, 0x3d, 0x0f, 0xc5, 0x07, 0x3e, - 0x0b, 0x21, 0x0f, 0xd5, 0x00, 0x3c, 0x1f, 0xf8, 0x1c, 0xff, 0x10, 0x01, - 0x0f, 0xf1, 0x0f, 0x15, 0x01, 0xe2, 0x00, 0x21, 0x0f, 0xc4, 0x07, 0xf5, - 0x0a, 0x48, 0x0c, 0x24, 0x09, 0x95, 0x1c, 0x00, 0x0b, 0x61, 0x00, 0x59, - 0x09, 0xc1, 0x00, 0xd2, 0x10, 0x01, 0x1b, 0x22, 0x1f, 0xe4, 0x0f, 0xf8, - 0x19, 0x80, 0x02, 0x3a, 0x00, 0x48, 0x02, 0xba, 0x00, 0x48, 0x0f, 0xc4, - 0x07, 0xc2, 0x00, 0x61, 0x0b, 0x15, 0x1f, 0xc4, 0x07, 0x62, 0x1b, 0x88, - 0x1f, 0xf6, 0x00, 0x4c, 0x1b, 0x89, 0x0f, 0xfb, 0x01, 0x92, 0x0f, 0xfb, - 0x19, 0xd5, 0x0f, 0xc4, 0x05, 0x80, 0x00, 0x02, 0x0f, 0xc2, 0x0e, 0x00, - 0x0f, 0xc2, 0x07, 0xff, 0x0f, 0xc4, 0x06, 0x31, 0x0f, 0xc5, 0x05, 0x83, - 0x04, 0xb7, 0x0f, 0xfb, 0x12, 0xf9, 0x0f, 0xc4, 0x01, 0x5e, 0x0f, 0xc0, - 0x1c, 0xda, 0x0f, 0xc4, 0x06, 0x92, 0x0f, 0xc5, 0x07, 0x5d, 0x0b, 0x40, - 0x0f, 0xc4, 0x07, 0xf4, 0x0b, 0x21, 0x01, 0x88, 0x00, 0xdf, 0x11, 0xc8, - 0x01, 0x1e, 0x12, 0x08, 0x0f, 0xc4, 0x01, 0x7c, 0x0c, 0x00, 0x0f, 0xfb, - 0x1f, 0x0b, 0x0f, 0xf1, 0x03, 0x11, 0x00, 0xe2, 0x0f, 0xc4, 0x06, 0x8e, - 0x0a, 0x64, 0x09, 0x80, 0x0f, 0xf1, 0x0c, 0x05, 0x0f, 0xc4, 0x05, 0xf2, - 0x0a, 0x48, 0x0c, 0x00, 0x0f, 0xf1, 0x03, 0x38, 0x0a, 0x62, 0x00, 0x64, - 0x09, 0xa1, 0x00, 0x54, 0x03, 0x48, 0x12, 0xc8, 0x04, 0x44, 0x0c, 0x00, - 0x0f, 0xf1, 0x03, 0x1a, 0x0a, 0x48, 0x0f, 0xf1, 0x03, 0x19, 0x0a, 0x49, - 0x0f, 0xc4, 0x01, 0x32, 0x0c, 0x02, 0x00, 0xb6, 0x0c, 0x42, 0x0f, 0xc8, - 0x1f, 0xe8, 0x01, 0x37, 0x0f, 0xc4, 0x06, 0x96, 0x00, 0x37, 0x0f, 0xc4, - 0x06, 0x2b, 0x02, 0xb7, 0x0f, 0xf7, 0x04, 0x66, 0x0f, 0xc4, 0x01, 0x7c, - 0x0b, 0x08, 0x00, 0xb6, 0x00, 0x09, 0x03, 0x37, 0x0f, 0xf7, 0x04, 0x72, - 0x0f, 0xf7, 0x01, 0x39, 0x0f, 0xc8, 0x1f, 0xfd, 0x01, 0x37, 0x0f, 0xc4, - 0x00, 0x8f, 0x00, 0x37, 0x0f, 0xf7, 0x0d, 0x69, 0x0f, 0xf1, 0x03, 0x0a, - 0x0a, 0x48, 0x0f, 0xf1, 0x03, 0x09, 0x0a, 0x49, 0x0f, 0xc4, 0x01, 0x30, - 0x0c, 0x02, 0x0c, 0x42, 0x0c, 0x61, 0x00, 0x17, 0x1f, 0xf9, 0x1d, 0x8b, - 0x0f, 0xc4, 0x01, 0x62, 0x00, 0x00, 0x0c, 0x61, 0x0f, 0xd1, 0x04, 0x00, - 0x0f, 0xf8, 0x1d, 0x97, 0x09, 0xc9, 0x0f, 0xc4, 0x01, 0x62, 0x00, 0x40, - 0x0c, 0x61, 0x0f, 0xd1, 0x0c, 0x00, 0x09, 0xc9, 0x00, 0x21, 0x0c, 0x18, - 0x09, 0xc8, 0x0c, 0x51, 0x09, 0xc9, 0x00, 0xb7, 0x0f, 0xc4, 0x06, 0x99, - 0x06, 0xb7, 0x03, 0x37, 0x03, 0xb7, 0x0f, 0xc4, 0x06, 0x8f, 0x0c, 0x02, - 0x00, 0xb6, 0x0c, 0x42, 0x05, 0x37, 0x04, 0x37, 0x06, 0xbb, 0x1f, 0xf7, - 0x00, 0x8f, 0x00, 0x36, 0x04, 0xc4, 0x0f, 0xfa, 0x07, 0xa2, 0x00, 0x0e, - 0x07, 0x37, 0x0f, 0xf0, 0x03, 0x0b, 0x00, 0x32, 0x0f, 0xf0, 0x03, 0x1b, - 0x00, 0x32, 0x0f, 0xf1, 0x03, 0x1d, 0x0a, 0x7f, 0x0f, 0xf0, 0x03, 0x1c, - 0x0f, 0xf2, 0x00, 0x3f, 0x0f, 0xf0, 0x03, 0x1e, 0x00, 0x32, 0x0f, 0xf0, - 0x03, 0x1f, 0x00, 0x32, 0x0f, 0xf0, 0x03, 0x2a, 0x0f, 0xf2, 0x07, 0xff, - 0x0f, 0xf0, 0x03, 0x29, 0x00, 0xb2, 0x0f, 0xf0, 0x03, 0x2b, 0x0f, 0xf2, - 0x00, 0x28, 0x0f, 0xf0, 0x03, 0x2f, 0x00, 0x72, 0x0f, 0xf0, 0x04, 0x00, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x11, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x15, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x17, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x0c, - 0x00, 0xb2, 0x0f, 0xf0, 0x05, 0x02, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x08, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x01, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x00, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x07, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x06, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x04, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x05, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x0a, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x0b, - 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x03, 0x00, 0x32, 0x0f, 0xf0, 0x05, 0x09, - 0x00, 0x32, 0x0f, 0xf0, 0x0a, 0x00, 0x05, 0xb2, 0x0f, 0xc8, 0x01, 0x13, - 0x0f, 0xc4, 0x06, 0x00, 0x0f, 0xf0, 0x0a, 0x01, 0x0c, 0x32, 0x0c, 0x00, - 0x0f, 0xf0, 0x0a, 0x20, 0x00, 0x32, 0x0f, 0xf0, 0x0a, 0x10, 0x00, 0x72, - 0x0f, 0xf0, 0x0a, 0x11, 0x00, 0x32, 0x0f, 0xf0, 0x0a, 0x0a, 0x00, 0x32, - 0x0f, 0xf0, 0x0a, 0x07, 0x04, 0x32, 0x0f, 0xf0, 0x0a, 0x2e, 0x0f, 0xf2, - 0x01, 0xc8, 0x0f, 0xf0, 0x0a, 0x30, 0x0f, 0xf6, 0x0d, 0xdb, 0x06, 0x32, - 0x0f, 0xf0, 0x0c, 0x11, 0x02, 0xb2, 0x0f, 0xf0, 0x0c, 0x12, 0x00, 0x32, - 0x0f, 0xf0, 0x0c, 0x03, 0x00, 0x72, 0x0f, 0xf0, 0x0c, 0x1a, 0x0f, 0xf2, - 0x08, 0x72, 0x0f, 0xf1, 0x0c, 0x1b, 0x0a, 0x62, 0x01, 0x25, 0x09, 0xb2, - 0x0f, 0xc4, 0x01, 0x80, 0x00, 0x40, 0x0f, 0xc8, 0x0c, 0x1d, 0x0c, 0x30, - 0x0f, 0xf2, 0x00, 0xa0, 0x0c, 0x31, 0x0a, 0x61, 0x0f, 0xd5, 0x02, 0xa0, - 0x10, 0x00, 0x0c, 0x30, 0x0f, 0xf2, 0x00, 0x60, 0x0c, 0x31, 0x0a, 0x61, - 0x0f, 0xd5, 0x01, 0x60, 0x10, 0x00, 0x0f, 0xf0, 0x0c, 0x1a, 0x0f, 0xf2, - 0x08, 0x71, 0x0f, 0xf1, 0x0d, 0x00, 0x0a, 0x49, 0x0c, 0x61, 0x02, 0xde, - 0x0c, 0x62, 0x0f, 0xe4, 0x08, 0x18, 0x1f, 0xe4, 0x08, 0x1e, 0x09, 0xa2, - 0x04, 0x25, 0x09, 0xb2, 0x0f, 0xf1, 0x0d, 0x01, 0x00, 0x22, 0x1f, 0xe2, - 0x0a, 0xaa, 0x0a, 0x64, 0x09, 0xb2, 0x0f, 0xc4, 0x06, 0x93, 0x0f, 0xc0, - 0x00, 0x2b, 0x0f, 0xc8, 0x0f, 0xff, 0x09, 0x37, 0x0f, 0xc4, 0x06, 0x01, - 0x00, 0x40, 0x04, 0x84, 0x00, 0x00, 0x04, 0x04, 0x00, 0x80, 0x0f, 0xc4, - 0x06, 0xba, 0x0f, 0xc0, 0x06, 0xbb, 0x04, 0x04, 0x00, 0x40, 0x0f, 0xc8, - 0x00, 0x28, 0x0f, 0xf7, 0x05, 0x23, 0x0f, 0xf6, 0x05, 0x23, 0x02, 0x88, - 0x00, 0x69, 0x0e, 0xff, 0x00, 0x28, 0x0e, 0xff, 0x02, 0x3a, 0x00, 0x08, - 0x02, 0xba, 0x00, 0x08, 0x0f, 0xc4, 0x01, 0xc1, 0x0f, 0xc0, 0x00, 0x47, - 0x0f, 0xc4, 0x07, 0xf3, 0x0b, 0x08, 0x0c, 0x38, 0x00, 0x00, 0x01, 0x04, - 0x0f, 0xc2, 0x0c, 0x18, 0x0f, 0xc2, 0x03, 0x01, 0x0f, 0xc2, 0x0d, 0x01, - 0x0f, 0xc2, 0x0d, 0x00, 0x0f, 0xc2, 0x03, 0x03, 0x0f, 0xc2, 0x03, 0x06, - 0x0f, 0xc2, 0x03, 0x05, 0x0f, 0xc2, 0x03, 0x28, 0x0f, 0xc2, 0x03, 0x18, - 0x0f, 0xc2, 0x03, 0x3c, 0x0f, 0xc2, 0x03, 0x3b, 0x0f, 0xc2, 0x03, 0x16, - 0x0f, 0xc2, 0x03, 0x17, 0x0f, 0xc2, 0x03, 0x26, 0x0f, 0xc2, 0x03, 0x27, - 0x0f, 0xc2, 0x03, 0x24, 0x0f, 0xc2, 0x03, 0x25, 0x0f, 0xc2, 0x03, 0x12, - 0x0f, 0xc2, 0x0f, 0x00, 0x0f, 0xc8, 0x0f, 0x12, 0x0c, 0x02, 0x0c, 0x02, - 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x0c, 0x02, - 0x0f, 0xc2, 0x0f, 0x10, 0x0f, 0xc2, 0x0f, 0x13, 0x0f, 0xc2, 0x02, 0x01, - 0x0f, 0xc2, 0x02, 0x04, 0x0f, 0xc2, 0x02, 0x03, 0x0f, 0xc2, 0x02, 0x06, - 0x00, 0xc5, 0x0a, 0x83, 0x0a, 0xa1, 0x0b, 0xf1, 0x0a, 0xd5, 0x0a, 0x42, - 0x1b, 0xf1, 0x1f, 0xf8, 0x1e, 0xd1, 0x1a, 0xd5, 0x0f, 0x39, 0x00, 0xc5, - 0x0b, 0xc4, 0x0a, 0xa1, 0x0a, 0xd5, 0x1b, 0xf0, 0x1f, 0xf8, 0x1e, 0xda, - 0x1b, 0xb2, 0x0f, 0x39, 0x0f, 0xc4, 0x06, 0xfd, 0x00, 0x61, 0x0b, 0x14, - 0x00, 0x00, 0x1f, 0xc4, 0x06, 0xfc, 0x1f, 0x38, 0x10, 0x00, 0x0f, 0xc4, - 0x06, 0xfb, 0x0b, 0x15, 0x0f, 0xc4, 0x06, 0xfc, 0x1b, 0x15, 0x1f, 0x39, - 0x00, 0x40, 0x0f, 0xe2, 0x0a, 0xaa, 0x00, 0x04, 0x0f, 0xc5, 0x07, 0xfd, - 0x0b, 0x63, 0x09, 0x82, 0x0f, 0xc5, 0x07, 0xfe, 0x0b, 0xe3, 0x09, 0x82, - 0x0b, 0xe3, 0x0f, 0xfa, 0x1e, 0x8f, 0x09, 0x82, 0x0f, 0xf0, 0x0f, 0x19, - 0x00, 0x32, 0x0f, 0xf0, 0x0f, 0x18, 0x00, 0x32, 0x0f, 0xf0, 0x0f, 0x1a, - 0x00, 0x72, 0x0f, 0xf9, 0x1f, 0x09, 0x0f, 0xf1, 0x0f, 0x14, 0x0f, 0xc4, - 0x01, 0x3e, 0x0a, 0x48, 0x0c, 0x02, 0x0f, 0xf1, 0x0f, 0x15, 0x0a, 0x42, - 0x0c, 0x21, 0x00, 0x1f, 0x0f, 0xc4, 0x07, 0xf0, 0x00, 0x00, 0x10, 0x40, - 0x00, 0x09, 0x00, 0x4a, 0x00, 0x5f, 0x10, 0x49, 0x10, 0x0a, 0x00, 0x9e, - 0x10, 0x49, 0x10, 0x4a, 0x0f, 0xc4, 0x07, 0xea, 0x0c, 0x40, 0x0f, 0xc4, - 0x07, 0xeb, 0x0c, 0x80, 0x0f, 0xe2, 0x02, 0xa0, 0x0f, 0xc4, 0x01, 0x7b, - 0x0c, 0x24, 0x09, 0x80, 0x0f, 0xc4, 0x01, 0x7a, 0x0c, 0x26, 0x09, 0xa4, - 0x0f, 0x38, 0x09, 0x80, 0x0f, 0xf9, 0x19, 0xf7, 0x0f, 0xf9, 0x1f, 0x42, - 0x0f, 0xf9, 0x1f, 0x46, 0x0f, 0xf9, 0x1f, 0x4a, 0x0f, 0xf9, 0x1f, 0x4e, - 0x0f, 0xf9, 0x1f, 0x52, 0x0f, 0xf9, 0x1f, 0x5a, 0x0f, 0xc4, 0x00, 0x83, - 0x00, 0x37, 0x0f, 0xb9, 0x0f, 0xc4, 0x00, 0xe0, 0x00, 0x37, 0x0f, 0xb9, - 0x0f, 0xc4, 0x00, 0xe3, 0x00, 0x37, 0x0f, 0xb9, 0x0f, 0xc4, 0x00, 0x8c, - 0x00, 0x37, 0x0f, 0xb9, 0x0f, 0xbb, 0x0f, 0xc4, 0x01, 0x71, 0x0b, 0x14, - 0x1f, 0x39, 0x0c, 0x51, 0x0f, 0x38, 0x09, 0xc9, 0x0f, 0xfb, 0x08, 0xc2, - 0x0f, 0xb9, 0x0f, 0xbf, 0x00, 0xf3, 0x0f, 0xfc, 0x1f, 0x34, 0x0f, 0xfc, - 0x0e, 0xe2, 0x0f, 0xfc, 0x09, 0x7e, 0x0f, 0xfc, 0x09, 0x84, 0x0f, 0xfc, - 0x09, 0x87, 0x0f, 0xfc, 0x0c, 0x9f, 0x0f, 0xfc, 0x0b, 0x5b, 0x0f, 0xfc, - 0x0a, 0x72, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0x77, 0x0f, 0xff, - 0x00, 0x26, 0x0f, 0xff, 0x00, 0x27, 0x0f, 0xff, 0x00, 0x28, 0x0f, 0xff, - 0x00, 0x29, 0x0f, 0xff, 0x00, 0x2a, 0x0f, 0xff, 0x02, 0x00, 0x0f, 0xff, - 0x02, 0x05, 0x0f, 0xff, 0x0c, 0xad, 0x0f, 0xff, 0x0a, 0xb6, 0x0f, 0xc4, - 0x06, 0xfd, 0x00, 0x00, 0x00, 0x04, 0x0f, 0xe2, 0x0a, 0xaa, 0x0f, 0xc5, - 0x07, 0xfd, 0x0b, 0xa3, 0x09, 0xa1, 0x0b, 0xd4, 0x1f, 0xc5, 0x07, 0xfe, - 0x1b, 0xa3, 0x19, 0xa1, 0x1b, 0xd4, 0x1b, 0xa3, 0x19, 0xa1, 0x1b, 0xd4, - 0x1f, 0xc4, 0x06, 0xfc, 0x1b, 0x21, 0x10, 0x54, 0x1f, 0xf9, 0x1f, 0xab, - 0x0f, 0xfb, 0x1b, 0x7f, 0x0f, 0xfb, 0x18, 0x51, 0x0f, 0xfb, 0x02, 0xe3, - 0x0f, 0xfb, 0x1b, 0xb6, 0x0f, 0xf0, 0x0f, 0x00, 0x0f, 0xf8, 0x1f, 0xb7, - 0x01, 0x72, 0x0f, 0xc4, 0x06, 0xfd, 0x0f, 0xfa, 0x1e, 0xd7, 0x00, 0x40, - 0x0f, 0xc4, 0x07, 0xf3, 0x0f, 0xfa, 0x1c, 0xa7, 0x00, 0x00, 0x0f, 0xf9, - 0x1f, 0xb7, 0x0f, 0xfb, 0x01, 0xcf, 0x0f, 0xf9, 0x1f, 0xc2, 0x03, 0x3a, - 0x00, 0xc8, 0x10, 0x55, 0x10, 0x95, 0x12, 0x15, 0x1f, 0xfb, 0x02, 0x35, - 0x0f, 0xfb, 0x01, 0xcb, 0x0f, 0xfb, 0x17, 0x17, 0x0f, 0xfb, 0x01, 0xcf, - 0x0f, 0xf8, 0x1f, 0xbb, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x07, 0x06 -}; - -#endif diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c new file mode 100644 index 000000000000..67dc53f53391 --- /dev/null +++ b/drivers/media/dvb/frontends/stv0297.c @@ -0,0 +1,783 @@ +/* + Driver for STV0297 demodulator + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> + +#include "dvb_frontend.h" +#include "stv0297.h" + +struct stv0297_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct stv0297_config* config; + + struct dvb_frontend frontend; + + int freq_off; + + unsigned long base_freq; + + u8 pwm; +}; + +#if 1 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define STV0297_CLOCK_KHZ 28900 + +static u8 init_tab [] = { + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0xff, + 0x4b, 0x7f, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x00, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x49, + 0x62, 0x0b, + 0x53, 0x08, + 0x59, 0x08, +}; + + +static int stv0297_writereg (struct stv0297_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " + "ret == %i)\n", __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int stv0297_readreg (struct stv0297_state* state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + // this device needs a STOP between the register and data + if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); + return -1; + } + if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); + return -1; + } + + return b1[0]; +} + +static int stv0297_writereg_mask (struct stv0297_state* state, u8 reg, u8 mask, u8 data) +{ + int val; + + val = stv0297_readreg(state, reg); + val &= ~mask; + val |= (data & mask); + stv0297_writereg(state, reg, val); + + return 0; +} + +static int stv0297_readregs (struct stv0297_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + // this device needs a STOP between the register and data + if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); + return -1; + } + if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); + return -1; + } + + return 0; +} + +static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate) +{ + long tmp; + + tmp = 131072L * srate; /* 131072 = 2^17 */ + tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */ + tmp = tmp * 8192L; /* 8192 = 2^13 */ + + stv0297_writereg (state, 0x55,(unsigned char)(tmp & 0xFF)); + stv0297_writereg (state, 0x56,(unsigned char)(tmp>> 8)); + stv0297_writereg (state, 0x57,(unsigned char)(tmp>>16)); + stv0297_writereg (state, 0x58,(unsigned char)(tmp>>24)); +} + +static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate) +{ + long tmp; + + tmp = (long) fshift *262144L; /* 262144 = 2*18 */ + tmp /= symrate; + tmp *= 1024; /* 1024 = 2*10 */ + + // adjust + if (tmp >= 0) { + tmp += 500000; + } else { + tmp -= 500000; + } + tmp /= 1000000; + + stv0297_writereg(state, 0x60, tmp & 0xFF); + stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); +} + +static void stv0297_set_carrieroffset(struct stv0297_state* state, long offset) +{ + long tmp; + + /* symrate is hardcoded to 10000 */ + tmp = offset * 26844L; /* (2**28)/10000 */ + if (tmp < 0) + tmp += 0x10000000; + tmp &= 0x0FFFFFFF; + + stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF)); + stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8)); + stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16)); + stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f); +} + +static long stv0297_get_carrieroffset(struct stv0297_state* state) +{ + s32 raw; + long tmp; + + stv0297_writereg(state,0x6B, 0x00); + + raw = stv0297_readreg(state,0x66); + raw |= (stv0297_readreg(state,0x67) << 8); + raw |= (stv0297_readreg(state,0x68) << 16); + raw |= (stv0297_readreg(state,0x69) & 0x0F) << 24; + + tmp = raw; + tmp /= 26844L; + + return tmp; +} + +static void stv0297_set_initialdemodfreq(struct stv0297_state* state, long freq) +{ +/* + s64 tmp; + + if (freq > 10000) freq -= STV0297_CLOCK_KHZ; + + tmp = freq << 16; + do_div(tmp, STV0297_CLOCK_KHZ); + if (tmp > 0xffff) tmp = 0xffff; // check this calculation + + stv0297_writereg_mask(state, 0x25, 0x80, 0x80); + stv0297_writereg(state, 0x21, tmp >> 8); + stv0297_writereg(state, 0x20, tmp); +*/ +} + +static int stv0297_set_qam(struct stv0297_state* state, fe_modulation_t modulation) +{ + int val = 0; + + switch(modulation) { + case QAM_16: + val = 0; + break; + + case QAM_32: + val = 1; + break; + + case QAM_64: + val = 4; + break; + + case QAM_128: + val = 2; + break; + + case QAM_256: + val = 3; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x00, 0x70, val << 4); + + return 0; +} + +static int stv0297_set_inversion(struct stv0297_state* state, fe_spectral_inversion_t inversion) +{ + int val = 0; + + switch(inversion) { + case INVERSION_OFF: + val = 0; + break; + + case INVERSION_ON: + val = 1; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x83, 0x08, val << 3); + + return 0; +} + + + + + + + + + + + + + +int stv0297_enable_plli2c(struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + stv0297_writereg(state, 0x87, 0x78); + stv0297_writereg(state, 0x86, 0xc8); + + return 0; +} + +static int stv0297_init (struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int i; + + /* soft reset */ + stv0297_writereg_mask(state, 0x80, 1, 1); + stv0297_writereg_mask(state, 0x80, 1, 0); + + /* reset deinterleaver */ + stv0297_writereg_mask(state, 0x81, 1, 1); + stv0297_writereg_mask(state, 0x81, 1, 0); + + /* load init table */ + for (i=0; i<sizeof(init_tab); i+=2) { + stv0297_writereg (state, init_tab[i], init_tab[i+1]); + } + + /* set a dummy symbol rate */ + stv0297_set_symbolrate(state, 6900); + + /* invert AGC1 polarity */ + stv0297_writereg_mask(state, 0x88, 0x10, 0x10); + + /* setup bit error counting */ + stv0297_writereg_mask(state, 0xA0, 0x80, 0x00); + stv0297_writereg_mask(state, 0xA0, 0x10, 0x00); + stv0297_writereg_mask(state, 0xA0, 0x08, 0x00); + stv0297_writereg_mask(state, 0xA0, 0x07, 0x04); + + /* min + max PWM */ + stv0297_writereg(state, 0x4a, 0x00); + stv0297_writereg(state, 0x4b, state->pwm); + msleep(200); + + if (state->config->pll_init) + state->config->pll_init(fe); + + return 0; +} + +static int stv0297_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + u8 sync = stv0297_readreg (state, 0xDF); + + *status = 0; + if (sync & 0x80) + *status |= + FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + return 0; +} + +static int stv0297_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 BER[3]; + + stv0297_writereg (state, 0xA0, 0x80); // Start Counting bit errors for 4096 Bytes + mdelay(25); // Hopefully got 4096 Bytes + stv0297_readregs (state, 0xA0, BER, 3); + mdelay(25); + *ber = (BER[2] << 8 | BER[1]) / ( 8 * 4096); + + return 0; +} + + +static int stv0297_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 STRENGTH[2]; + + stv0297_readregs (state, 0x41, STRENGTH, 2); + *strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; + + return 0; +} + +static int stv0297_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 SNR[2]; + + stv0297_readregs (state, 0x07, SNR, 2); + *snr = SNR[1] << 8 | SNR[0]; + + return 0; +} + +static int stv0297_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + *ucblocks = (stv0297_readreg (state, 0xD5) << 8) + | stv0297_readreg (state, 0xD4); + + return 0; +} + +static int stv0297_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int u_threshold; + int initial_u; + int blind_u; + int delay; + int sweeprate; + int carrieroffset; + unsigned long starttime; + unsigned long timeout; + + switch(p->u.qam.modulation) { + case QAM_16: + case QAM_32: + case QAM_64: + delay = 100; + sweeprate = 1500; + break; + + case QAM_128: + delay = 150; + sweeprate = 1000; + break; + + case QAM_256: + delay = 200; + sweeprate = 500; + break; + + default: + return -EINVAL; + } + + // determine inversion dependant parameters + carrieroffset = -330; + switch(p->inversion) { + case INVERSION_OFF: + break; + + case INVERSION_ON: + sweeprate = -sweeprate; + carrieroffset = -carrieroffset; + break; + + default: + return -EINVAL; + } + + state->config->pll_set(fe, p); + + /* clear software interrupts */ + stv0297_writereg(state, 0x82, 0x0); + + /* set initial demodulation frequency */ + stv0297_set_initialdemodfreq(state, state->freq_off + 7250); + + /* setup AGC */ + stv0297_writereg_mask(state, 0x43, 0x10, 0x00); + stv0297_writereg(state, 0x41, 0x00); + stv0297_writereg_mask(state, 0x42, 0x03, 0x01); + stv0297_writereg_mask(state, 0x36, 0x60, 0x00); + stv0297_writereg_mask(state, 0x36, 0x18, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x80); + stv0297_writereg(state, 0x72, 0x00); + stv0297_writereg(state, 0x73, 0x00); + stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); + stv0297_writereg_mask(state, 0x43, 0x08, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x00); + + /* setup STL */ + stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); + stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); + stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); + + /* disable frequency sweep */ + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + + /* reset deinterleaver */ + stv0297_writereg_mask(state, 0x81, 0x01, 0x01); + stv0297_writereg_mask(state, 0x81, 0x01, 0x00); + + /* ??? */ + stv0297_writereg_mask(state, 0x83, 0x20, 0x20); + stv0297_writereg_mask(state, 0x83, 0x20, 0x00); + + /* reset equaliser */ + u_threshold = stv0297_readreg(state, 0x00) & 0xf; + initial_u = stv0297_readreg(state, 0x01) >> 4; + blind_u = stv0297_readreg(state, 0x01) & 0xf; + stv0297_writereg_mask(state, 0x84, 0x01, 0x01); + stv0297_writereg_mask(state, 0x84, 0x01, 0x00); + stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); + stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); + stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); + + /* data comes from internal A/D */ + stv0297_writereg_mask(state, 0x87, 0x80, 0x00); + + /* clear phase registers */ + stv0297_writereg(state, 0x63, 0x00); + stv0297_writereg(state, 0x64, 0x00); + stv0297_writereg(state, 0x65, 0x00); + stv0297_writereg(state, 0x66, 0x00); + stv0297_writereg(state, 0x67, 0x00); + stv0297_writereg(state, 0x68, 0x00); + stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); + + /* set parameters */ + stv0297_set_qam(state, p->u.qam.modulation); + stv0297_set_symbolrate(state, p->u.qam.symbol_rate/1000); + stv0297_set_sweeprate(state, sweeprate, p->u.qam.symbol_rate / 1000); + stv0297_set_carrieroffset(state, carrieroffset); + stv0297_set_inversion(state, p->inversion); + + /* kick off lock */ + stv0297_writereg_mask(state, 0x88, 0x08, 0x08); + stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); + stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); + stv0297_writereg_mask(state, 0x43, 0x40, 0x40); + stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); + stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); + stv0297_writereg_mask(state, 0x03, 0x03, 0x03); + stv0297_writereg_mask(state, 0x43, 0x10, 0x10); + + /* wait for WGAGC lock */ + starttime = jiffies; + timeout = jiffies + (200*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + if (stv0297_readreg(state, 0x43) & 0x08) + break; + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(20); + + /* wait for equaliser partial convergence */ + timeout = jiffies + (50*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x04) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + + /* wait for equaliser full convergence */ + timeout = jiffies + (delay*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x08) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + + /* disable sweep */ + stv0297_writereg_mask(state, 0x6a, 1, 0); + stv0297_writereg_mask(state, 0x88, 8, 0); + + /* wait for main lock */ + timeout = jiffies + (20*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0xDF) & 0x80) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(100); + + /* is it still locked after that delay? */ + if (!(stv0297_readreg(state, 0xDF) & 0x80)) { + goto timeout; + } + + /* success!! */ + stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); + state->freq_off = stv0297_get_carrieroffset(state); + state->base_freq = p->frequency; + return 0; + +timeout: + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + return 0; +} + +static int stv0297_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int reg_00, reg_83; + + reg_00 = stv0297_readreg(state, 0x00); + reg_83 = stv0297_readreg(state, 0x83); + + p->frequency = state->base_freq + state->freq_off; + p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; + p->u.qam.symbol_rate = 0; + p->u.qam.fec_inner = 0; + + switch((reg_00 >> 4) & 0x7) { + case 0: + p->u.qam.modulation = QAM_16; + break; + case 1: + p->u.qam.modulation = QAM_32; + break; + case 2: + p->u.qam.modulation = QAM_128; + break; + case 3: + p->u.qam.modulation = QAM_256; + break; + case 4: + p->u.qam.modulation = QAM_64; + break; + } + + return 0; +} + +static void stv0297_release(struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops stv0297_ops; + +struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter *i2c, int pwm) +{ + struct stv0297_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct stv0297_state*) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); + state->freq_off = 0; + state->base_freq = 0; + state->pwm = pwm; + + /* check if the demod is there */ + if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) + goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops stv0297_ops = { + + .info = { + .name = "ST STV0297 DVB-C", + .type = FE_QAM, + .frequency_min = 64000000, + .frequency_max = 1300000000, + .frequency_stepsize = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO}, + + .release = stv0297_release, + + .init = stv0297_init, + + .set_frontend = stv0297_set_frontend, + .get_frontend = stv0297_get_frontend, + + .read_status = stv0297_read_status, + .read_ber = stv0297_read_ber, + .read_signal_strength = stv0297_read_signal_strength, + .read_snr = stv0297_read_snr, + .read_ucblocks = stv0297_read_ucblocks, +}; + +MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); +MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(stv0297_attach); +EXPORT_SYMBOL(stv0297_enable_plli2c); diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h new file mode 100644 index 000000000000..355aa872086d --- /dev/null +++ b/drivers/media/dvb/frontends/stv0297.h @@ -0,0 +1,41 @@ +/* + Driver for STV0297 demodulator + + Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef STV0297_H +#define STV0297_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +struct stv0297_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c, int pwm); +extern int stv0297_enable_plli2c(struct dvb_frontend* fe); + +#endif // STV0297_H diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index c5db89da1d94..bf5de103142f 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -1,8 +1,5 @@ /* - Universal driver for STV0299/TDA5059/SL1935 based - DVB QPSK frontends - - Alps BSRU6, LG TDQB-S00x + Driver for ST STV0299 demodulator Copyright (C) 2001-2002 Convergence Integrated Media GmbH <ralph@convergence.de>, @@ -54,236 +51,42 @@ #include <asm/div64.h> #include "dvb_frontend.h" +#include "stv0299.h" -#define FRONTEND_NAME "dvbfe_stv0299" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; -static int stv0299_status; +struct stv0299_state { -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -module_param(stv0299_status, int, 0444); -MODULE_PARM_DESC(stv0299_status, "Which status value to support " - "(0 == BER (default), 1 == UCBLOCKS)"); + struct i2c_adapter* i2c; -#define STATUS_BER 0 -#define STATUS_UCBLOCKS 1 + struct dvb_frontend_ops ops; + const struct stv0299_config* config; -/* frontend types */ -#define UNKNOWN_FRONTEND -1 -#define PHILIPS_SU1278_TSA 0 // SU1278 with TSA5059 synth and datasheet recommended settings -#define ALPS_BSRU6 1 -#define LG_TDQF_S001F 2 -#define PHILIPS_SU1278_TUA 3 // SU1278 with TUA6100 synth -#define SAMSUNG_TBMU24112IMB 4 -#define PHILIPS_SU1278_TSA_TT 5 // SU1278 with TSA5059 synth and TechnoTrend settings -#define PHILIPS_SU1278_TSA_TY 6 // SU1278 with TUA5059 synth and Typhoon wiring -#define PHILIPS_SU1278_TSA_CI 7 // SU1278 with TUA5059 synth and TerraTec Cinergy wiring - -/* Master Clock = 88 MHz */ -#define M_CLK (88000000UL) - -/* Master Clock for TT cards = 64 MHz */ -#define M_CLK_SU1278_TSA_TT (64000000UL) - -static struct dvb_frontend_info uni0299_info = { - .name = "STV0299/TSA5059/SL1935 based", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = M_CLK/2000, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .symbol_rate_tolerance = 500, /* ppm */ - .notifier_delay = 0, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_QPSK | - FE_CAN_FEC_AUTO -}; + struct dvb_frontend frontend; - -struct stv0299_state { - u8 tuner_type; u8 initialised:1; u32 tuner_frequency; u32 symbol_rate; fe_code_rate_t fec_inner; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - - -static u8 init_tab [] = { - 0x04, 0x7d, /* F22FR = 0x7d */ - /* F22 = f_VCO / 128 / 0x7d = 22 kHz */ - - /* I2C bus repeater */ - 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ - - /* general purpose DAC registers */ - 0x06, 0x40, /* DAC not used, set to high impendance mode */ - 0x07, 0x00, /* DAC LSB */ - - /* DiSEqC registers */ - 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ - 0x09, 0x00, /* FIFO */ - - /* Input/Output configuration register */ - 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ - /* OP0 ctl = Normal, OP0 val = 1 (18 V) */ - /* Nyquist filter = 00, QPSK reverse = 0 */ - - /* AGC1 control register */ - 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ - - /* Timing loop register */ - 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ - - 0x10, 0x3f, // AGC2 0x3d - - 0x11, 0x84, - 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on - - 0x15, 0xc9, // lock detector threshold - - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - - 0x1f, 0x50, - - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - - 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 - - 0x29, 0x1e, // 1/2 threshold - 0x2a, 0x14, // 2/3 threshold - 0x2b, 0x0f, // 3/4 threshold - 0x2c, 0x09, // 5/6 threshold - 0x2d, 0x05, // 7/8 threshold - 0x2e, 0x01, - - 0x31, 0x1f, // test all FECs - - 0x32, 0x19, // viterbi and synchro search - 0x33, 0xfc, // rs control - 0x34, 0x93, // error control -}; - - -static u8 init_tab_samsung [] = { - 0x01, 0x15, - 0x02, 0x00, - 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 + int errmode; }; +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 -static u8 init_tab_su1278_tsa_tt [] = { - 0x01, 0x0f, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x5b, - 0x05, 0x85, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0x02, - 0x09, 0x00, - 0x0C, 0x01, - 0x0D, 0x81, - 0x0E, 0x44, - 0x0f, 0x14, - 0x10, 0x3c, - 0x11, 0x84, - 0x12, 0xda, - 0x13, 0x97, - 0x14, 0x95, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x13 -}; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "stv0299: " args); \ + } while (0) -static int stv0299_set_FEC (struct i2c_adapter *i2c, fe_code_rate_t fec); -static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type); -static int stv0299_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) +static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) { int ret; u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x68, .flags = 0, .buf = buf, .len = 2 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer (i2c, &msg, 1); + ret = i2c_transfer (state->i2c, &msg, 1); if (ret != 1) dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " @@ -292,16 +95,23 @@ static int stv0299_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } +int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + return stv0299_writeregI(state, reg, data); +} + -static u8 stv0299_readreg (struct i2c_adapter *i2c, u8 reg) +static u8 stv0299_readreg (struct stv0299_state* state, u8 reg) { int ret; u8 b0 [] = { reg }; u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x68, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", @@ -311,13 +121,13 @@ static u8 stv0299_readreg (struct i2c_adapter *i2c, u8 reg) } -static int stv0299_readregs (struct i2c_adapter *i2c, u8 reg1, u8 *b, u8 len) +static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len) { int ret; - struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = ®1, .len = 1 }, - { .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -326,373 +136,44 @@ static int stv0299_readregs (struct i2c_adapter *i2c, u8 reg1, u8 *b, u8 len) } -static int pll_write (struct i2c_adapter *i2c, u8 addr, u8 *data, int len) -{ - int ret; - struct i2c_msg msg = { .addr = addr, .buf = data, .len = len }; - - - stv0299_writereg(i2c, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - - ret = i2c_transfer (i2c, &msg, 1); - - stv0299_writereg(i2c, 0x05, 0x35); /* disable i2c repeater on stv0299 */ - - if (ret != 1) - dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - -static int sl1935_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype) -{ - u8 buf[4]; - u32 div; - - div = freq / 125; - - dprintk("%s : freq = %i, div = %i\n", __FUNCTION__, freq, div); - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x84; // 0xC4 - buf[3] = 0x08; - - if (freq < 1500000) buf[3] |= 0x10; - - return pll_write (i2c, 0x61, buf, sizeof(buf)); -} - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int tsa5059_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype, int srate) -{ - u8 addr; - u32 div; - u8 buf[4]; - int divisor, regcode; - - dprintk ("%s: freq %i, ftype %i\n", __FUNCTION__, freq, ftype); - - if ((freq < 950000) || (freq > 2150000)) return -EINVAL; - - if (ftype == PHILIPS_SU1278_TSA_TT) { - divisor = 500; - regcode = 2; - } else { - divisor = 125; - regcode = 4; - } - - // setup frequency divisor - div = (freq + (divisor - 1)) / divisor; // round correctly - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | regcode; - buf[3] = 0; - - // tuner-specific settings - switch(ftype) { - case PHILIPS_SU1278_TSA: - case PHILIPS_SU1278_TSA_TT: - case PHILIPS_SU1278_TSA_TY: - case PHILIPS_SU1278_TSA_CI: - if (ftype == PHILIPS_SU1278_TSA_TY || ftype == PHILIPS_SU1278_TSA_CI) - addr = 0x61; - else - addr = 0x60; - - buf[3] |= 0x20; - - if (srate < 4000000) buf[3] |= 1; - - if (freq < 1250000) buf[3] |= 0; - else if (freq < 1550000) buf[3] |= 0x40; - else if (freq < 2050000) buf[3] |= 0x80; - else if (freq < 2150000) buf[3] |= 0xC0; - break; - - case ALPS_BSRU6: - addr = 0x61; - buf[3] = 0xC4; - if (freq > 1530000) buf[3] = 0xc0; - break; - - default: - return -EINVAL; - } - - return pll_write (i2c, addr, buf, sizeof(buf)); -} - - -#define MIN2(a,b) ((a) < (b) ? (a) : (b)) -#define MIN3(a,b,c) MIN2(MIN2(a,b),c) - -static int tua6100_set_tv_freq (struct i2c_adapter *i2c, u32 freq, - int ftype, int srate) -{ - u8 reg0 [2] = { 0x00, 0x00 }; - u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 }; - u8 reg2 [3] = { 0x02, 0x00, 0x00 }; - int _fband; - int first_ZF; - int R, A, N, P, M; - int err; - - first_ZF = (freq) / 1000; - - if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) < - abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890)))) - _fband = 2; - else - _fband = 3; - - if (_fband == 2) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1430) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1430)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - } - - if(_fband == 3) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1455) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1420)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - else if ((first_ZF >= 1420) && (first_ZF < 1455)) - reg0[1] = 0x0F; -} - - if (first_ZF > 1525) - reg1[1] |= 0x80; - else - reg1[1] &= 0x7F; - - if (_fband == 2) { - if (first_ZF > 1430) { /* 1430MHZ */ - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } -} - - if (_fband == 3) { - if ((first_ZF >= 1455) && - (first_ZF < 1630)) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - } else { - if (first_ZF < 1455) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - if (first_ZF >= 1630) { - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } - } - } - } - - /* set ports, enable P0 for symbol rates > 4Ms/s */ - if (srate >= 4000000) - reg1[1] |= 0x0c; - else - reg1[1] |= 0x04; - - reg2[1] |= 0x0c; - - R = 64; - A = 64; - P = 64; //32 - - M = (freq * R) / 4; /* in Mhz */ - N = (M - A * 1000) / (P * 1000); - - reg1[1] |= (N >> 9) & 0x03; - reg1[2] = (N >> 1) & 0xff; - reg1[3] = (N << 7) & 0x80; - - reg2[1] |= (R >> 8) & 0x03; - reg2[2] = R & 0xFF; /* R */ - - reg1[3] |= A & 0x7f; /* A */ - - if (P == 64) - reg1[1] |= 0x40; /* Prescaler 64/65 */ - - reg0[1] |= 0x03; - - if ((err = pll_write(i2c, 0x60, reg0, sizeof(reg0)))) - return err; - - if ((err = pll_write(i2c, 0x60, reg1, sizeof(reg1)))) - return err; - - if ((err = pll_write(i2c, 0x60, reg2, sizeof(reg2)))) - return err; - - return 0; -} - - -static int pll_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype, int srate) -{ - switch(ftype) { - case SAMSUNG_TBMU24112IMB: - return sl1935_set_tv_freq(i2c, freq, ftype); - - case LG_TDQF_S001F: - return sl1935_set_tv_freq(i2c, freq, ftype); - - case PHILIPS_SU1278_TUA: - return tua6100_set_tv_freq(i2c, freq, ftype, srate); - - default: - return tsa5059_set_tv_freq(i2c, freq, ftype, srate); -} -} - -#if 0 -static int tsa5059_read_status (struct i2c_adapter *i2c) -{ - int ret; - u8 rpt1 [] = { 0x05, 0xb5 }; - u8 stat [] = { 0 }; - - struct i2c_msg msg [] = {{ .addr = 0x68, .flags = 0, .buf = rpt1, .len = 2 }, - { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }}; - - dprintk ("%s\n", __FUNCTION__); - - ret = i2c_transfer (i2c, msg, 2); - - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return stat[0]; -} -#endif - - -static int stv0299_init (struct i2c_adapter *i2c, int ftype) -{ - int i; - - dprintk("stv0299: init chip\n"); - - switch(ftype) { - case SAMSUNG_TBMU24112IMB: - dprintk("%s: init stv0299 chip for Samsung TBMU24112IMB\n", __FUNCTION__); - - for (i=0; i<sizeof(init_tab_samsung); i+=2) - { - dprintk("%s: reg == 0x%02x, val == 0x%02x\n", __FUNCTION__, init_tab_samsung[i], init_tab_samsung[i+1]); - - stv0299_writereg (i2c, init_tab_samsung[i], init_tab_samsung[i+1]); - } - break; - - case PHILIPS_SU1278_TSA_TT: - for (i=0; i<sizeof(init_tab_su1278_tsa_tt); i+=2) { - stv0299_writereg (i2c, init_tab_su1278_tsa_tt[i], init_tab_su1278_tsa_tt[i+1]); - } - break; - - default: - stv0299_writereg (i2c, 0x01, 0x15); - stv0299_writereg (i2c, 0x02, ftype == PHILIPS_SU1278_TUA ? 0x00 : 0x30); - stv0299_writereg (i2c, 0x03, 0x00); - - for (i=0; i<sizeof(init_tab); i+=2) - stv0299_writereg (i2c, init_tab[i], init_tab[i+1]); - - /* AGC1 reference register setup */ - if (ftype == PHILIPS_SU1278_TSA || ftype == PHILIPS_SU1278_TSA_TY || ftype == PHILIPS_SU1278_TSA_CI) - stv0299_writereg (i2c, 0x0f, 0x92); /* Iagc = Inverse, m1 = 18 */ - else if (ftype == PHILIPS_SU1278_TUA) - stv0299_writereg (i2c, 0x0f, 0x94); /* Iagc = Inverse, m1 = 20 */ - else - stv0299_writereg (i2c, 0x0f, 0x52); /* Iagc = Normal, m1 = 18 */ - break; - } - - switch(stv0299_status) { - case STATUS_BER: - stv0299_writereg(i2c, 0x34, 0x93); - break; - - case STATUS_UCBLOCKS: - stv0299_writereg(i2c, 0x34, 0xB3); - break; - } - - return 0; -} - - -static int stv0299_set_FEC (struct i2c_adapter *i2c, fe_code_rate_t fec) +static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) { dprintk ("%s\n", __FUNCTION__); switch (fec) { case FEC_AUTO: { - dprintk ("%s : FEC_AUTO\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x1f); + return stv0299_writeregI (state, 0x31, 0x1f); } case FEC_1_2: { - dprintk ("%s : FEC_1_2\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x01); + return stv0299_writeregI (state, 0x31, 0x01); } case FEC_2_3: { - dprintk ("%s : FEC_2_3\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x02); + return stv0299_writeregI (state, 0x31, 0x02); } case FEC_3_4: { - dprintk ("%s : FEC_3_4\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x04); + return stv0299_writeregI (state, 0x31, 0x04); } case FEC_5_6: { - dprintk ("%s : FEC_5_6\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x08); + return stv0299_writeregI (state, 0x31, 0x08); } case FEC_7_8: { - dprintk ("%s : FEC_7_8\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x31, 0x10); + return stv0299_writeregI (state, 0x31, 0x10); } default: { - dprintk ("%s : FEC invalid\n", __FUNCTION__); return -EINVAL; } } } -static fe_code_rate_t stv0299_get_fec (struct i2c_adapter *i2c) +static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state) { static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_1_2 }; @@ -700,7 +181,7 @@ static fe_code_rate_t stv0299_get_fec (struct i2c_adapter *i2c) dprintk ("%s\n", __FUNCTION__); - index = stv0299_readreg (i2c, 0x1b); + index = stv0299_readreg (state, 0x1b); index &= 0x7; if (index > 4) @@ -710,13 +191,13 @@ static fe_code_rate_t stv0299_get_fec (struct i2c_adapter *i2c) } -static int stv0299_wait_diseqc_fifo (struct i2c_adapter *i2c, int timeout) +static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) { unsigned long start = jiffies; dprintk ("%s\n", __FUNCTION__); - while (stv0299_readreg(i2c, 0x0a) & 1) { + while (stv0299_readreg(state, 0x0a) & 1) { if (jiffies - start > timeout) { dprintk ("%s: timeout!!\n", __FUNCTION__); return -ETIMEDOUT; @@ -728,13 +209,13 @@ static int stv0299_wait_diseqc_fifo (struct i2c_adapter *i2c, int timeout) } -static int stv0299_wait_diseqc_idle (struct i2c_adapter *i2c, int timeout) +static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) { unsigned long start = jiffies; dprintk ("%s\n", __FUNCTION__); - while ((stv0299_readreg(i2c, 0x0a) & 3) != 2 ) { + while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { if (jiffies - start > timeout) { dprintk ("%s: timeout!!\n", __FUNCTION__); return -ETIMEDOUT; @@ -745,101 +226,156 @@ static int stv0299_wait_diseqc_idle (struct i2c_adapter *i2c, int timeout) return 0; } +static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + u64 big = srate; + u32 ratio; + + // check rate is within limits + if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + + // calculate value to program + big = big << 20; + big += (state->config->mclk-1); // round correctly + do_div(big, state->config->mclk); + ratio = big << 4; + + return state->config->set_symbol_rate(fe, srate, ratio); +} + + +static int stv0299_get_symbolrate (struct stv0299_state* state) +{ + u32 Mclk = state->config->mclk / 4096L; + u32 srate; + s32 offset; + u8 sfr[3]; + s8 rtf; + + dprintk ("%s\n", __FUNCTION__); + + stv0299_readregs (state, 0x1f, sfr, 3); + stv0299_readregs (state, 0x1a, &rtf, 1); + + srate = (sfr[0] << 8) | sfr[1]; + srate *= Mclk; + srate /= 16; + srate += (sfr[2] >> 4) * Mclk / 256; + offset = (s32) rtf * (srate / 4096L); + offset /= 128; + + dprintk ("%s : srate = %i\n", __FUNCTION__, srate); + dprintk ("%s : ofset = %i\n", __FUNCTION__, offset); + + srate += offset; + + srate += 1000; + srate /= 2000; + srate *= 2000; + + return srate; +} + + + + + + + -static int stv0299_send_diseqc_msg (struct i2c_adapter *i2c, + + + + + + +static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *m) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; int i; dprintk ("%s\n", __FUNCTION__); - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); - if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ return -EREMOTEIO; for (i=0; i<m->msg_len; i++) { - if (stv0299_wait_diseqc_fifo (i2c, 100) < 0) + if (stv0299_wait_diseqc_fifo (state, 100) < 0) return -ETIMEDOUT; - if (stv0299_writereg (i2c, 0x09, m->msg[i])) + if (stv0299_writeregI (state, 0x09, m->msg[i])) return -EREMOTEIO; } - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; return 0; } -static int stv0299_send_diseqc_burst (struct i2c_adapter *i2c, fe_sec_mini_cmd_t burst) +static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; dprintk ("%s\n", __FUNCTION__); - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); - if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ return -EREMOTEIO; - if (stv0299_writereg (i2c, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) + if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) return -EREMOTEIO; - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - if (stv0299_writereg (i2c, 0x08, val)) + if (stv0299_writeregI (state, 0x08, val)) return -EREMOTEIO; return 0; } -static int stv0299_set_tone (struct i2c_adapter *i2c, fe_sec_tone_mode_t tone) +static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; - dprintk("%s: %s\n", __FUNCTION__, - tone == SEC_TONE_ON ? "SEC_TONE_ON" : - tone == SEC_TONE_OFF ? "SEC_TONE_OFF" : "??"); - - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); switch (tone) { case SEC_TONE_ON: - { - dprintk("%s: TONE_ON\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x08, val | 0x3); - } + return stv0299_writeregI (state, 0x08, val | 0x3); + case SEC_TONE_OFF: - { - dprintk("%s: TONE_OFF\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x08, (val & ~0x3) | 0x02); - } + return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02); + default: - { - dprintk("%s: TONE INVALID\n", __FUNCTION__); return -EINVAL; } - }; } -static int stv0299_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage, - int tuner_type) +static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 reg0x08; u8 reg0x0c; @@ -847,8 +383,8 @@ static int stv0299_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltag voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); - reg0x08 = stv0299_readreg (i2c, 0x08); - reg0x0c = stv0299_readreg (i2c, 0x0c); + reg0x08 = stv0299_readreg (state, 0x08); + reg0x0c = stv0299_readreg (state, 0x0c); /** * H/V switching over OP0, OP1 and OP2 are LNB power enable bits @@ -856,37 +392,28 @@ static int stv0299_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltag reg0x0c &= 0x0f; if (voltage == SEC_VOLTAGE_OFF) { - stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */ - return stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */ + stv0299_writeregI (state, 0x0c, 0x00); /* LNB power off! */ + return stv0299_writeregI (state, 0x08, 0x00); /* LNB power off! */ } - if (tuner_type == PHILIPS_SU1278_TSA_CI) - { - stv0299_writereg (i2c, 0x08, reg0x08 & 0xBF); // switch LNB power on OP2/LOCK pin off - } - else - { - stv0299_writereg (i2c, 0x08, reg0x08 | 0x40); - } + stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); switch (voltage) { case SEC_VOLTAGE_13: - if (tuner_type == PHILIPS_SU1278_TSA_TY || tuner_type == PHILIPS_SU1278_TSA_CI) - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x10); - else - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x40); + if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10; + else reg0x0c |= 0x40; - case SEC_VOLTAGE_18: - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x50); + return stv0299_writeregI(state, 0x0c, reg0x0c); + case SEC_VOLTAGE_18: + return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50); default: return -EINVAL; }; } -static int stv0299_send_legacy_dish_cmd(struct i2c_adapter *i2c, u32 cmd, - int tuner_type) +static int stv0299_send_legacy_dish_cmd(struct dvb_frontend* fe, u32 cmd) { u8 last = 1; int i; @@ -901,15 +428,14 @@ static int stv0299_send_legacy_dish_cmd(struct i2c_adapter *i2c, u32 cmd, cmd = cmd << 1; dprintk("%s switch command: 0x%04x\n",__FUNCTION__, cmd); - stv0299_set_voltage(i2c,SEC_VOLTAGE_18,tuner_type); + stv0299_set_voltage(fe,SEC_VOLTAGE_18); msleep(32); for (i=0; i<9; i++) { if((cmd & 0x01) != last) { - stv0299_set_voltage(i2c, + stv0299_set_voltage(fe, last ? SEC_VOLTAGE_13 : - SEC_VOLTAGE_18, - tuner_type); + SEC_VOLTAGE_18); last = (last) ? 0 : 1; } @@ -922,160 +448,35 @@ static int stv0299_send_legacy_dish_cmd(struct i2c_adapter *i2c, u32 cmd, return 0; } -static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type) -{ - u64 big = srate; - u32 ratio; - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - int Mclk = M_CLK; - - // check rate is within limits - if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; - - // calculate value to program - if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; - big = big << 20; - big += (Mclk-1); // round correctly - do_div(big, Mclk); - ratio = big << 4; - - // program registers - switch(tuner_type) { - case PHILIPS_SU1278_TSA_TT: - stv0299_writereg (i2c, 0x0e, 0x44); - if (srate >= 10000000) { - stv0299_writereg (i2c, 0x13, 0x97); - stv0299_writereg (i2c, 0x14, 0x95); - stv0299_writereg (i2c, 0x15, 0xc9); - stv0299_writereg (i2c, 0x17, 0x8c); - stv0299_writereg (i2c, 0x1a, 0xfe); - stv0299_writereg (i2c, 0x1c, 0x7f); - stv0299_writereg (i2c, 0x2d, 0x09); - } else { - stv0299_writereg (i2c, 0x13, 0x99); - stv0299_writereg (i2c, 0x14, 0x8d); - stv0299_writereg (i2c, 0x15, 0xce); - stv0299_writereg (i2c, 0x17, 0x43); - stv0299_writereg (i2c, 0x1a, 0x1d); - stv0299_writereg (i2c, 0x1c, 0x12); - stv0299_writereg (i2c, 0x2d, 0x05); - } - stv0299_writereg (i2c, 0x0e, 0x23); - stv0299_writereg (i2c, 0x0f, 0x94); - stv0299_writereg (i2c, 0x10, 0x39); - stv0299_writereg (i2c, 0x15, 0xc9); - - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - break; - - case PHILIPS_SU1278_TSA_TY: - case PHILIPS_SU1278_TSA_CI: - case PHILIPS_SU1278_TSA: - aclk = 0xb5; - if (srate < 2000000) bclk = 0x86; - else if (srate < 5000000) bclk = 0x89; - else if (srate < 15000000) bclk = 0x8f; - else if (srate < 45000000) bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) m1 = 0x10; - - stv0299_writereg (i2c, 0x13, aclk); - stv0299_writereg (i2c, 0x14, bclk); - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1); - break; - - case ALPS_BSRU6: - default: - 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 (i2c, 0x13, aclk); - stv0299_writereg (i2c, 0x14, bclk); - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - break; - } - - - return 0; -} - -static int stv0299_get_symbolrate (struct i2c_adapter *i2c, int tuner_type) +static int stv0299_init (struct dvb_frontend* fe) { - u32 Mclk = M_CLK / 4096L; - u32 srate; - s32 offset; - u8 sfr[3]; - s8 rtf; - - dprintk ("%s\n", __FUNCTION__); - - if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT / 4096L; - - stv0299_readregs (i2c, 0x1f, sfr, 3); - stv0299_readregs (i2c, 0x1a, &rtf, 1); - - srate = (sfr[0] << 8) | sfr[1]; - srate *= Mclk; - srate /= 16; - srate += (sfr[2] >> 4) * Mclk / 256; - - offset = (s32) rtf * (srate / 4096L); - offset /= 128; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + int i; - dprintk ("%s : srate = %i\n", __FUNCTION__, srate); - dprintk ("%s : ofset = %i\n", __FUNCTION__, offset); + dprintk("stv0299: init chip\n"); - srate += offset; + for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) + stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]); - srate += 1000; - srate /= 2000; - srate *= 2000; + if (state->config->pll_init) { + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_init(fe); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + } - return srate; + return 0; } -static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct stv0299_state *state = (struct stv0299_state *) fe->data; - struct i2c_adapter *i2c = state->i2c; - - dprintk ("%s\n", __FUNCTION__); - switch (cmd) { - case FE_GET_INFO: +static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) arg; - memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info)); - - if (state->tuner_type == PHILIPS_SU1278_TSA_TT) { - tmp->frequency_tolerance = M_CLK_SU1278_TSA_TT / 2000; - } - break; - } + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; - u8 signal = 0xff - stv0299_readreg (i2c, 0x18); - u8 sync = stv0299_readreg (i2c, 0x1b); + u8 signal = 0xff - stv0299_readreg (state, 0x18); + u8 sync = stv0299_readreg (state, 0x1b); dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __FUNCTION__, sync); - *status = 0; if (signal > 10) @@ -1093,53 +494,61 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if ((sync & 0x98) == 0x98) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: - if (stv0299_status == STATUS_BER) { - *((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8) - | stv0299_readreg (i2c, 0x1e); - } else { - *((u32*) arg) = 0; +static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + if (state->errmode != STATUS_BER) return 0; + *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + + return 0; } - break; - case FE_READ_SIGNAL_STRENGTH: +static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - s32 signal = 0xffff - ((stv0299_readreg (i2c, 0x18) << 8) - | stv0299_readreg (i2c, 0x19)); + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8) + | stv0299_readreg (state, 0x19)); dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __FUNCTION__, - stv0299_readreg (i2c, 0x18), - stv0299_readreg (i2c, 0x19), (int) signal); + stv0299_readreg (state, 0x18), + stv0299_readreg (state, 0x19), (int) signal); signal = signal * 5 / 4; - *((u16*) arg) = (signal > 0xffff) ? 0xffff : - (signal < 0) ? 0 : signal; - break; - } - case FE_READ_SNR: - { - s32 snr = 0xffff - ((stv0299_readreg (i2c, 0x24) << 8) - | stv0299_readreg (i2c, 0x25)); - snr = 3 * (snr - 0xa100); - *((u16*) arg) = (snr > 0xffff) ? 0xffff : - (snr < 0) ? 0 : snr; - break; - } - case FE_READ_UNCORRECTED_BLOCKS: - if (stv0299_status == STATUS_UCBLOCKS) { - *((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8) - | stv0299_readreg (i2c, 0x1e); - } else { - *((u32*) arg) = 0; + *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; + + return 0; } - break; - case FE_SET_FRONTEND: +static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr) { - struct dvb_frontend_parameters *p = arg; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8) + | stv0299_readreg (state, 0x25)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + + return 0; +} + +static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0; + else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + + return 0; +} + +static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; int invval = 0; dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__); @@ -1151,12 +560,10 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) printk("stv0299 does not support auto-inversion\n"); return -EINVAL; } - if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1; - stv0299_writereg(i2c, 0x0c, (stv0299_readreg(i2c, 0x0c) & 0xfe) | invval); + if (state->config->invert) invval = (~invval) & 1; + stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - { + if (state->config->enhanced_tuning) { /* check if we should do a finetune */ int frequency_delta = p->frequency - state->tuner_frequency; int minmax = p->u.qpsk.symbol_rate / 2000; @@ -1165,130 +572,88 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) && (state->fec_inner == p->u.qpsk.fec_inner) && (state->symbol_rate == p->u.qpsk.symbol_rate)) { - int Drot_freq = (frequency_delta << 16) / (M_CLK_SU1278_TSA_TT / 1000); + int Drot_freq = (frequency_delta << 16) / (state->config->mclk / 1000); // zap the derotator registers first - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); // now set them as we want - stv0299_writereg (i2c, 0x22, Drot_freq >> 8); - stv0299_writereg (i2c, 0x23, Drot_freq); + stv0299_writeregI(state, 0x22, Drot_freq >> 8); + stv0299_writeregI(state, 0x23, Drot_freq); } else { /* A "normal" tune is requested */ - pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate); - stv0299_writereg (i2c, 0x32, 0x80); - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); - stv0299_writereg (i2c, 0x32, 0x19); - stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type); - stv0299_set_FEC (i2c, p->u.qpsk.fec_inner); - } - break; + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_set(fe, p); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + + stv0299_writeregI(state, 0x32, 0x80); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); + stv0299_writeregI(state, 0x32, 0x19); + stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); + stv0299_set_FEC (state, p->u.qpsk.fec_inner); } - - default: - pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate); - stv0299_set_FEC (i2c, p->u.qpsk.fec_inner); - stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type); - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); - stv0299_readreg (i2c, 0x23); - stv0299_writereg (i2c, 0x12, 0xb9); - break; + } else { + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_set(fe, p); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + + stv0299_set_FEC (state, p->u.qpsk.fec_inner); + stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); + stv0299_readreg (state, 0x23); + stv0299_writeregI(state, 0x12, 0xb9); } state->tuner_frequency = p->frequency; state->fec_inner = p->u.qpsk.fec_inner; state->symbol_rate = p->u.qpsk.symbol_rate; - break; + + return 0; } - case FE_GET_FRONTEND: +static int stv0299_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) { - struct dvb_frontend_parameters *p = arg; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; s32 derot_freq; - int Mclk = M_CLK; int invval; - if (state->tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; + derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8) + | stv0299_readreg (state, 0x23)); - derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8) - | stv0299_readreg (i2c, 0x23)); - - derot_freq *= (Mclk >> 16); + derot_freq *= (state->config->mclk >> 16); derot_freq += 500; derot_freq /= 1000; p->frequency += derot_freq; - invval = stv0299_readreg (i2c, 0x0c) & 1; - if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1; + invval = stv0299_readreg (state, 0x0c) & 1; + if (state->config->invert) invval = (~invval) & 1; p->inversion = invval ? INVERSION_ON : INVERSION_OFF; - p->u.qpsk.fec_inner = stv0299_get_fec (i2c); - p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, state->tuner_type); - break; - } - - case FE_SLEEP: - stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */ - stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */ - stv0299_writereg (i2c, 0x02, 0x80); - state->initialised = 0; - break; - - case FE_INIT: - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - state->tuner_frequency = 0; - if (!state->initialised) { - state->initialised = 1; - return stv0299_init (i2c, state->tuner_type); - } - break; + p->u.qpsk.fec_inner = stv0299_get_fec (state); + p->u.qpsk.symbol_rate = stv0299_get_symbolrate (state); - default: - return stv0299_init (i2c, state->tuner_type); + return 0; } - break; - - case FE_DISEQC_SEND_MASTER_CMD: - return stv0299_send_diseqc_msg (i2c, arg); - - case FE_DISEQC_SEND_BURST: - return stv0299_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg); - case FE_SET_TONE: - return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg); +static int stv0299_sleep(struct dvb_frontend* fe) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - case FE_SET_VOLTAGE: - return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg, - state->tuner_type); + stv0299_writeregI(state, 0x02, 0x80); + state->initialised = 0; - case FE_DISHNETWORK_SEND_LEGACY_CMD: - return stv0299_send_legacy_dish_cmd (i2c, - (u32)(unsigned long)arg, - state->tuner_type); + return 0; +} - case FE_GET_TUNE_SETTINGS: +static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - fesettings->min_delay_ms = 50; - if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) { - fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000; - fesettings->max_drift = 5000; - } else { - fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000; - fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000; - } - break; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - default: - fesettings->min_delay_ms = 100; + fesettings->min_delay_ms = state->config->min_delay_ms; if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) { fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000; fesettings->max_drift = 5000; @@ -1296,225 +661,103 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000; fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000; } - break; - } - - return 0; - } - - default: - return -EOPNOTSUPP; - }; - return 0; } -static long probe_tuner (struct i2c_adapter *adapter) -{ - struct i2c_adapter *i2c = adapter; /* superfluous */ - - /* read the status register of TSA5059 */ - u8 rpt[] = { 0x05, 0xb5 }; - u8 stat [] = { 0 }; - u8 tda6100_buf [] = { 0, 0 }; - int ret; - struct i2c_msg msg1 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, .len = 2 }, - { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }}; - struct i2c_msg msg2 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, .len = 2 }, - { .addr = 0x61, .flags = I2C_M_RD, .buf = stat, .len = 1 }}; - struct i2c_msg msg3 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, .len = 2 }, - { .addr = 0x60, .flags = 0, .buf = tda6100_buf, .len = 2 }}; - - stv0299_writereg (i2c, 0x01, 0x15); - stv0299_writereg (i2c, 0x02, 0x30); - stv0299_writereg (i2c, 0x03, 0x00); - - - printk("stv0299: try to attach to %s\n", adapter->name); - - if (!strcmp(adapter->name, "SkyStar2")) { - printk ("stv0299: setup for tuner Samsung TBMU24112IMB\n"); - return SAMSUNG_TBMU24112IMB; - } - - if ((ret = i2c_transfer(i2c, msg1, 2)) == 2) { - if ( strcmp(adapter->name, "TT-Budget/WinTV-NOVA-CI PCI") == 0 ) { - // technotrend cards require non-datasheet settings - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on TechnoTrend hardware\n"); - return PHILIPS_SU1278_TSA_TT; - } else { - // fall back to datasheet-recommended settings - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth)\n"); - return PHILIPS_SU1278_TSA; - } - } - - if ((ret = i2c_transfer(i2c, msg2, 2)) == 2) { - if ( strcmp(adapter->name, "KNC1 DVB-S") == 0 ) +static void stv0299_release(struct dvb_frontend* fe) { - // Typhoon cards have unusual wiring. - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on Typhoon hardware\n"); - return PHILIPS_SU1278_TSA_TY; - } - else if ( strcmp(adapter->name, "TerraTec Cinergy 1200 DVB-S") == 0 ) - { - // Cinergy cards have unusual wiring. - printk ("%s: setup for tuner SU1278 (TSA5059 synth) on" - " TerraTec hardware\n", __FILE__); - return PHILIPS_SU1278_TSA_CI; - } - //else if ((stat[0] & 0x3f) == 0) { - else if (0) { - printk ("stv0299: setup for tuner TDQF-S001F\n"); - return LG_TDQF_S001F; - } else { - printk ("stv0299: setup for tuner BSRU6, TDQB-S00x\n"); - return ALPS_BSRU6; - } - } - - /** - * setup i2c timing for SU1278... - */ - stv0299_writereg (i2c, 0x02, 0x00); - - if ((ret = i2c_transfer(i2c, msg3, 2)) == 2) { - printk ("stv0299: setup for tuner Philips SU1278 (TUA6100 synth)\n"); - return PHILIPS_SU1278_TUA; - } - - printk ("stv0299: unknown PLL synthesizer (ret == %i), please report to <linuxdvb@linuxtv.org>!!\n", ret); - - return UNKNOWN_FRONTEND; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_client client_template; +static struct dvb_frontend_ops stv0299_ops; -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct stv0299_state* state; - int tuner_type; - int ret; - u8 id; + struct stv0299_state* state = NULL; + int id; - stv0299_writereg(adapter, 0x02, 0x34); /* standby off */ - msleep(200); - id = stv0299_readreg(adapter, 0x00); + /* allocate memory for the internal state */ + state = (struct stv0299_state*) kmalloc(sizeof(struct stv0299_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &stv0299_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + state->errmode = STATUS_BER; - dprintk ("%s: id == 0x%02x\n", __FUNCTION__, id); + /* check if the demod is there */ + stv0299_writeregI(state, 0x02, 0x34); /* standby off */ + msleep(200); + id = stv0299_readreg(state, 0x00); /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ /* register 0x00 might contain 0x80 when returning from standby */ - if (id != 0xa1 && id != 0x80) - return -ENODEV; - - if ((tuner_type = probe_tuner(adapter)) < 0) - return -ENODEV; - - if ((state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - state->tuner_type = tuner_type; - state->tuner_frequency = 0; - state->initialised = 0; - state->i2c = adapter; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = (0x68>>1); - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return -EFAULT; - } - - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(uni0299_ioctl, state->dvb, state, - &uni0299_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return -EFAULT; -} - - return 0; -} - -static int detach_client(struct i2c_client *client) -{ - struct stv0299_state *state = (struct stv0299_state*)i2c_get_clientdata(client); - - dvb_unregister_frontend (uni0299_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct stv0299_state *data = (struct stv0299_state*)i2c_get_clientdata(client); - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: { - data->dvb = (struct dvb_adapter*)arg; - break; - } - case FE_UNREGISTER: { - data->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_STV0299, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + if (id != 0xa1 && id != 0x80) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops stv0299_ops = { + + .info = { + .name = "ST STV0299 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_tolerance = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = stv0299_release, + + .init = stv0299_init, + .sleep = stv0299_sleep, + + .set_frontend = stv0299_set_frontend, + .get_frontend = stv0299_get_frontend, + .get_tune_settings = stv0299_get_tune_settings, + + .read_status = stv0299_read_status, + .read_ber = stv0299_read_ber, + .read_signal_strength = stv0299_read_signal_strength, + .read_snr = stv0299_read_snr, + .read_ucblocks = stv0299_read_ucblocks, + + .diseqc_send_master_cmd = stv0299_send_diseqc_msg, + .diseqc_send_burst = stv0299_send_diseqc_burst, + .set_tone = stv0299_set_tone, + .set_voltage = stv0299_set_voltage, + .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd, }; -static int __init init_uni0299 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_uni0299 (void) -{ - if (i2c_del_driver(&driver)) - printk("stv0299: driver deregistration failed\n"); -} - -module_init (init_uni0299); -module_exit (exit_uni0299); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("Universal STV0299/TSA5059/SL1935 DVB Frontend driver"); +MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(stv0299_writereg); +EXPORT_SYMBOL(stv0299_attach); diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h new file mode 100644 index 000000000000..79457a80a11f --- /dev/null +++ b/drivers/media/dvb/frontends/stv0299.h @@ -0,0 +1,104 @@ +/* + Driver for ST STV0299 demodulator + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + <ralph@convergence.de>, + <holger@convergence.de>, + <js@convergence.de> + + + Philips SU1278/SH + + Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de> + + + LG TDQF-S001F + + Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net> + & Andreas Oberritter <obi@linuxtv.org> + + + Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B + + Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>: + + Support for Philips SU1278 on Technotrend hardware + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + + 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 STV0299_H +#define STV0299_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +#define STV0229_LOCKOUTPUT_0 0 +#define STV0229_LOCKOUTPUT_1 1 +#define STV0229_LOCKOUTPUT_CF 2 +#define STV0229_LOCKOUTPUT_LK 3 + +#define STV0299_VOLT13_OP0 0 +#define STV0299_VOLT13_OP1 1 + +struct stv0299_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* inittab - array of pairs of values. + * First of each pair is the register, second is the value. + * List should be terminated with an 0xff, 0xff pair. + */ + u8* inittab; + + /* master clock to use */ + u32 mclk; + + /* does the inversion require inversion? */ + u8 invert:1; + + /* Should the enhanced tuning code be used? */ + u8 enhanced_tuning:1; + + /* Skip reinitialisation? */ + u8 skip_reinit:1; + + /* LOCK OUTPUT setting */ + u8 lock_output:2; + + /* Is 13v controlled by OP0 or OP1? */ + u8 volt13_op0_op1:1; + + /* minimum delay before retuning */ + int min_delay_ms; + + /* Set the symbol rate */ + int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data); + +extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c); + +#endif // STV0299_H diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c new file mode 100644 index 000000000000..df0b844e9f88 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10021.c @@ -0,0 +1,483 @@ +/* + TDA10021 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Suppport for TDA10021 + + 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/config.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include "dvb_frontend.h" +#include "tda10021.h" + + +struct tda10021_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda10021_config* config; + + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; +}; + + +#if 0 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +static int verbose; + +#define XIN 57840000UL +#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) +#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) +#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) + +#define FIN (XIN >> 4) + +int tda10021_inittab_size = 0x40; +static u8 tda10021_inittab[0x40]= +{ + 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, + 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, + 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, + 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, + 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, + 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, +}; + +static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) + printk("DVB: TDA10021(%d): %s, writereg error " + "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, reg, data, ret); + + msleep(10); + return (ret != 1) ? -EREMOTEIO : 0; +} + + +static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + if (ret != 2) + printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, ret); + return b1[0]; +} + +//get access to tuner +static int lock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10021: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10021: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, + fe_spectral_inversion_t inversion) +{ + reg0 |= state->reg0 & 0x63; + + if (INVERSION_ON == inversion) + ENABLE_INVERSION(reg0); + else if (INVERSION_OFF == inversion) + DISABLE_INVERSION(reg0); + + tda10021_writereg (state, 0x00, reg0 & 0xfe); + tda10021_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) +{ + s32 BDR; + s32 BDRI; + s16 SFIL=0; + u16 NDEC = 0; + u32 tmp, ratio; + + if (symbolrate > XIN/2) + symbolrate = XIN/2; + if (symbolrate < 500000) + symbolrate = 500000; + + if (symbolrate < XIN/16) NDEC = 1; + if (symbolrate < XIN/32) NDEC = 2; + if (symbolrate < XIN/64) NDEC = 3; + + if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; + if (symbolrate < (u32)(XIN/16)) SFIL = 0; + if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; + if (symbolrate < (u32)(XIN/32)) SFIL = 0; + if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; + if (symbolrate < (u32)(XIN/64)) SFIL = 0; + if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; + + symbolrate <<= NDEC; + ratio = (symbolrate << 4) / FIN; + tmp = ((symbolrate << 4) % FIN) << 8; + ratio = (ratio << 8) + tmp / FIN; + tmp = (tmp % FIN) << 8; + ratio = (ratio << 8) + (tmp + FIN/2) / FIN; + + BDR = ratio; + BDRI = (((XIN << 5) / symbolrate) + 1) / 2; + + if (BDRI > 0xFF) + BDRI = 0xFF; + + SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; + + NDEC = (NDEC << 6) | tda10021_inittab[0x03]; + + tda10021_writereg (state, 0x03, NDEC); + tda10021_writereg (state, 0x0a, BDR&0xff); + tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); + tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); + + tda10021_writereg (state, 0x0d, BDRI); + tda10021_writereg (state, 0x0e, SFIL); + + return 0; +} + + + + + + + + + + +static int tda10021_init (struct dvb_frontend *fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int i; + + dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); + + //tda10021_writereg (fe, 0, 0); + + for (i=0; i<tda10021_inittab_size; i++) + tda10021_writereg (state, i, tda10021_inittab[i]); + + tda10021_writereg (state, 0x34, state->pwm); + + //Comment by markus + //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) + //0x2A[4] == BYPPLL -> Power down mode (default 1) + //0x2A[5] == LCK -> PLL Lock Flag + //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) + + //Activate PLL + tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); + + if (state->config->pll_init) { + lock_tuner(state); + state->config->pll_init(fe); + unlock_tuner(state); + } + + return 0; +} + +static int tda10021_set_parameters (struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + //table for QAM4-QAM256 ready QAM4 QAM16 QAM32 QAM64 QAM128 QAM256 + //CONF + static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c, 0x10 }; + //AGCREF value + static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78, 0x5c }; + //LTHR value + static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36, 0x26 }; + //MSETH + static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34, 0x23 }; + //AREF + static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e, 0x6b }; + + int qam = p->u.qam.modulation; + + if (qam < 0 || qam > 5) + return -EINVAL; + + //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); + + lock_tuner(state); + state->config->pll_set(fe, p); + unlock_tuner(state); + + tda10021_set_symbolrate (state, p->u.qam.symbol_rate); + tda10021_writereg (state, 0x34, state->pwm); + + tda10021_writereg (state, 0x01, reg0x01[qam]); + tda10021_writereg (state, 0x05, reg0x05[qam]); + tda10021_writereg (state, 0x08, reg0x08[qam]); + tda10021_writereg (state, 0x09, reg0x09[qam]); + + tda10021_setup_reg0 (state, reg0x00[qam], p->inversion); + + return 0; +} + +static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int sync; + + *status = 0; + //0x11[0] == EQALGO -> Equalizer algorithms state + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10021_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u32 _ber = tda10021_readreg(state, 0x14) | + (tda10021_readreg(state, 0x15) << 8) | + ((tda10021_readreg(state, 0x16) & 0x0f) << 16); + *ber = 10 * _ber; + + return 0; +} + +static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u8 gain = tda10021_readreg(state, 0x17); + *strength = (gain << 8) | gain; + + return 0; +} + +static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u8 quality = ~tda10021_readreg(state, 0x18); + *snr = (quality << 8) | quality; + + return 0; +} + +static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + + /* reset uncorrected block counter */ + tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); + tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); + + return 0; +} + +static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int sync; + s8 afc = 0; + + sync = tda10021_readreg(state, 0x11); + afc = tda10021_readreg(state, 0x19); + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : + "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->u.qam.symbol_rate * afc) >> 10); + } + + p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->u.qam.fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10021_sleep(struct dvb_frontend* fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ + tda10021_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static void tda10021_release(struct dvb_frontend* fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda10021_ops; + +struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct tda10021_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + state->reg0 = tda10021_inittab[0]; + + /* check if the demod is there */ + if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda10021_ops = { + + .info = { + .name = "Philips TDA10021 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ + #if 0 + .frequency_tolerance = ???, + .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ + #endif + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10021_release, + + .init = tda10021_init, + .sleep = tda10021_sleep, + + .set_frontend = tda10021_set_parameters, + .get_frontend = tda10021_get_frontend, + + .read_status = tda10021_read_status, + .read_ber = tda10021_read_ber, + .read_signal_strength = tda10021_read_signal_strength, + .read_snr = tda10021_read_snr, + .read_ucblocks = tda10021_read_ucblocks, +}; + +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); + +MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda10021_attach); diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h new file mode 100644 index 000000000000..a34a941b961b --- /dev/null +++ b/drivers/media/dvb/frontends/tda10021.h @@ -0,0 +1,43 @@ +/* + TDA10021 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Suppport for TDA10021 + + 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 TDA10021_H +#define TDA10021_H + +#include <linux/dvb/frontend.h> + +struct tda10021_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, + struct i2c_adapter* i2c, + u8 pwm); + +#endif // TDA10021_H diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 5701387de65e..1e38162c2ecd 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1,5 +1,5 @@ /* - Driver for Philips tda1004xh OFDM Frontend + Driver for Philips tda1004xh OFDM Demodulator (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach @@ -32,23 +32,35 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/firmware.h> - #include "dvb_frontend.h" +#include "tda1004x.h" -#define FRONTEND_NAME "dvbfe_tda1004x" +#define TDA1004X_DEMOD_TDA10045 0 +#define TDA1004X_DEMOD_TDA10046 1 -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) -static int debug; +struct tda1004x_state +{ + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; -#define MC44BC374_ADDRESS 0x65 + const struct tda1004x_config* config; + + struct dvb_frontend frontend; + + /* private demod data */ + u8 initialised:1; + + u8 demod_type; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda1004x: " args); \ + } while (0) #define TDA1004X_CHIPID 0x00 #define TDA1004X_AUTO 0x01 @@ -128,53 +140,7 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); #define TDA10046H_CODE_IN 0x58 -#define FE_TYPE_TDA10045H 0 -#define FE_TYPE_TDA10046H 1 - -#define TUNER_TYPE_TD1344 0 -#define TUNER_TYPE_TD1316 1 - -static struct dvb_frontend_info tda10045h_info = { - .name = "Philips TDA10045H", - .type = FE_OFDM, - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, - .caps = - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO -}; - -static struct dvb_frontend_info tda10046h_info = { - .name = "Philips TDA10046H", - .type = FE_OFDM, - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, - .caps = - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO -}; - -struct tda1004x_state { - u8 tda1004x_address; - u8 tuner_address; - u8 initialised; - u8 tuner_type; - u8 fe_type; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - - int dspCodeCounterReg; - int dspCodeInReg; - int dspVersion; -}; - -static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int data) +static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data) { int ret; u8 buf[] = { reg, data }; @@ -182,8 +148,8 @@ static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *t dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data); - msg.addr = tda_state->tda1004x_address; - ret = i2c_transfer(i2c, &msg, 1); + msg.addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", @@ -194,7 +160,7 @@ static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *t return (ret != 1) ? -1 : 0; } -static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg) +static int tda1004x_read_byte(struct tda1004x_state *state, int reg) { int ret; u8 b0[] = { reg }; @@ -204,9 +170,9 @@ static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *td dprintk("%s: reg=0x%x\n", __FUNCTION__, reg); - msg[0].addr = tda_state->tda1004x_address; - msg[1].addr = tda_state->tda1004x_address; - ret = i2c_transfer(i2c, msg, 2); + msg[0].addr = state->config->demod_address; + msg[1].addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg, @@ -219,14 +185,14 @@ static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *td return b1[0]; } -static int tda1004x_write_mask(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int mask, int data) +static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data) { int val; dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg, mask, data); // read a byte and check - val = tda1004x_read_byte(i2c, tda_state, reg); + val = tda1004x_read_byte(state, reg); if (val < 0) return val; @@ -235,10 +201,10 @@ static int tda1004x_write_mask(struct i2c_adapter *i2c, struct tda1004x_state *t val |= data & 0xff; // write it out again - return tda1004x_write_byte(i2c, tda_state, reg, val); + return tda1004x_write_byteI(state, reg, val); } -static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, unsigned char *buf, int len) +static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len) { int i; int result; @@ -247,7 +213,7 @@ static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *td result = 0; for (i = 0; i < len; i++) { - result = tda1004x_write_byte(i2c, tda_state, reg + i, buf[i]); + result = tda1004x_write_byteI(state, reg + i, buf[i]); if (result != 0) break; } @@ -255,25 +221,24 @@ static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *td return result; } -static int tda1004x_enable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) +static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state) { int result; dprintk("%s\n", __FUNCTION__); - result = tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 2); + result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2); msleep(1); return result; } -static int tda1004x_disable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) +static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state) { dprintk("%s\n", __FUNCTION__); - return tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 0); + return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0); } -static int tda10045h_set_bandwidth(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda10045h_set_bandwidth(struct tda1004x_state *state, fe_bandwidth_t bandwidth) { static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f }; @@ -282,31 +247,27 @@ static int tda10045h_set_bandwidth(struct i2c_adapter *i2c, switch (bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); break; case BANDWIDTH_7_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); break; default: return -EINVAL; } - tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0); + tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0); return 0; } -static int tda10046h_set_bandwidth(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda10046h_set_bandwidth(struct tda1004x_state *state, fe_bandwidth_t bandwidth) { static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e }; @@ -315,18 +276,15 @@ static int tda10046h_set_bandwidth(struct i2c_adapter *i2c, switch (bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); break; case BANDWIDTH_7_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0xFF); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); break; default: @@ -336,7 +294,9 @@ static int tda10046h_set_bandwidth(struct i2c_adapter *i2c, return 0; } -static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *state, unsigned char *mem, unsigned int len) +static int tda1004x_do_upload(struct tda1004x_state *state, + unsigned char *mem, unsigned int len, + u8 dspCodeCounterReg, u8 dspCodeInReg) { u8 buf[65]; struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 }; @@ -344,10 +304,10 @@ static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *st int pos = 0; /* clear code counter */ - tda1004x_write_byte(i2c, state, state->dspCodeCounterReg, 0); - fw_msg.addr = state->tda1004x_address; + tda1004x_write_byteI(state, dspCodeCounterReg, 0); + fw_msg.addr = state->config->demod_address; - buf[0] = state->dspCodeInReg; + buf[0] = dspCodeInReg; while (pos != len) { // work out how much to send this time @@ -359,7 +319,7 @@ static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *st // send the chunk memcpy(buf + 1, mem + pos, tx_size); fw_msg.len = tx_size + 1; - if (i2c_transfer(i2c, &fw_msg, 1) != 1) { + if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) { printk("tda1004x: Error during firmware upload\n"); return -EIO; } @@ -370,18 +330,17 @@ static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *st return 0; } -static int tda1004x_check_upload_ok(struct i2c_adapter *i2c, struct tda1004x_state *state) +static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion) { u8 data1, data2; // check upload was OK - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP - tda1004x_write_byte(i2c, state, TDA1004X_DSP_CMD, 0x67); + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP + tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67); - data1 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA1); - data2 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA2); - if (data1 != 0x67 || data2 != state->dspVersion) { - printk("tda1004x: firmware upload failed!\n"); + data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1); + data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2); + if (data1 != 0x67 || data2 != dspVersion) { return -EIO; } @@ -389,31 +348,34 @@ static int tda1004x_check_upload_ok(struct i2c_adapter *i2c, struct tda1004x_sta } -static int tda10045_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client) +static int tda10045_fwupload(struct dvb_frontend* fe) { + struct tda1004x_state* state = fe->demodulator_priv; int ret; const struct firmware *fw; + + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0; + /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, TDA10045_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); if (ret) { printk("tda1004x: no firmware upload (timeout or file not found?)\n"); return ret; } - /* set some valid bandwith parameters before uploading */ - /* reset chip */ - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); msleep(10); /* set parameters */ - tda10045h_set_bandwidth(i2c, state, BANDWIDTH_8_MHZ); + tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ); - ret = tda1004x_do_upload(i2c, state, fw->data, fw->size); + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN); if (ret) return ret; @@ -421,49 +383,47 @@ static int tda10045_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *sta /* DSPREADY doesn't seem to work on the TDA10045H */ msleep(100); - ret = tda1004x_check_upload_ok(i2c, state); - if (ret) - return ret; - - return 0; + return tda1004x_check_upload_ok(state, 0x2c); } -static int tda10046_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client) +static int tda10046_fwupload(struct dvb_frontend* fe) { + struct tda1004x_state* state = fe->demodulator_priv; unsigned long timeout; int ret; const struct firmware *fw; + /* reset + wake up chip */ + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); + msleep(100); + + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0; + /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, TDA10046_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); if (ret) { printk("tda1004x: no firmware upload (timeout or file not found?)\n"); return ret; } - /* set some valid bandwith parameters before uploading */ - - /* reset chip */ - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 1, 0); - tda1004x_write_mask(i2c, state, TDA10046H_CONF_TRISTATE1, 1, 0); - msleep(10); - /* set parameters */ - tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL2, 10); - tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL3, 0); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_OFFSET, 99); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_MSB, 0xd4); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_LSB, 0x2c); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST - - ret = tda1004x_do_upload(i2c, state, fw->data, fw->size); + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); if (ret) return ret; /* wait for DSP to initialise */ timeout = jiffies + HZ; - while(!(tda1004x_read_byte(i2c, state, TDA1004X_STATUS_CD) & 0x20)) { + while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) { if (time_after(jiffies, timeout)) { printk("tda1004x: DSP failed to initialised.\n"); return -EIO; @@ -471,95 +431,7 @@ static int tda10046_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *sta msleep(1); } - ret = tda1004x_check_upload_ok(i2c, state); - if (ret) - return ret; - - return 0; -} - -static int tda10045h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) -{ - struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - - dprintk("%s\n", __FUNCTION__); - - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC - - // Disable the MC44BC374C - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = MC44BC374_ADDRESS; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { - i2c_transfer(i2c, &tuner_msg, 1); - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - - // tda setup - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface - tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e); - - return 0; -} - -static int tda10046h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) -{ - struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - - dprintk("%s\n", __FUNCTION__); - - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0); // wake up the chip - - // Disable the MC44BC374C - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = MC44BC374_ADDRESS; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { - i2c_transfer(i2c, &tuner_msg, 1); - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - - // tda setup - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0x40); // TT TDA10046H needs inversion ON - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); // PLL M = 10 - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_CONF, 0); // AGC setup - tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MIN, 0); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MIN, 0); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MAX, 0xff); // } - tda1004x_write_mask(i2c, tda_state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1 - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config - tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup - tda1004x_write_byte(i2c, tda_state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config - tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select - tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz - - return 0; + return tda1004x_check_upload_ok(state, 0x20); } static int tda1004x_encode_fec(int fec) @@ -602,216 +474,194 @@ static int tda1004x_decode_fec(int tdafec) return -1; } -static int tda1004x_set_frequency(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, - struct dvb_frontend_parameters *fe_params) -{ - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - int counter, counter2; - dprintk("%s\n", __FUNCTION__); - // setup the frequency buffer - switch (tda_state->tuner_type) { - case TUNER_TYPE_TD1344: - - // setup tuner buffer - // ((Fif+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = - (((fe_params->frequency / 1000) * 6) + 217502) / 1000; - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0x88; - if (fe_params->frequency < 550000000) { - tuner_buf[3] = 0xab; - } else { - tuner_buf[3] = 0xeb; - } - // tune it - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = tda_state->tuner_address; - tuner_msg.len = 4; - i2c_transfer(i2c, &tuner_msg, 1); - - // wait for it to finish - tuner_msg.len = 1; - tuner_msg.flags = I2C_M_RD; - counter = 0; - counter2 = 0; - while (counter++ < 100) { - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - if (tuner_buf[0] & 0x40) { - counter2++; - } else { - counter2 = 0; - } - } - - if (counter2 > 10) { - break; - } - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - break; - case TUNER_TYPE_TD1316: - // determine charge pump - tuner_frequency = fe_params->frequency + 36130000; - if (tuner_frequency < 87000000) { - return -EINVAL; - } else if (tuner_frequency < 130000000) { - cp = 3; - } else if (tuner_frequency < 160000000) { - cp = 5; - } else if (tuner_frequency < 200000000) { - cp = 6; - } else if (tuner_frequency < 290000000) { - cp = 3; - } else if (tuner_frequency < 420000000) { - cp = 5; - } else if (tuner_frequency < 480000000) { - cp = 6; - } else if (tuner_frequency < 620000000) { - cp = 3; - } else if (tuner_frequency < 830000000) { - cp = 5; - } else if (tuner_frequency < 895000000) { - cp = 7; - } else { - return -EINVAL; - } - // determine band - if (fe_params->frequency < 49000000) { - return -EINVAL; - } else if (fe_params->frequency < 159000000) { - band = 1; - } else if (fe_params->frequency < 444000000) { - band = 2; - } else if (fe_params->frequency < 861000000) { - band = 4; - } else { - return -EINVAL; + + + + + + + + + + + + +int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + return tda1004x_write_byteI(state, reg, data); } - // work out filter - switch (fe_params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - filter = 0; - break; +static int tda10045_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; - case BANDWIDTH_7_MHZ: - filter = 0; - break; + dprintk("%s\n", __FUNCTION__); - case BANDWIDTH_8_MHZ: - filter = 1; - break; + if (state->initialised) return 0; - default: - return -EINVAL; + if (tda10045_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); + return -EIO; } - // calculate divisor - // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = - (((fe_params->frequency / 1000) * 6) + 217280) / 1000; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - // tune it - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - // setup auto offset - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0); - - // disable agc_conf[2] - tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 0); + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC + + // Init the PLL + if (state->config->pll_init) { + tda1004x_enable_tuner_i2c(state); + state->config->pll_init(fe); + tda1004x_disable_tuner_i2c(state); + } + + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface + tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface + tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity + tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e); + + tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); + + state->initialised = 1; + return 0; } - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = tda_state->tuner_address; - tuner_msg.len = 4; - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { + +static int tda10046_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + dprintk("%s\n", __FUNCTION__); + + if (state->initialised) return 0; + + if (tda10046_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); return -EIO; } - msleep(1); - tda1004x_disable_tuner_i2c(i2c, tda_state); - if (tda_state->fe_type == FE_TYPE_TDA10046H) - tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 4); - break; - default: - return -EINVAL; - } + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip - dprintk("%s: success\n", __FUNCTION__); + // Init the PLL + if (state->config->pll_init) { + tda1004x_enable_tuner_i2c(state); + state->config->pll_init(fe); + tda1004x_disable_tuner_i2c(state); + } + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0x40); + tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10 + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99 + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221 + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // } + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0); // AGC setup + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // } + tda1004x_write_mask(state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits + tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1 + tda1004x_write_mask(state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config + tda1004x_write_mask(state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup + tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config + tda1004x_write_mask(state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select + tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz + + tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); + + state->initialised = 1; return 0; } -static int tda1004x_set_fe(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda1004x_set_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int inversion; dprintk("%s\n", __FUNCTION__); + if (state->demod_type == TDA1004X_DEMOD_TDA10046) { + // setup auto offset + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0); + + // disable agc_conf[2] + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0); + } + // set frequency - if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0) - return tmp; + tda1004x_enable_tuner_i2c(state); + state->config->pll_set(fe, fe_params); + tda1004x_disable_tuner_i2c(state); - // Hardcoded to use auto as much as possible - // The TDA10045 is very unreliable if AUTO mode is _not_ used. I have not - // yet tested the TDA10046 to see if this issue has been fixed + if (state->demod_type == TDA1004X_DEMOD_TDA10046) + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 4); + + // Hardcoded to use auto as much as possible on the TDA10045 as it + // is very unreliable if AUTO mode is _not_ used. + if (state->demod_type == TDA1004X_DEMOD_TDA10045) { fe_params->u.ofdm.code_rate_HP = FEC_AUTO; fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + } // Set standard params.. or put them to auto if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) || (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) || (fe_params->u.ofdm.constellation == QAM_AUTO) || (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) { - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 1); // enable auto - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits } else { - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 0); // disable auto + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto // set HP FEC tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP); if (tmp < 0) return tmp; - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 7, tmp); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp); // set LP FEC tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP); if (tmp < 0) return tmp; - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x38, tmp << 3); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3); // set constellation switch (fe_params->u.ofdm.constellation) { case QPSK: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0); break; case QAM_16: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 1); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1); break; case QAM_64: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 2); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2); break; default: @@ -821,19 +671,19 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, // set hierarchy switch (fe_params->u.ofdm.hierarchy_information) { case HIERARCHY_NONE: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5); break; case HIERARCHY_1: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 1 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5); break; case HIERARCHY_2: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 2 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5); break; case HIERARCHY_4: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 3 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5); break; default: @@ -842,30 +692,26 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, } // set bandwidth - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth); break; - case FE_TYPE_TDA10046H: - tda10046h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); + case TDA1004X_DEMOD_TDA10046: + tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth); break; } - // need to invert the inversion for TT TDA10046H - inversion = fe_params->inversion; - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - inversion = inversion ? INVERSION_OFF : INVERSION_ON; - } - // set inversion + inversion = fe_params->inversion; + if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON; switch (inversion) { case INVERSION_OFF: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0); + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0); break; case INVERSION_ON: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0x20); + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20); break; default: @@ -875,28 +721,28 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, // set guard interval switch (fe_params->u.ofdm.guard_interval) { case GUARD_INTERVAL_1_32: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; case GUARD_INTERVAL_1_16: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); break; case GUARD_INTERVAL_1_8: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); break; case GUARD_INTERVAL_1_4: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); break; case GUARD_INTERVAL_AUTO: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 2); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; default: @@ -906,18 +752,18 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, // set transmission mode switch (fe_params->u.ofdm.transmission_mode) { case TRANSMISSION_MODE_2K: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0 << 4); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4); break; case TRANSMISSION_MODE_8K: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 1 << 4); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4); break; case TRANSMISSION_MODE_AUTO: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 4); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0); break; default: @@ -925,15 +771,15 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, } // start the lock - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); msleep(10); break; - case FE_TYPE_TDA10046H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40); + case TDA1004X_DEMOD_TDA10046: + tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40); msleep(10); break; } @@ -941,25 +787,22 @@ static int tda1004x_set_fe(struct i2c_adapter *i2c, return 0; } -static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, struct dvb_frontend_parameters *fe_params) +static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params) { + struct tda1004x_state* state = fe->demodulator_priv; dprintk("%s\n", __FUNCTION__); // inversion status fe_params->inversion = INVERSION_OFF; - if (tda1004x_read_byte(i2c, tda_state, TDA1004X_CONFC1) & 0x20) { + if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) { fe_params->inversion = INVERSION_ON; } - - // need to invert the inversion for TT TDA10046H - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; - } + if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; // bandwidth - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - switch (tda1004x_read_byte(i2c, tda_state, TDA10045H_WREF_LSB)) { + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) { case 0x14: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; @@ -972,8 +815,8 @@ static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_s } break; - case FE_TYPE_TDA10046H: - switch (tda1004x_read_byte(i2c, tda_state, TDA10046H_TIME_WREF1)) { + case TDA1004X_DEMOD_TDA10046: + switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) { case 0x60: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; @@ -989,12 +832,12 @@ static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_s // FEC fe_params->u.ofdm.code_rate_HP = - tda1004x_decode_fec(tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) & 7); + tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7); fe_params->u.ofdm.code_rate_LP = - tda1004x_decode_fec((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) >> 3) & 7); + tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7); // constellation - switch (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 3) { + switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) { case 0: fe_params->u.ofdm.constellation = QPSK; break; @@ -1008,12 +851,12 @@ static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_s // transmission mode fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; - if (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x10) { + if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) { fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; } // guard interval - switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { case 0: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; @@ -1029,7 +872,7 @@ static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_s } // hierarchy - switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { case 0: fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; break; @@ -1047,8 +890,9 @@ static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_s return 0; } -static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, fe_status_t * fe_status) +static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status) { + struct tda1004x_state* state = fe->demodulator_priv; int status; int cber; int vber; @@ -1056,7 +900,7 @@ static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* dprintk("%s\n", __FUNCTION__); // read status - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD); + status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); if (status == -1) { return -EIO; } @@ -1071,12 +915,12 @@ static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* // is getting anything valid if (!(*fe_status & FE_HAS_VITERBI)) { // read the CBER - cber = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB); + cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB); if (cber == -1) return -EIO; - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB); + status = tda1004x_read_byte(state, TDA1004X_CBER_MSB); if (status == -1) return -EIO; cber |= (status << 8); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET); + tda1004x_read_byte(state, TDA1004X_CBER_RESET); if (cber != 65535) { *fe_status |= FE_HAS_VITERBI; @@ -1087,15 +931,15 @@ static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { // read the VBER - vber = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_LSB); + vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB); if (vber == -1) return -EIO; - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MID); + status = tda1004x_read_byte(state, TDA1004X_VBER_MID); if (status == -1) return -EIO; vber |= (status << 8); - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MSB); + status = tda1004x_read_byte(state, TDA1004X_VBER_MSB); if (status == -1) return -EIO; vber |= ((status << 16) & 0x0f); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CVBER_LUT); + tda1004x_read_byte(state, TDA1004X_CVBER_LUT); // if RS has passed some valid TS packets, then we must be // getting some SYNC bytes @@ -1109,26 +953,27 @@ static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* return 0; } -static int tda1004x_read_signal_strength(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * signal) +static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int reg = 0; dprintk("%s\n", __FUNCTION__); // determine the register to use - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: reg = TDA10045H_S_AGC; break; - case FE_TYPE_TDA10046H: + case TDA1004X_DEMOD_TDA10046: reg = TDA10046H_AGC_IF_LEVEL; break; } // read it - tmp = tda1004x_read_byte(i2c, tda_state, reg); + tmp = tda1004x_read_byte(state, reg); if (tmp < 0) return -EIO; @@ -1137,14 +982,15 @@ static int tda1004x_read_signal_strength(struct i2c_adapter *i2c, struct tda1004 return 0; } -static int tda1004x_read_snr(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * snr) +static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; dprintk("%s\n", __FUNCTION__); // read it - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SNR); + tmp = tda1004x_read_byte(state, TDA1004X_SNR); if (tmp < 0) return -EIO; if (tmp) { @@ -1156,8 +1002,9 @@ static int tda1004x_read_snr(struct i2c_adapter *i2c, struct tda1004x_state* tda return 0; } -static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ucblocks) +static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int tmp2; int counter; @@ -1166,16 +1013,16 @@ static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state // read the UCBLOCKS and reset counter = 0; - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR); + tmp = tda1004x_read_byte(state, TDA1004X_UNCOR); if (tmp < 0) return -EIO; tmp &= 0x7f; while (counter++ < 5) { - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); - tmp2 = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR); + tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR); if (tmp2 < 0) return -EIO; tmp2 &= 0x7f; @@ -1192,373 +1039,189 @@ static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state return 0; } -static int tda1004x_read_ber(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ber) +static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; dprintk("%s\n", __FUNCTION__); // read it in - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB); + tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB); if (tmp < 0) return -EIO; *ber = tmp << 1; - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB); + tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB); if (tmp < 0) return -EIO; *ber |= (tmp << 9); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET); + tda1004x_read_byte(state, TDA1004X_CBER_RESET); dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber); return 0; } -static int tda1004x_sleep(struct i2c_adapter *i2c, struct tda1004x_state* tda_state) +static int tda1004x_sleep(struct dvb_frontend* fe) { - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0x10); - break; + struct tda1004x_state* state = fe->demodulator_priv; - case FE_TYPE_TDA10046H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 1); - break; - } - - return 0; -} - -static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct tda1004x_state *tda_state = (struct tda1004x_state *) fe->data; - struct i2c_adapter *i2c = tda_state->i2c; - int status = 0; - - dprintk("%s: cmd=0x%x\n", __FUNCTION__, cmd); - - switch (cmd) { - case FE_GET_INFO: - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info)); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10); break; - case FE_TYPE_TDA10046H: - memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info)); + case TDA1004X_DEMOD_TDA10046: + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } - break; - - case FE_READ_STATUS: - return tda1004x_read_status(i2c, tda_state, (fe_status_t *) arg); - - case FE_READ_BER: - return tda1004x_read_ber(i2c, tda_state, (u32 *) arg); - - case FE_READ_SIGNAL_STRENGTH: - return tda1004x_read_signal_strength(i2c, tda_state, (u16 *) arg); - - case FE_READ_SNR: - return tda1004x_read_snr(i2c, tda_state, (u16 *) arg); - - case FE_READ_UNCORRECTED_BLOCKS: - return tda1004x_read_ucblocks(i2c, tda_state, (u32 *) arg); - - case FE_SET_FRONTEND: - return tda1004x_set_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg); - - case FE_GET_FRONTEND: - return tda1004x_get_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg); - - case FE_SLEEP: - tda_state->initialised = 0; - return tda1004x_sleep(i2c, tda_state); - - case FE_INIT: + state->initialised = 0; - // don't bother reinitialising - if (tda_state->initialised) return 0; - - // OK, perform initialisation - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - status = tda10045h_init(i2c, tda_state); - break; - - case FE_TYPE_TDA10046H: - status = tda10046h_init(i2c, tda_state); - break; } - if (status == 0) - tda_state->initialised = 1; - return status; - case FE_GET_TUNE_SETTINGS: +static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; fesettings->min_delay_ms = 800; fesettings->step_size = 166667; fesettings->max_drift = 166667*2; return 0; } - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int tda1004x_attach(struct i2c_adapter *i2c, struct tda1004x_state* state) +static void tda1004x_release(struct dvb_frontend* fe) { - int tda1004x_address = -1; - int tuner_address = -1; - int fe_type = -1; - int tuner_type = -1; - struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=NULL, .len=0 }; - static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab }; - static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab }; - - dprintk("%s\n", __FUNCTION__); - - // probe for tda10045h - if (tda1004x_address == -1) { - state->tda1004x_address = 0x08; - if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x25) { - tda1004x_address = 0x08; - fe_type = FE_TYPE_TDA10045H; - printk("tda1004x: Detected Philips TDA10045H.\n"); - } - } - - // probe for tda10046h - if (tda1004x_address == -1) { - state->tda1004x_address = 0x08; - if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x46) { - tda1004x_address = 0x08; - fe_type = FE_TYPE_TDA10046H; - printk("tda1004x: Detected Philips TDA10046H.\n"); - } - } - - // did we find a frontend? - if (tda1004x_address == -1) { - return -ENODEV; - } - - // enable access to the tuner - tda1004x_enable_tuner_i2c(i2c, state); - - // check for a TD1344 first - if (tuner_address == -1) { - tuner_msg.addr = 0x61; - tuner_msg.buf = td1344_init; - tuner_msg.len = sizeof(td1344_init); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x61; - tuner_type = TUNER_TYPE_TD1344; - printk("tda1004x: Detected Philips TD1344 tuner.\n"); - } - } - - // OK, try a TD1316 on address 0x63 - if (tuner_address == -1) { - tuner_msg.addr = 0x63; - tuner_msg.buf = td1316_init; - tuner_msg.len = sizeof(td1316_init); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x63; - tuner_type = TUNER_TYPE_TD1316; - printk("tda1004x: Detected Philips TD1316 tuner.\n"); - } - } - - // OK, TD1316 again, on address 0x60 (TDA10046H) - if (tuner_address == -1) { - tuner_msg.addr = 0x60; - tuner_msg.buf = td1316_init_tda10046h; - tuner_msg.len = sizeof(td1316_init_tda10046h); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x60; - tuner_type = TUNER_TYPE_TD1316; - printk("tda1004x: Detected Philips TD1316 tuner.\n"); - } - } - tda1004x_disable_tuner_i2c(i2c, state); - - // did we find a tuner? - if (tuner_address == -1) { - printk("tda1004x: Detected, but with unknown tuner.\n"); - return -ENODEV; + struct tda1004x_state* state = (struct tda1004x_state*) fe->demodulator_priv; + kfree(state); } - // create state - state->tda1004x_address = tda1004x_address; - state->fe_type = fe_type; - state->tuner_address = tuner_address; - state->tuner_type = tuner_type; - state->initialised = 0; +static struct dvb_frontend_ops tda10045_ops; - return 0; -} - -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct tda1004x_state *state; - int ret; + struct tda1004x_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (state == NULL) goto error; - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - return -ENOMEM; - } - - if (NULL == (state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - state->i2c = adapter; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->demod_type = TDA1004X_DEMOD_TDA10045; - ret = tda1004x_attach(adapter, state); - if (ret) { - kfree(state); - kfree(client); - return -ENODEV; - } + /* check if the demod is there */ + if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) goto error; - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = state->tda1004x_address; - i2c_set_clientdata(client, (void*)state); + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; +error: + if (state) kfree(state); + return NULL; } - // upload firmware - BUG_ON(!state->dvb); - - switch(state->fe_type) { - case FE_TYPE_TDA10045H: - state->dspCodeCounterReg = TDA10045H_FWPAGE; - state->dspCodeInReg = TDA10045H_CODE_IN; - state->dspVersion = 0x2c; - - ret = tda10045_fwupload(adapter, state, client); - if (ret) { - printk("tda1004x: firmware upload failed\n"); - goto out; - } - - ret = dvb_register_frontend(tda1004x_ioctl, state->dvb, - state, &tda10045h_info, - THIS_MODULE); - break; - case FE_TYPE_TDA10046H: - state->dspCodeCounterReg = TDA10046H_CODE_CPT; - state->dspCodeInReg = TDA10046H_CODE_IN; - state->dspVersion = 0x20; - - ret = tda10046_fwupload(adapter, state, client); - if (ret) { - printk("tda1004x: firmware upload failed\n"); - goto out; - } +static struct dvb_frontend_ops tda10046_ops; - ret = dvb_register_frontend(tda1004x_ioctl, state->dvb, - state, &tda10046h_info, - THIS_MODULE); - break; - default: - BUG_ON(1); - } - - if (ret) { - printk("tda1004x: registering frontend failed\n"); - goto out; - } - - return 0; -out: - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; -} - -static int detach_client(struct i2c_client *client) +struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) { - struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client); + struct tda1004x_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (state == NULL) goto error; - dvb_unregister_frontend (tda1004x_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->demod_type = TDA1004X_DEMOD_TDA10046; -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client); + /* check if the demod is there */ + if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error; - dprintk ("%s\n", __FUNCTION__); + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter*)arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} +error: + if (state) kfree(state); + return NULL; + } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_TDA1004X, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, +static struct dvb_frontend_ops tda10045_ops = { + + .info = { + .name = "Philips TDA10045H DVB-T", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .frequency_stepsize = 166667, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10045_init, + .sleep = tda1004x_sleep, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + .get_tune_settings = tda1004x_get_tune_settings, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, }; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, +static struct dvb_frontend_ops tda10046_ops = { + + .info = { + .name = "Philips TDA10046H DVB-T", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .frequency_stepsize = 166667, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10046_init, + .sleep = tda1004x_sleep, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + .get_tune_settings = tda1004x_get_tune_settings, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, }; -static int __init init_tda1004x(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_tda1004x(void) -{ - if (i2c_del_driver(&driver)) - printk("tda1004x: driver deregistration failed\n"); -} - -module_init(init_tda1004x); -module_exit(exit_tda1004x); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Frontend"); +MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(tda10045_attach); +EXPORT_SYMBOL(tda10046_attach); +EXPORT_SYMBOL(tda1004x_write_byte); diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h new file mode 100644 index 000000000000..e452fc0bad11 --- /dev/null +++ b/drivers/media/dvb/frontends/tda1004x.h @@ -0,0 +1,56 @@ + /* + Driver for Philips tda1004xh OFDM Frontend + + (c) 2004 Andrew de Quincey + + 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 TDA1004X_H +#define TDA1004X_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct tda1004x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the "inversion" need inverted? */ + u8 invert:1; + + /* Does the OCLK signal need inverted? */ + u8 invert_oclk:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); + +extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); + +extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data); + +#endif // TDA1004X_H diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c new file mode 100644 index 000000000000..03c4366e1889 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8083.c @@ -0,0 +1,490 @@ +/* + Driver for Philips TDA8083 based QPSK Demodulator + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler <ralph@convergence.de> + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler <holger@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "dvb_frontend.h" +#include "tda8083.h" + + +struct tda8083_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda8083_config* config; + + struct dvb_frontend frontend; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda8083: " args); \ + } while (0) + + +static u8 tda8083_init_tab [] = { + 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, + 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, + 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, + 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, + 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + + +static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk ("%s: writereg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + + +static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk ("%s: readreg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg1, ret); + + return ret == 2 ? 0 : -1; +} + + +static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) +{ + u8 val; + + tda8083_readregs (state, reg, &val, 1); + + return val; +} + + + +static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion) +{ + /* XXX FIXME: implement other modes than FEC_AUTO */ + if (inversion == INVERSION_AUTO) + return 0; + + return -EINVAL; +} + + +static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) +{ + if (fec == FEC_AUTO) + return tda8083_writereg (state, 0x07, 0xff); + + if (fec >= FEC_1_2 && fec <= FEC_8_9) + return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec)); + + return -EINVAL; +} + + +static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state) +{ + u8 index; + static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; + + index = tda8083_readreg(state, 0x0e) & 0x07; + + return fec_tab [index]; +} + + +static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) +{ + u32 ratio; + u32 tmp; + u8 filter; + + if (srate > 32000000) + srate = 32000000; + if (srate < 500000) + srate = 500000; + + filter = 0; + if (srate < 24000000) + filter = 2; + if (srate < 16000000) + filter = 3; + + tmp = 31250 << 16; + ratio = tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); + + tda8083_writereg (state, 0x05, filter); + tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff); + tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff); + tda8083_writereg (state, 0x04, (ratio ) & 0xff); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 1; +} + + +static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) +{ + unsigned long start = jiffies; + + while (jiffies - start < timeout && + !(tda8083_readreg(state, 0x02) & 0x80)) + { + msleep(50); + }; +} + +static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) +{ + tda8083_writereg (state, 0x26, 0xf1); + + switch (tone) { + case SEC_TONE_OFF: + return tda8083_writereg (state, 0x29, 0x00); + case SEC_TONE_ON: + return tda8083_writereg (state, 0x29, 0x80); + default: + return -EINVAL; + }; +} + + +static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage) +{ + switch (voltage) { + case SEC_VOLTAGE_13: + return tda8083_writereg (state, 0x20, 0x00); + case SEC_VOLTAGE_18: + return tda8083_writereg (state, 0x20, 0x11); + default: + return -EINVAL; + }; +} + +static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst) +{ + switch (burst) { + case SEC_MINI_A: + tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */ + break; + case SEC_MINI_B: + tda8083_writereg (state, 0x29, (7 << 2)); /* send B */ + break; + default: + return -EINVAL; + }; + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + +static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + int i; + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ + + for (i=0; i<m->msg_len; i++) + tda8083_writereg (state, 0x23 + i, m->msg[i]); + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + +static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + u8 sync = tda8083_readreg (state, 0x02); + + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x01) + *status |= FE_HAS_CARRIER; + + if (sync & 0x02) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if ((sync & 0x1f) == 0x1f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + *strength = (signal << 8) | signal; + + return 0; +} + +static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 _snr = tda8083_readreg (state, 0x08); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + state->config->pll_set(fe, p); + tda8083_set_inversion (state, p->inversion); + tda8083_set_fec (state, p->u.qpsk.fec_inner); + tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + /* FIXME: get symbolrate & frequency offset...*/ + /*p->frequency = ???;*/ + p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ? + INVERSION_ON : INVERSION_OFF; + p->u.qpsk.fec_inner = tda8083_get_fec (state); + /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/ + + return 0; +} + +static int tda8083_sleep(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_writereg (state, 0x00, 0x02); + return 0; +} + +static int tda8083_init(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + int i; + + for (i=0; i<44; i++) + tda8083_writereg (state, i, tda8083_init_tab[i]); + + if (state->config->pll_init) state->config->pll_init(fe); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_send_diseqc_burst (state, burst); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_set_tone (state, tone); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_set_voltage (state, voltage); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static void tda8083_release(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda8083_ops; + +struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c) +{ + struct tda8083_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if ((tda8083_readreg(state, 0x00)) != 0x05) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda8083_ops = { + + .info = { + .name = "Philips TDA8083 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, /* FIXME: guessed! */ + .frequency_max = 1400000, /* FIXME: guessed! */ + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + /* .frequency_tolerance = ???,*/ + .symbol_rate_min = 1000000, /* FIXME: guessed! */ + .symbol_rate_max = 45000000, /* FIXME: guessed! */ + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_MUTE_TS + }, + + .release = tda8083_release, + + .init = tda8083_init, + .sleep = tda8083_sleep, + + .set_frontend = tda8083_set_frontend, + .get_frontend = tda8083_get_frontend, + + .read_status = tda8083_read_status, + .read_signal_strength = tda8083_read_signal_strength, + .read_snr = tda8083_read_snr, + + .diseqc_send_master_cmd = tda8083_send_diseqc_msg, + .diseqc_send_burst = tda8083_diseqc_send_burst, + .set_tone = tda8083_diseqc_set_tone, + .set_voltage = tda8083_diseqc_set_voltage, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda8083_attach); diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h new file mode 100644 index 000000000000..466663307bf1 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8083.h @@ -0,0 +1,45 @@ +/* + Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler <ralph@convergence.de> + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler <holger@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef TDA8083_H +#define TDA8083_H + +#include <linux/dvb/frontend.h> + +struct tda8083_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c); + +#endif // TDA8083_H diff --git a/drivers/media/dvb/frontends/tda80xx.c b/drivers/media/dvb/frontends/tda80xx.c new file mode 100644 index 000000000000..5b8828b27445 --- /dev/null +++ b/drivers/media/dvb/frontends/tda80xx.c @@ -0,0 +1,749 @@ +/* + * tda80xx.c + * + * Philips TDA8044 / TDA8083 QPSK demodulator driver + * + * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net> + * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org> + * + * 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/config.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/threads.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "tda80xx.h" + +enum { + ID_TDA8044 = 0x04, + ID_TDA8083 = 0x05, +}; + + +struct tda80xx_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda80xx_config* config; + + struct dvb_frontend frontend; + + u32 clk; + int afc_loop; + struct work_struct worklet; + fe_code_rate_t code_rate; + fe_spectral_inversion_t spectral_inversion; + fe_status_t status; + u8 id; +}; + +static int debug = 1; +#define dprintk if (debug) printk + +static u8 tda8044_inittab_pre[] = { + 0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea, + 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58, + 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68, + 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00, + 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +static u8 tda8044_inittab_post[] = { + 0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea, + 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50, + 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68, + 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c, + 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +static u8 tda8083_inittab[] = { + 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, + 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, + 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, + 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, + 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static __inline__ u32 tda80xx_div(u32 a, u32 b) +{ + return (a + (b / 2)) / b; +} + +static __inline__ u32 tda80xx_gcd(u32 a, u32 b) +{ + u32 r; + + while ((r = a % b)) { + a = b; + b = r; + } + + return b; +} + +static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len) +{ + int ret; + struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg, ret); + + mdelay(10); + + return (ret == 2) ? 0 : -EREMOTEIO; +} + +static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len) +{ + int ret; + u8 wbuf[len + 1]; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 }; + + wbuf[0] = reg; + memcpy(&wbuf[1], buf, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret); + + mdelay(10); + + return (ret == 1) ? 0 : -EREMOTEIO; +} + +static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg) +{ + u8 val; + + tda80xx_read(state, reg, &val, 1); + + return val; +} + +static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data) +{ + return tda80xx_write(state, reg, &data, 1); +} + +static int tda80xx_set_parameters(struct tda80xx_state* state, + fe_spectral_inversion_t inversion, + u32 symbol_rate, + fe_code_rate_t fec_inner) +{ + u8 buf[15]; + u64 ratio; + u32 clk; + u32 k; + u32 sr = symbol_rate; + u32 gcd; + u8 scd; + + if (symbol_rate > (state->clk * 3) / 16) + scd = 0; + else if (symbol_rate > (state->clk * 3) / 32) + scd = 1; + else if (symbol_rate > (state->clk * 3) / 64) + scd = 2; + else + scd = 3; + + clk = scd ? (state->clk / (scd * 2)) : state->clk; + + /* + * Viterbi decoder: + * Differential decoding off + * Spectral inversion unknown + * QPSK modulation + */ + if (inversion == INVERSION_ON) + buf[0] = 0x60; + else if (inversion == INVERSION_OFF) + buf[0] = 0x20; + else + buf[0] = 0x00; + + /* + * CLK ratio: + * system clock frequency is up to 64 or 96 MHz + * + * formula: + * r = k * clk / symbol_rate + * + * k: 2^21 for caa 0..3, + * 2^20 for caa 4..5, + * 2^19 for caa 6..7 + */ + if (symbol_rate <= (clk * 3) / 32) + k = (1 << 19); + else if (symbol_rate <= (clk * 3) / 16) + k = (1 << 20); + else + k = (1 << 21); + + gcd = tda80xx_gcd(clk, sr); + clk /= gcd; + sr /= gcd; + + gcd = tda80xx_gcd(k, sr); + k /= gcd; + sr /= gcd; + + ratio = (u64)k * (u64)clk; + do_div(ratio, sr); + + buf[1] = ratio >> 16; + buf[2] = ratio >> 8; + buf[3] = ratio; + + /* nyquist filter roll-off factor 35% */ + buf[4] = 0x20; + + clk = scd ? (state->clk / (scd * 2)) : state->clk; + + /* Anti Alias Filter */ + if (symbol_rate < (clk * 3) / 64) + printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate); + else if (symbol_rate <= clk / 16) + buf[4] |= 0x07; + else if (symbol_rate <= (clk * 3) / 32) + buf[4] |= 0x06; + else if (symbol_rate <= clk / 8) + buf[4] |= 0x05; + else if (symbol_rate <= (clk * 3) / 16) + buf[4] |= 0x04; + else if (symbol_rate <= clk / 4) + buf[4] |= 0x03; + else if (symbol_rate <= (clk * 3) / 8) + buf[4] |= 0x02; + else if (symbol_rate <= clk / 2) + buf[4] |= 0x01; + else + buf[4] |= 0x00; + + /* Sigma Delta converter */ + buf[5] = 0x00; + + /* FEC: Possible puncturing rates */ + if (fec_inner == FEC_NONE) + buf[6] = 0x00; + else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9)) + buf[6] = (1 << (8 - fec_inner)); + else if (fec_inner == FEC_AUTO) + buf[6] = 0xff; + else + return -EINVAL; + + /* carrier lock detector threshold value */ + buf[7] = 0x30; + /* AFC1: proportional part settings */ + buf[8] = 0x42; + /* AFC1: integral part settings */ + buf[9] = 0x98; + /* PD: Leaky integrator SCPC mode */ + buf[10] = 0x28; + /* AFC2, AFC1 controls */ + buf[11] = 0x30; + /* PD: proportional part settings */ + buf[12] = 0x42; + /* PD: integral part settings */ + buf[13] = 0x99; + /* AGC */ + buf[14] = 0x50 | scd; + + printk("symbol_rate=%u clk=%u\n", symbol_rate, clk); + + return tda80xx_write(state, 0x01, buf, sizeof(buf)); +} + +static int tda80xx_set_clk(struct tda80xx_state* state) +{ + u8 buf[2]; + + /* CLK proportional part */ + buf[0] = (0x06 << 5) | 0x08; /* CMP[2:0], CSP[4:0] */ + /* CLK integral part */ + buf[1] = (0x04 << 5) | 0x1a; /* CMI[2:0], CSI[4:0] */ + + return tda80xx_write(state, 0x17, buf, sizeof(buf)); +} + +#if 0 +static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state) +{ + /* a constant value is nonsense here imho */ + return tda80xx_writereg(state, 0x22, 0xf9); +} +#endif + +static int tda80xx_close_loop(struct tda80xx_state* state) +{ + u8 buf[2]; + + /* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */ + buf[0] = 0x68; + /* AFC1: Loop closed, CAR Feedback: 8192 */ + buf[1] = 0x70; + + return tda80xx_write(state, 0x0b, buf, sizeof(buf)); +} + +static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt) +{ + schedule_work(priv); + + return IRQ_HANDLED; +} + +static void tda80xx_read_status_int(struct tda80xx_state* state) +{ + u8 val; + + static const fe_spectral_inversion_t inv_tab[] = { + INVERSION_OFF, INVERSION_ON + }; + + static const fe_code_rate_t fec_tab[] = { + FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8, + }; + + val = tda80xx_readreg(state, 0x02); + + state->status = 0; + + if (val & 0x01) /* demodulator lock */ + state->status |= FE_HAS_SIGNAL; + if (val & 0x02) /* clock recovery lock */ + state->status |= FE_HAS_CARRIER; + if (val & 0x04) /* viterbi lock */ + state->status |= FE_HAS_VITERBI; + if (val & 0x08) /* deinterleaver lock (packet sync) */ + state->status |= FE_HAS_SYNC; + if (val & 0x10) /* derandomizer lock (frame sync) */ + state->status |= FE_HAS_LOCK; + if (val & 0x20) /* frontend can not lock */ + state->status |= FE_TIMEDOUT; + + if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) { + printk("tda80xx: closing loop\n"); + tda80xx_close_loop(state); + state->afc_loop = 0; + } + + if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) { + val = tda80xx_readreg(state, 0x0e); + state->code_rate = fec_tab[val & 0x07]; + if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK)) + state->spectral_inversion = inv_tab[(val >> 7) & 0x01]; + else + state->spectral_inversion = INVERSION_AUTO; + } + else { + state->code_rate = FEC_AUTO; + } +} + +static void tda80xx_worklet(void *priv) +{ + struct tda80xx_state *state = priv; + + tda80xx_writereg(state, 0x00, 0x04); + enable_irq(state->config->irq); + + tda80xx_read_status_int(state); +} + +static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state) +{ + size_t i; + + for (i = 0; i < 100; i++) { + if (tda80xx_readreg(state, 0x02) & 0x80) + break; + msleep(10); + } +} + +static int tda8044_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + int ret; + + /* + * this function is a mess... + */ + + if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre)))) + return ret; + + tda80xx_writereg(state, 0x0f, 0x50); +#if 1 + tda80xx_writereg(state, 0x20, 0x8F); /* FIXME */ + tda80xx_writereg(state, 0x20, state->config->volt18setting); /* FIXME */ + //tda80xx_writereg(state, 0x00, 0x04); + tda80xx_writereg(state, 0x00, 0x0C); +#endif + //tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */ + + tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post)); + + if (state->config->pll_init) { + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_init(fe); + tda80xx_writereg(state, 0x1c, 0x00); + } + + return 0; +} + +static int tda8083_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab)); + + if (state->config->pll_init) { + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_init(fe); + tda80xx_writereg(state, 0x1c, 0x00); + } + + return 0; +} + + + + + + + + + + + + + + + + +static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + return tda80xx_writereg(state, 0x20, state->config->volt13setting); + case SEC_VOLTAGE_18: + return tda80xx_writereg(state, 0x20, state->config->volt18setting); + case SEC_VOLTAGE_OFF: + return tda80xx_writereg(state, 0x20, 0); + default: + return -EINVAL; + } +} + +static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_OFF: + return tda80xx_writereg(state, 0x29, 0x00); + case SEC_TONE_ON: + return tda80xx_writereg(state, 0x29, 0x80); + default: + return -EINVAL; + } +} + +static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (cmd->msg_len > 6) + return -EINVAL; + + tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3)); + tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len); + tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3)); + tda80xx_wait_diseqc_fifo(state); + + return 0; +} + +static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (cmd) { + case SEC_MINI_A: + tda80xx_writereg(state, 0x29, 0x14); + break; + case SEC_MINI_B: + tda80xx_writereg(state, 0x29, 0x1c); + break; + default: + return -EINVAL; + } + + tda80xx_wait_diseqc_fifo(state); + + return 0; +} + +static int tda80xx_sleep(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_writereg(state, 0x00, 0x02); /* enter standby */ + + return 0; +} + +static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_set(fe, p); + tda80xx_writereg(state, 0x1c, 0x00); + + tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner); + tda80xx_set_clk(state); + //tda80xx_set_scpc_freq_offset(state); + state->afc_loop = 1; + + return 0; +} + +static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (!state->config->irq) + tda80xx_read_status_int(state); + + p->inversion = state->spectral_inversion; + p->u.qpsk.fec_inner = state->code_rate; + + return 0; +} + +static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (!state->config->irq) + tda80xx_read_status_int(state); + *status = state->status; + + return 0; +} + +static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + int ret; + u8 buf[3]; + + if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf)))) + return ret; + + *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + u8 gain = ~tda80xx_readreg(state, 0x01); + *strength = (gain << 8) | gain; + + return 0; +} + +static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + u8 quality = tda80xx_readreg(state, 0x08); + *snr = (quality << 8) | quality; + + return 0; +} + +static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + *ucblocks = tda80xx_readreg(state, 0x0f); + if (*ucblocks == 0xff) + *ucblocks = 0xffffffff; + + return 0; +} + +static int tda80xx_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch(state->id) { + case ID_TDA8044: + return tda8044_init(fe); + + case ID_TDA8083: + return tda8083_init(fe); + } + return 0; +} + +static void tda80xx_release(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (state->config->irq) + free_irq(state->config->irq, &state->worklet); + + kfree(state); +} + +static struct dvb_frontend_ops tda80xx_ops; + +struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, + struct i2c_adapter* i2c) +{ + struct tda80xx_state* state = NULL; + int ret; + + /* allocate memory for the internal state */ + state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops)); + state->spectral_inversion = INVERSION_AUTO; + state->code_rate = FEC_AUTO; + state->status = 0; + state->afc_loop = 0; + + /* check if the demod is there */ + if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error; + state->id = tda80xx_readreg(state, 0x00); + + switch (state->id) { + case ID_TDA8044: + state->clk = 96000000; + printk("tda80xx: Detected tda8044\n"); + break; + + case ID_TDA8083: + state->clk = 64000000; + printk("tda80xx: Detected tda8083\n"); + break; + + default: + goto error; + } + + /* setup IRQ */ + if (state->config->irq) { + INIT_WORK(&state->worklet, tda80xx_worklet, state); + if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) { + printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret); + goto error; + } + } + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda80xx_ops = { + + .info = { + .name = "Philips TDA80xx DVB-S", + .type = FE_QPSK, + .frequency_min = 500000, + .frequency_max = 2700000, + .frequency_stepsize = 125, + .symbol_rate_min = 4500000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_MUTE_TS + }, + + .release = tda80xx_release, + + .init = tda80xx_init, + .sleep = tda80xx_sleep, + + .set_frontend = tda80xx_set_frontend, + .get_frontend = tda80xx_get_frontend, + + .read_status = tda80xx_read_status, + .read_ber = tda80xx_read_ber, + .read_signal_strength = tda80xx_read_signal_strength, + .read_snr = tda80xx_read_snr, + .read_ucblocks = tda80xx_read_ucblocks, + + .diseqc_send_master_cmd = tda80xx_send_diseqc_msg, + .diseqc_send_burst = tda80xx_send_diseqc_burst, + .set_tone = tda80xx_set_tone, + .set_voltage = tda80xx_set_voltage, +}; + +module_param(debug, int, 0644); + +MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver"); +MODULE_AUTHOR("Felix Domke, Andreas Oberritter"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda80xx_attach); diff --git a/drivers/media/dvb/frontends/tda80xx.h b/drivers/media/dvb/frontends/tda80xx.h new file mode 100644 index 000000000000..cd639a0aad55 --- /dev/null +++ b/drivers/media/dvb/frontends/tda80xx.h @@ -0,0 +1,51 @@ +/* + * tda80xx.c + * + * Philips TDA8044 / TDA8083 QPSK demodulator driver + * + * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net> + * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org> + * + * 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 TDA80XX_H +#define TDA80XX_H + +#include <linux/dvb/frontend.h> + +struct tda80xx_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* IRQ to use (0=>no IRQ used) */ + u32 irq; + + /* Register setting to use for 13v */ + u8 volt13setting; + + /* Register setting to use for 18v */ + u8 volt18setting; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, + struct i2c_adapter* i2c); + +#endif // TDA80XX_H diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c index 40f9b6910e12..3d8e7804c580 100644 --- a/drivers/media/dvb/frontends/ves1820.c +++ b/drivers/media/dvb/frontends/ves1820.c @@ -1,6 +1,5 @@ /* VES1820 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> @@ -27,70 +26,34 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/slab.h> +#include <asm/div64.h> #include "dvb_frontend.h" +#include "ves1820.h" -/* I2C_DRIVERID_VES1820 is already defined in i2c-id.h */ -#if 0 -static int debug = 0; -#define dprintk if (debug) printk -#endif - -static int verbose; struct ves1820_state { - int pwm; - u8 reg0; - int tuner; - u8 demod_addr; + struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; -/* possible ves1820 adresses */ -static u8 addr[] = { 0x61, 0x62 }; - -#if defined(CONFIG_DBOX2) -#define XIN 69600000UL -#define DISABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define HAS_INVERSION(reg0) (reg0 & 0x20) -#else /* PCI cards */ -#define XIN 57840000UL -#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) -#endif - -#define FIN (XIN >> 4) - - - -static struct dvb_frontend_info ves1820_info = { - .name = "VES1820 based DVB-C frontend", - .type = FE_QAM, - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ -#if 0 - .frequency_tolerance = ???, - .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ - .notifier_delay = ?, -#endif - .caps = FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_FEC_AUTO | - FE_CAN_INVERSION_AUTO, + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct ves1820_config* config; + + struct dvb_frontend frontend; + + /* private demodulator data */ + u8 reg0; + u8 pwm; }; + +static int verbose; + static u8 ves1820_inittab[] = { - 0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A, + 0x69, 0x6A, 0x93, 0x12, 0x12, 0x46, 0x26, 0x1A, 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, @@ -102,7 +65,7 @@ static u8 ves1820_inittab[] = { static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data) { u8 buf[] = { 0x00, reg, data }; - struct i2c_msg msg = {.addr = state->demod_addr,.flags = 0,.buf = buf,.len = 3 }; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 }; int ret; ret = i2c_transfer(state->i2c, &msg, 1); @@ -120,8 +83,8 @@ static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) u8 b0 [] = { 0x00, reg }; u8 b1 [] = { 0 }; struct i2c_msg msg[] = { - {.addr = state->demod_addr,.flags = 0,.buf = b0,.len = 2}, - {.addr = state->demod_addr,.flags = I2C_M_RD,.buf = b1,.len = 1} + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} }; int ret; @@ -134,148 +97,88 @@ static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) return b1[0]; } -static int tuner_write(struct ves1820_state *state, u8 addr, u8 data[4]) -{ - int ret; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; - - ret = i2c_transfer(state->i2c, &msg, 1); - - if (ret != 1) - printk("ves1820: %s(): i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -EREMOTEIO : 0; -} - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 62.5 kHz. - */ -static int tuner_set_tv_freq(struct ves1820_state *state, u32 freq) -{ - u32 div, ifreq; - static u8 byte3 [] = { 0x8e, 0x85 }; - int tuner_type = state->tuner; - u8 buf [4]; - - if (tuner_type == 0xff) /* PLL not reachable over i2c ... */ - return 0; - - if (strstr(state->i2c->name, "Technotrend") - || strstr(state->i2c->name, "TT-Budget")) - ifreq = 35937500; - else - ifreq = 36125000; - - div = (freq + ifreq + 31250) / 62500; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = byte3[tuner_type]; - - if (tuner_type == 1) { - buf[2] |= (div >> 10) & 0x60; - buf[3] = (freq < 174000000 ? 0x88 : freq < 470000000 ? 0x84 : 0x81); - } else { - buf[3] = (freq < 174000000 ? 0xa1 : freq < 454000000 ? 0x92 : 0x34); - } - - return tuner_write(state, addr[tuner_type], buf); -} static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion) { reg0 |= state->reg0 & 0x62; - if (INVERSION_ON == inversion) - ENABLE_INVERSION(reg0); - else if (INVERSION_OFF == inversion) - DISABLE_INVERSION(reg0); + if (INVERSION_ON == inversion) { + if (!state->config->invert) reg0 |= 0x20; + else reg0 &= ~0x20; - ves1820_writereg(state, 0x00, reg0 & 0xfe); - ves1820_writereg(state, 0x00, reg0 | 0x01); - - /** - * check lock and toggle inversion bit if required... - */ - if (INVERSION_AUTO == inversion && !(ves1820_readreg(state, 0x11) & 0x08)) { - mdelay(50); - if (!(ves1820_readreg(state, 0x11) & 0x08)) { - reg0 ^= 0x20; + } else if (INVERSION_OFF == inversion) { + + if (!state->config->invert) reg0 &= ~0x20; + else reg0 |= 0x20; + } + ves1820_writereg(state, 0x00, reg0 & 0xfe); ves1820_writereg(state, 0x00, reg0 | 0x01); - } - } state->reg0 = reg0; return 0; } -static int ves1820_init(struct ves1820_state *state) -{ - int i; - - ves1820_writereg(state, 0, 0); - -#if defined(CONFIG_DBOX2) - ves1820_inittab[2] &= ~0x08; -#endif - - for (i=0; i<53; i++) - ves1820_writereg(state, i, ves1820_inittab[i]); - - ves1820_writereg(state, 0x34, state->pwm); - - return 0; -} - static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) { s32 BDR; s32 BDRI; s16 SFIL=0; u16 NDEC = 0; - u32 tmp, ratio; + u32 ratio; + u32 fin; + u32 tmp; + u64 fptmp; + u64 fpxin; - if (symbolrate > XIN/2) - symbolrate = XIN/2; + if (symbolrate > state->config->xin / 2) + symbolrate = state->config->xin / 2; if (symbolrate < 500000) symbolrate = 500000; - if (symbolrate < XIN / 16) + if (symbolrate < state->config->xin / 16) NDEC = 1; - if (symbolrate < XIN / 32) + if (symbolrate < state->config->xin / 32) NDEC = 2; - if (symbolrate < XIN / 64) + if (symbolrate < state->config->xin / 64) NDEC = 3; - if (symbolrate < (u32) (XIN / 12.3)) + /* yeuch! */ + fpxin = state->config->xin * 10; + fptmp = fpxin; do_div(fptmp, 123); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 16)) + fptmp = fpxin; do_div(fptmp, 160); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 24.6)) + fptmp = fpxin; do_div(fptmp, 246); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 32)) + fptmp = fpxin; do_div(fptmp, 320); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 49.2)) + fptmp = fpxin; do_div(fptmp, 492); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 64)) + fptmp = fpxin; do_div(fptmp, 640); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 98.4)) + fptmp = fpxin; do_div(fptmp, 984); + if (symbolrate < fptmp); SFIL = 1; + fin = state->config->xin >> 4; symbolrate <<= NDEC; - ratio = (symbolrate << 4) / FIN; - tmp = ((symbolrate << 4) % FIN) << 8; - ratio = (ratio << 8) + tmp / FIN; - tmp = (tmp % FIN) << 8; - ratio = (ratio << 8) + (tmp + FIN/2) / FIN; + ratio = (symbolrate << 4) / fin; + tmp = ((symbolrate << 4) % fin) << 8; + ratio = (ratio << 8) + tmp / fin; + tmp = (tmp % fin) << 8; + ratio = (ratio << 8) + (tmp + fin / 2) / fin; BDR = ratio; - BDRI = (((XIN << 5) / symbolrate) + 1) / 2; + BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; if (BDRI > 0xFF) BDRI = 0xFF; @@ -295,8 +198,42 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) return 0; } -static int ves1820_set_parameters(struct ves1820_state *state, struct dvb_frontend_parameters *p) + + + + + + + + + + + + +static int ves1820_init(struct dvb_frontend* fe) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + int i; + int val; + + ves1820_writereg(state, 0, 0); + + for (i = 0; i < 53; i++) { + val = ves1820_inittab[i]; + if ((i == 2) && (state->config->selagc)) val |= 0x08; + ves1820_writereg(state, i, val); + } + + ves1820_writereg(state, 0x34, state->pwm); + + if (state->config->pll_init) state->config->pll_init(fe); + + return 0; +} + +static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; static const u8 reg0x00 [] = { 0x00, 0x04, 0x08, 0x0c, 0x10 }; static const u8 reg0x01 [] = { 140, 140, 106, 100, 92 }; static const u8 reg0x05 [] = { 135, 100, 70, 54, 38 }; @@ -307,7 +244,7 @@ static int ves1820_set_parameters(struct ves1820_state *state, struct dvb_fronte if (real_qam < 0 || real_qam > 4) return -EINVAL; - tuner_set_tv_freq(state, p->frequency); + state->config->pll_set(fe, p); ves1820_set_symbolrate(state, p->u.qam.symbol_rate); ves1820_writereg(state, 0x34, state->pwm); @@ -318,25 +255,12 @@ static int ves1820_set_parameters(struct ves1820_state *state, struct dvb_fronte ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion); - /* yes, this speeds things up: userspace reports lock in about 8 ms - instead of 500 to 1200 ms after calling FE_SET_FRONTEND. */ - mdelay(50); - return 0; } -static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct ves1820_state *state = (struct ves1820_state *) fe->data; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &ves1820_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; int sync; *status = 0; @@ -358,46 +282,59 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if (sync & 8) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: +static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 ber = ves1820_readreg(state, 0x14) | + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + + u32 _ber = ves1820_readreg(state, 0x14) | (ves1820_readreg(state, 0x15) << 8) | ((ves1820_readreg(state, 0x16) & 0x0f) << 16); - *((u32*) arg) = 10 * ber; - break; + *ber = 10 * _ber; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: + +static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + u8 gain = ves1820_readreg(state, 0x17); - *((u16*) arg) = (gain << 8) | gain; - break; + *strength = (gain << 8) | gain; + + return 0; } - case FE_READ_SNR: +static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + u8 quality = ~ves1820_readreg(state, 0x18); - *((u16*) arg) = (quality << 8) | quality; - break; + *snr = (quality << 8) | quality; + + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: - *((u32 *) arg) = ves1820_readreg(state, 0x13) & 0x7f; - if (*((u32*) arg) == 0x7f) - *((u32*) arg) = 0xffffffff; +static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + + *ucblocks = ves1820_readreg(state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + /* reset uncorrected block counter */ ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf); ves1820_writereg(state, 0x10, ves1820_inittab[0x10]); - break; - case FE_SET_FRONTEND: - return ves1820_set_parameters(state, arg); + return 0; +} - case FE_GET_FRONTEND: +static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = (struct dvb_frontend_parameters *)arg; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; int sync; s8 afc = 0; @@ -409,7 +346,12 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10); } - p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + if (!state->config->invert) { + p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF; + } else { + p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF; + } + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; p->u.qam.fec_inner = FEC_NONE; @@ -417,229 +359,112 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) p->frequency = ((p->frequency + 31250) / 62500) * 62500; if (sync & 2) p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; - break; - } - case FE_SLEEP: - ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ - ves1820_writereg(state, 0x00, 0x80); /* standby */ - break; - - case FE_INIT: - return ves1820_init(state); - - default: - return -EINVAL; - } return 0; } -static long probe_tuner(struct i2c_adapter *i2c) -{ - struct i2c_msg msg1 = {.addr = 0x61,.flags = 0,.buf = NULL,.len = 0 }; - struct i2c_msg msg2 = {.addr = 0x62,.flags = 0,.buf = NULL,.len = 0 }; - int type; - - if (i2c_transfer(i2c, &msg1, 1) == 1) { - type = 0; - printk("ves1820: setup for tuner spXXXX\n"); - } else if (i2c_transfer(i2c, &msg2, 1) == 1) { - type = 1; - printk("ves1820: setup for tuner sp5659c\n"); - } else { - type = -1; - } - - return type; -} - -static u8 read_pwm(struct i2c_adapter *i2c) +static int ves1820_sleep(struct dvb_frontend* fe) { - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg [] = { { .addr = 0x50, .flags = 0, .buf = &b, .len = 1 }, - {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} - }; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; - if ((i2c_transfer(i2c, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; + ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ + ves1820_writereg(state, 0x00, 0x80); /* standby */ - printk("ves1820: pwm=0x%02x\n", pwm); - - return pwm; + return 0; } -static long probe_demod_addr(struct i2c_adapter *i2c) +static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - u8 b [] = { 0x00, 0x1a }; - u8 id; - struct i2c_msg msg [] = { { .addr = 0x08, .flags = 0, .buf = b, .len = 2 }, - {.addr = 0x08,.flags = I2C_M_RD,.buf = &id,.len = 1} - }; - - if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70) - return msg[0].addr; - - msg[0].addr = msg[1].addr = 0x09; - if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70) - return msg[0].addr; - - return -1; -} - -static ssize_t attr_read_pwm(struct device *dev, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - return sprintf(buf, "0x%02x\n", state->pwm); + fesettings->min_delay_ms = 200; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; } -static ssize_t attr_write_pwm(struct device *dev, const char *buf, size_t count) +static void ves1820_release(struct dvb_frontend* fe) { - struct i2c_client *client = to_i2c_client(dev); - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - unsigned long pwm; - pwm = simple_strtoul(buf, NULL, 0); - state->pwm = pwm & 0xff; - return strlen(buf)+1; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + kfree(state); } -static struct device_attribute dev_attr_client_name = { - .attr = { .name = "pwm", .mode = S_IRUGO|S_IWUGO, .owner = THIS_MODULE }, - .show = &attr_read_pwm, - .store = &attr_write_pwm, -}; +static struct dvb_frontend_ops ves1820_ops; -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, + u8 pwm) { - struct i2c_client *client; - struct ves1820_state *state; - long demod_addr; - int tuner_type; - int ret; + struct ves1820_state* state = NULL; - demod_addr = probe_demod_addr(adapter); - if (demod_addr < 0) - return -ENODEV; + /* allocate memory for the internal state */ + state = (struct ves1820_state*) kmalloc(sizeof(struct ves1820_state), GFP_KERNEL); + if (state == NULL) + goto error; - tuner_type = probe_tuner(adapter); - if (tuner_type < 0) { - printk("ves1820: demod found, but unknown tuner type.\n"); - return -ENODEV; - } - - if ((state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - memset(state, 0, sizeof(*state)); - state->i2c = adapter; - state->tuner = tuner_type; - state->pwm = read_pwm(adapter); + /* setup the state */ + memcpy(&state->ops, &ves1820_ops, sizeof(struct dvb_frontend_ops)); state->reg0 = ves1820_inittab[0]; - state->demod_addr = demod_addr; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = addr[tuner_type]; - - i2c_set_clientdata(client, (void *) state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; -} + state->config = config; + state->i2c = i2c; + state->pwm = pwm; - BUG_ON(!state->dvb); + /* check if the demod is there */ + if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70) + goto error; - device_create_file(&client->dev, &dev_attr_client_name); - - ret = dvb_register_frontend(ves1820_ioctl, state->dvb, state, &ves1820_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; - } - - return 0; -} + if (verbose) + printk("ves1820: pwm=0x%02x\n", state->pwm); -static int detach_client(struct i2c_client *client) -{ - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - dvb_unregister_frontend(ves1820_ioctl, state->dvb); - device_remove_file(&client->dev, &dev_attr_client_name); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} + state->ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */ + state->ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */ -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - switch (cmd) { - case FE_REGISTER:{ - state->dvb = (struct dvb_adapter *) arg; - break; - } - case FE_UNREGISTER:{ - state->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; +error: + if (state) kfree(state); + return NULL; } - return 0; -} -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "ves1820", - .id = I2C_DRIVERID_VES1820, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, +static struct dvb_frontend_ops ves1820_ops = { + + .info = { + .name = "VLSI VES1820 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .caps = FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = ves1820_release, + + .init = ves1820_init, + .sleep = ves1820_sleep, + + .set_frontend = ves1820_set_parameters, + .get_frontend = ves1820_get_frontend, + .get_tune_settings = ves1820_get_tune_settings, + + .read_status = ves1820_read_status, + .read_ber = ves1820_read_ber, + .read_signal_strength = ves1820_read_signal_strength, + .read_snr = ves1820_read_snr, + .read_ucblocks = ves1820_read_ucblocks, }; -static struct i2c_client client_template = { - I2C_DEVNAME("ves1820"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_ves1820 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_ves1820 (void) -{ - if (i2c_del_driver(&driver)) - printk("ves1820: driver deregistration failed\n"); -} - -module_init(init_ves1820); -module_exit(exit_ves1820); - -MODULE_PARM(verbose, "i"); +module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); -MODULE_DESCRIPTION("VES1820 DVB-C frontend driver"); +MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(ves1820_attach); diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h new file mode 100644 index 000000000000..8739fec48ec2 --- /dev/null +++ b/drivers/media/dvb/frontends/ves1820.h @@ -0,0 +1,52 @@ +/* + VES1820 - Single Chip Cable Channel Receiver driver module + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef VES1820_H +#define VES1820_H + +#include <linux/dvb/frontend.h> + +#define VES1820_SELAGC_PWM 0 +#define VES1820_SELAGC_SIGNAMPERR 1 + +struct ves1820_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* does inversion need inverted? */ + u8 invert:1; + + /* SELAGC control */ + u8 selagc:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, + u8 pwm); + +#endif // VES1820_H diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c index 83d7f6a9006a..4a13dcea97d3 100644 --- a/drivers/media/dvb/frontends/ves1x93.c +++ b/drivers/media/dvb/frontends/ves1x93.c @@ -1,5 +1,5 @@ /* - Driver for VES1893 and VES1993 QPSK Frontends + Driver for VES1893 and VES1993 QPSK Demodulators Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> @@ -31,41 +31,33 @@ #include <linux/delay.h> #include "dvb_frontend.h" +#include "ves1x93.h" -static int debug = 0; -#define dprintk if (debug) printk -static int board_type = 0; -#define BOARD_SIEMENS_PCI 0 -#define BOARD_NOKIA_DBOX2 1 -#define BOARD_SAGEM_DBOX2 2 +struct ves1x93_state { -static int demod_type = 0; -#define DEMOD_VES1893 0 -#define DEMOD_VES1993 1 + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct ves1x93_config* config; -static struct dvb_frontend_info ves1x93_info = { - .name = "VES1x93", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, -/* .symbol_rate_tolerance = ???,*/ - .notifier_delay = 50, /* 1/20 s */ - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + fe_spectral_inversion_t inversion; + u8 *init_1x93_tab; + u8 *init_1x93_wtab; + u8 tab_size; + u8 demod_type; }; +static int debug = 0; +#define dprintk if (debug) printk -/** - * nokia dbox2 (ves1893) and sagem dbox2 (ves1993) - * need bit AGCR[PWMS] set to 1 - */ +#define DEMOD_VES1893 0 +#define DEMOD_VES1993 1 static u8 init_1893_tab [] = { 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4, @@ -89,10 +81,6 @@ static u8 init_1993_tab [] = { 0x00, 0x00, 0x0e, 0x80, 0x00 }; - -static u8 * init_1x93_tab; - - static u8 init_1893_wtab[] = { 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, @@ -110,21 +98,13 @@ static u8 init_1993_wtab[] = 1,1,1,0,1,1,1,1, 1,1,1,1,1 }; -struct ves1x93_state { - fe_spectral_inversion_t inversion; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - - - -static int ves1x93_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) +static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data) { u8 buf [] = { 0x00, reg, data }; - struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 3 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 }; int err; - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); return -EREMOTEIO; } @@ -133,151 +113,31 @@ static int ves1x93_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) } -static u8 ves1x93_readreg (struct i2c_adapter *i2c, u8 reg) +static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg) { int ret; u8 b0 [] = { 0x00, reg }; u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = 0x08, .flags = 0, .buf = b0, .len = 2 }, - { .addr = 0x08, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + if (ret != 2) return ret; return b1[0]; } - -static int tuner_write (struct i2c_adapter *i2c, u8 *data, u8 len) -{ - int ret; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = len }; - - ves1x93_writereg(i2c, 0x00, 0x11); - ret = i2c_transfer (i2c, &msg, 1); - ves1x93_writereg(i2c, 0x00, 0x01); - - if (ret != 1) - printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int sp5659_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u8 pwr = 0; - u8 buf[4]; - u32 div = (freq + 479500) / 125; - - if (freq > 2000000) pwr = 3; - else if (freq > 1800000) pwr = 2; - else if (freq > 1600000) pwr = 1; - else if (freq > 1200000) pwr = 0; - else if (freq >= 1100000) pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - return tuner_write (i2c, buf, sizeof(buf)); -} - - -static int tsa5059_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - int ret; - u8 buf [2]; - - freq /= 1000; - - buf[0] = (freq >> 8) & 0x7F; - buf[1] = freq & 0xFF; - - ret = tuner_write(i2c, buf, sizeof(buf)); - - return ret; -} - - -static int tuner_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - if ((demod_type == DEMOD_VES1893) && (board_type == BOARD_SIEMENS_PCI)) - return sp5659_set_tv_freq (i2c, freq); - else if (demod_type == DEMOD_VES1993) - return tsa5059_set_tv_freq (i2c, freq); - - return -EINVAL; -} - - -static int ves1x93_init (struct i2c_adapter *i2c) -{ - int i; - int size; - u8 *init_1x93_wtab; - - dprintk("%s: init chip\n", __FUNCTION__); - - switch (demod_type) { - case DEMOD_VES1893: - init_1x93_tab = init_1893_tab; - init_1x93_wtab = init_1893_wtab; - size = sizeof(init_1893_tab); - if (board_type == BOARD_NOKIA_DBOX2) - init_1x93_tab[0x05] |= 0x20; /* invert PWM */ - break; - - case DEMOD_VES1993: - init_1x93_tab = init_1993_tab; - init_1x93_wtab = init_1993_wtab; - size = sizeof(init_1993_tab); - if (board_type == BOARD_SAGEM_DBOX2) - init_1x93_tab[0x05] |= 0x20; /* invert PWM */ - break; - - default: - return -EINVAL; - } - - for (i = 0; i < size; i++) - if (init_1x93_wtab[i]) - ves1x93_writereg (i2c, i, init_1x93_tab[i]); - - if (demod_type == DEMOD_VES1993) { - if (board_type == BOARD_NOKIA_DBOX2) - tuner_write(i2c, "\x06\x5c\x83\x60", 4); - else if (board_type == BOARD_SAGEM_DBOX2) - tuner_write(i2c, "\x25\x70\x92\x40", 4); - } - - return 0; -} - - -static int ves1x93_clr_bit (struct i2c_adapter *i2c) +static int ves1x93_clr_bit (struct ves1x93_state* state) { msleep(10); - ves1x93_writereg (i2c, 0, init_1x93_tab[0] & 0xfe); - ves1x93_writereg (i2c, 0, init_1x93_tab[0]); + ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe); + ves1x93_writereg (state, 0, state->init_1x93_tab[0]); msleep(50); return 0; } - -static int ves1x93_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion) +static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion) { u8 val; @@ -300,66 +160,47 @@ static int ves1x93_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion return -EINVAL; } - return ves1x93_writereg (i2c, 0x0c, (init_1x93_tab[0x0c] & 0x3f) | val); + return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); } -static int ves1x93_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) +static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) { if (fec == FEC_AUTO) - return ves1x93_writereg (i2c, 0x0d, 0x08); + return ves1x93_writereg (state, 0x0d, 0x08); else if (fec < FEC_1_2 || fec > FEC_8_9) return -EINVAL; else - return ves1x93_writereg (i2c, 0x0d, fec - FEC_1_2); + return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); } -static fe_code_rate_t ves1x93_get_fec (struct i2c_adapter *i2c) +static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state) { - return FEC_1_2 + ((ves1x93_readreg (i2c, 0x0d) >> 4) & 0x7); + return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); } -static int ves1x93_set_symbolrate (struct i2c_adapter *i2c, u32 srate) +static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate) { u32 BDR; u32 ratio; u8 ADCONF, FCONF, FNR; u32 BDRI; u32 tmp; - u32 XIN, FIN; + u32 FIN; dprintk("%s: srate == %d\n", __FUNCTION__, (unsigned int) srate); - switch (board_type) { - case BOARD_SIEMENS_PCI: - XIN = 90100000UL; - break; - case BOARD_NOKIA_DBOX2: - if (demod_type == DEMOD_VES1893) - XIN = 91000000UL; - else if (demod_type == DEMOD_VES1993) - XIN = 96000000UL; - else - return -EINVAL; - break; - case BOARD_SAGEM_DBOX2: - XIN = 92160000UL; - break; - default: - return -EINVAL; - } - - if (srate > XIN/2) - srate = XIN/2; + if (srate > state->config->xin/2) + srate = state->config->xin/2; if (srate < 500000) srate = 500000; #define MUL (1UL<<26) - FIN = (XIN + 6000) >> 4; + FIN = (state->config->xin + 6000) >> 4; tmp = srate << 6; ratio = tmp / FIN; @@ -404,55 +245,87 @@ static int ves1x93_set_symbolrate (struct i2c_adapter *i2c, u32 srate) if (BDRI > 0xff) BDRI = 0xff; - ves1x93_writereg (i2c, 0x06, 0xff & BDR); - ves1x93_writereg (i2c, 0x07, 0xff & (BDR >> 8)); - ves1x93_writereg (i2c, 0x08, 0x0f & (BDR >> 16)); + ves1x93_writereg (state, 0x06, 0xff & BDR); + ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8)); + ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16)); - ves1x93_writereg (i2c, 0x09, BDRI); - ves1x93_writereg (i2c, 0x20, ADCONF); - ves1x93_writereg (i2c, 0x21, FCONF); + ves1x93_writereg (state, 0x09, BDRI); + ves1x93_writereg (state, 0x20, ADCONF); + ves1x93_writereg (state, 0x21, FCONF); if (srate < 6000000) - ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] | 0x80); + ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] | 0x80); else - ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] & 0x7f); + ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] & 0x7f); /* ves1993 hates this, will lose lock */ - if (demod_type != DEMOD_VES1993) - ves1x93_clr_bit (i2c); + if (state->demod_type != DEMOD_VES1993) + ves1x93_clr_bit (state); + + return 0; +} + + + + + + + + + + + + + + + +static int ves1x93_init (struct dvb_frontend* fe) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + int i; + int val; + + dprintk("%s: init chip\n", __FUNCTION__); + + for (i = 0; i < state->tab_size; i++) { + if (state->init_1x93_wtab[i]) { + val = state->init_1x93_tab[i]; + + if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */ + ves1x93_writereg (state, i, val); + } + } + + if (state->config->pll_init) { + ves1x93_writereg(state, 0x00, 0x11); + state->config->pll_init(fe); + ves1x93_writereg(state, 0x00, 0x01); + } return 0; } -static int ves1x93_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage) +static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) { + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + switch (voltage) { case SEC_VOLTAGE_13: - return ves1x93_writereg (i2c, 0x1f, 0x20); + return ves1x93_writereg (state, 0x1f, 0x20); case SEC_VOLTAGE_18: - return ves1x93_writereg (i2c, 0x1f, 0x30); + return ves1x93_writereg (state, 0x1f, 0x30); case SEC_VOLTAGE_OFF: - return ves1x93_writereg (i2c, 0x1f, 0x00); + return ves1x93_writereg (state, 0x1f, 0x00); default: return -EINVAL; } } - -static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct ves1x93_state *state = (struct ves1x93_state*) fe->data; - struct i2c_adapter *i2c = state->i2c; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &ves1x93_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = arg; - u8 sync = ves1x93_readreg (i2c, 0x0e); + u8 sync = ves1x93_readreg (state, 0x0e); /* * The ves1893 sometimes returns sync values that make no sense, @@ -466,7 +339,7 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) int maxtry = 10; /* just for safety - let's not get stuck here */ while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) { msleep(10); - sync = ves1x93_readreg (i2c, 0x0e); + sync = ves1x93_readreg (state, 0x0e); } *status = 0; @@ -486,64 +359,78 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) if ((sync & 0x1f) == 0x1f) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: + +static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - *ber = ves1x93_readreg (i2c, 0x15); - *ber |= (ves1x93_readreg (i2c, 0x16) << 8); - *ber |= ((ves1x93_readreg (i2c, 0x17) & 0x0F) << 16); + *ber = ves1x93_readreg (state, 0x15); + *ber |= (ves1x93_readreg (state, 0x16) << 8); + *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16); *ber *= 10; - break; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u8 signal = ~ves1x93_readreg (i2c, 0x0b); - *((u16*) arg) = (signal << 8) | signal; - break; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + + u8 signal = ~ves1x93_readreg (state, 0x0b); + *strength = (signal << 8) | signal; + + return 0; } - case FE_READ_SNR: +static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr) { - u8 snr = ~ves1x93_readreg (i2c, 0x1c); - *(u16*) arg = (snr << 8) | snr; - break; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + + u8 _snr = ~ves1x93_readreg (state, 0x1c); + *snr = (_snr << 8) | _snr; + + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: +static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - *(u32*) arg = ves1x93_readreg (i2c, 0x18) & 0x7f; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - if (*(u32*) arg == 0x7f) - *(u32*) arg = 0xffffffff; /* counter overflow... */ + *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f; - ves1x93_writereg (i2c, 0x18, 0x00); /* reset the counter */ - ves1x93_writereg (i2c, 0x18, 0x80); /* dto. */ - break; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; /* counter overflow... */ + + ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */ + ves1x93_writereg (state, 0x18, 0x80); /* dto. */ + + return 0; } - case FE_SET_FRONTEND: +static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; - - tuner_set_tv_freq (i2c, p->frequency); - ves1x93_set_inversion (i2c, p->inversion); - ves1x93_set_fec (i2c, p->u.qpsk.fec_inner); - ves1x93_set_symbolrate (i2c, p->u.qpsk.symbol_rate); + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + + ves1x93_writereg(state, 0x00, 0x11); + state->config->pll_set(fe, p); + ves1x93_writereg(state, 0x00, 0x01); + ves1x93_set_inversion (state, p->inversion); + ves1x93_set_fec (state, p->u.qpsk.fec_inner); + ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate); state->inversion = p->inversion; - break; + + return 0; } - case FE_GET_FRONTEND: +static int ves1x93_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; int afc; - afc = ((int)((char)(ves1x93_readreg (i2c, 0x0a) << 1)))/2; + afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2; afc = (afc * (int)(p->u.qpsk.symbol_rate/1000/8))/16; p->frequency -= afc; @@ -553,185 +440,125 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) * if auto inversion was used */ if (state->inversion == INVERSION_AUTO) - p->inversion = (ves1x93_readreg (i2c, 0x0f) & 2) ? + p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ? INVERSION_OFF : INVERSION_ON; - p->u.qpsk.fec_inner = ves1x93_get_fec (i2c); + p->u.qpsk.fec_inner = ves1x93_get_fec (state); /* XXX FIXME: timing offset !! */ - break; - } - case FE_SLEEP: - if (board_type == BOARD_SIEMENS_PCI) - ves1x93_writereg (i2c, 0x1f, 0x00); /* LNB power off */ - return ves1x93_writereg (i2c, 0x00, 0x08); - - case FE_INIT: - return ves1x93_init (i2c); - - case FE_SET_TONE: - return -EOPNOTSUPP; /* the ves1893 can generate the 22k */ - /* let's implement this when we have */ - /* a box that uses the 22K_0 pin... */ + return 0; +} - case FE_SET_VOLTAGE: - return ves1x93_set_voltage (i2c, (fe_sec_voltage_t) arg); +static int ves1x93_sleep(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - default: - return -EOPNOTSUPP; - }; + return ves1x93_writereg (state, 0x00, 0x08); +} - return 0; +static void ves1x93_release(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_client client_template; +static struct dvb_frontend_ops ves1x93_ops; -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct ves1x93_state* state; - u8 identity = ves1x93_readreg(adapter, 0x1e); - int ret; + struct ves1x93_state* state = NULL; + u8 identity; + + /* allocate memory for the internal state */ + state = (struct ves1x93_state*) kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + if (state == NULL) goto error; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops)); + state->inversion = INVERSION_OFF; + + /* check if the demod is there + identify it */ + identity = ves1x93_readreg(state, 0x1e); switch (identity) { case 0xdc: /* VES1893A rev1 */ printk("ves1x93: Detected ves1893a rev1\n"); - demod_type = DEMOD_VES1893; - ves1x93_info.name[4] = '8'; + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); break; + case 0xdd: /* VES1893A rev2 */ printk("ves1x93: Detected ves1893a rev2\n"); - demod_type = DEMOD_VES1893; - ves1x93_info.name[4] = '8'; + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); break; + case 0xde: /* VES1993 */ printk("ves1x93: Detected ves1993\n"); - demod_type = DEMOD_VES1993; - ves1x93_info.name[4] = '9'; + state->demod_type = DEMOD_VES1993; + state->init_1x93_tab = init_1993_tab; + state->init_1x93_wtab = init_1993_wtab; + state->tab_size = sizeof(init_1993_tab); break; - default: - dprintk("VES1x93 not found (identity %02x)\n", identity); - return -ENODEV; - } - if ((state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - state->inversion = INVERSION_OFF; - state->i2c = adapter; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = (0x08>>1); - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return -EFAULT; + default: + goto error; } - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(ves1x93_ioctl, state->dvb, state, - &ves1x93_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return -EFAULT; -} - - return 0; -} - -static int detach_client(struct i2c_client *client) -{ - struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client); - dvb_unregister_frontend(ves1x93_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - switch (cmd) { - case FE_REGISTER: { - state->dvb = (struct dvb_adapter*)arg; - break; - } - case FE_UNREGISTER: { - state->dvb = NULL; - break; +error: + if (state) kfree(state); + return NULL; } - default: - return -EOPNOTSUPP; -} - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "ves1x93", - .id = I2C_DRIVERID_DVBFE_VES1X93, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; -static struct i2c_client client_template = { - I2C_DEVNAME("ves1x93"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, +static struct dvb_frontend_ops ves1x93_ops = { + + .info = { + .name = "VLSI VES1x93 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = ves1x93_release, + + .init = ves1x93_init, + .sleep = ves1x93_sleep, + + .set_frontend = ves1x93_set_frontend, + .get_frontend = ves1x93_get_frontend, + + .read_status = ves1x93_read_status, + .read_ber = ves1x93_read_ber, + .read_signal_strength = ves1x93_read_signal_strength, + .read_snr = ves1x93_read_snr, + .read_ucblocks = ves1x93_read_ucblocks, + + .set_voltage = ves1x93_set_voltage, }; -static int __init init_ves1x93 (void) -{ - switch (board_type) { - case BOARD_NOKIA_DBOX2: - dprintk("%s: NOKIA_DBOX2\n", __FILE__); - break; - case BOARD_SAGEM_DBOX2: - dprintk("%s: SAGEM_DBOX2\n", __FILE__); - break; - case BOARD_SIEMENS_PCI: - dprintk("%s: SIEMENS_PCI\n", __FILE__); - break; - default: - return -EIO; - } - - return i2c_add_driver(&driver); -} - - -static void __exit exit_ves1x93 (void) -{ - if (i2c_del_driver(&driver)) - printk("vex1x93: driver deregistration failed\n"); -} - -module_init(init_ves1x93); -module_exit(exit_ves1x93); - +module_param(debug, int, 0644); -MODULE_DESCRIPTION("VES1x93 DVB-S Frontend"); +MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); MODULE_AUTHOR("Ralph Metzler"); MODULE_LICENSE("GPL"); -MODULE_PARM(debug,"i"); -MODULE_PARM(board_type,"i"); +EXPORT_SYMBOL(ves1x93_attach); diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h new file mode 100644 index 000000000000..1627e37c57a4 --- /dev/null +++ b/drivers/media/dvb/frontends/ves1x93.h @@ -0,0 +1,50 @@ +/* + Driver for VES1893 and VES1993 QPSK Demodulators + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> + Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de> + Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org> + + 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 VES1X93_H +#define VES1X93_H + +#include <linux/dvb/frontend.h> + +struct ves1x93_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* should PWM be inverted? */ + u8 invert_pwm:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c); + +#endif // VES1X93_H diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index cf84d6633ff8..edb9c730ebc9 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -4,6 +4,12 @@ config DVB_AV7110 select FW_LOADER select VIDEO_DEV select VIDEO_SAA7146_VV + select DVB_VES1820 + select DVB_VES1X93 + select DVB_STV0299 + select DVB_TDA8083 + select DVB_SP8870 + select DVB_STV0297 help Support for SAA7146 and AV7110 based DVB cards as produced by Fujitsu-Siemens, Technotrend, Hauppauge and others. @@ -53,6 +59,12 @@ config DVB_BUDGET tristate "Budget cards" depends on DVB_CORE && PCI select VIDEO_SAA7146 + select DVB_STV0299 + select DVB_VES1X93 + select DVB_VES1820 + select DVB_L64781 + select DVB_TDA8083 + select DVB_TDA10021 help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -67,6 +79,8 @@ config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_CORE && PCI select VIDEO_SAA7146 + select DVB_STV0299 + select DVB_TDA1004X help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -85,6 +99,7 @@ config DVB_BUDGET_AV depends on DVB_CORE && PCI select VIDEO_DEV select VIDEO_SAA7146_VV + select DVB_STV0299 help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -99,6 +114,9 @@ config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" depends on DVB_CORE && DVB_BUDGET select DVB_AV7110 + select DVB_STV0299 + select DVB_VES1X93 + select DVB_TDA8083 help Support for Budget Patch (full TS) modification on SAA7146+AV7110 based cards (DVB-S cards). This diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile index 043468a1d749..825ab1c38a4f 100644 --- a/drivers/media/dvb/ttpci/Makefile +++ b/drivers/media/dvb/ttpci/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_DVB_BUDGET_CI) += budget-core.o budget-ci.o ttpci-eeprom.o obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-core.o budget-patch.o ttpci-eeprom.o obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ttpci-eeprom.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ hostprogs-y := fdump diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 27b4b871d564..41ade3783a03 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -77,7 +77,7 @@ static int rgb_on; static int volume = 255; module_param_named(debug, av7110_debug, int, 0644); -MODULE_PARM_DESC(av7110_debug, "Turn on/off debugging (default:off)."); +MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); module_param(vidmode, int, 0444); MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); module_param(pids_off, int, 0444); @@ -94,7 +94,16 @@ MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); static void restart_feeds(struct av7110 *av7110); -int av7110_num = 0; +static int av7110_num = 0; + +#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ +{\ + if (fe_func != NULL) { \ + av7110_copy = fe_func; \ + fe_func = av7110_func; \ + } \ +} + static void init_av7110_av(struct av7110 *av7110) { @@ -108,7 +117,7 @@ static void init_av7110_av(struct av7110 *av7110) /* handle different card types */ /* remaining inits according to card and frontend type */ - av7110->has_analog_tuner = 0; + av7110->analog_tuner_flags = 0; av7110->current_input = 0; if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", @@ -140,10 +149,12 @@ static void init_av7110_av(struct av7110 *av7110) // switch DVB SCART on av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); - if (rgb_on) + if (rgb_on && + (av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 } + } av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); av7110_setup_irc_config(av7110, 0); @@ -183,9 +194,10 @@ static int arm_thread(void *data) av7110->arm_thread = current; - while (1) { - timeout = wait_event_interruptible_timeout(av7110->arm_wait,0 != av7110->arm_rmmod, 5*HZ); - if (-ERESTARTSYS == timeout || 0 != av7110->arm_rmmod) { + for (;;) { + timeout = wait_event_interruptible_timeout(av7110->arm_wait, + av7110->arm_rmmod, 5 * HZ); + if (-ERESTARTSYS == timeout || av7110->arm_rmmod) { /* got signal or told to quit*/ break; } @@ -259,15 +271,15 @@ void av7110_unregister_irc_handler(void (*func)(u32)) irc_handler = NULL; } -void run_handlers(unsigned long ircom) +static void run_handlers(unsigned long ircom) { if (irc_handler != NULL) (*irc_handler)((u32) ircom); } -DECLARE_TASKLET(irtask,run_handlers,0); +static DECLARE_TASKLET(irtask, run_handlers, 0); -void IR_handle(struct av7110 *av7110, u32 ircom) +static void IR_handle(struct av7110 *av7110, u32 ircom) { dprintk(4, "ircommand = %08x\n", ircom); irtask.data = (unsigned long) ircom; @@ -278,7 +290,7 @@ void IR_handle(struct av7110 *av7110, u32 ircom) * IRQ handling ****************************************************************************/ -static inline int DvbDmxFilterCallback(u8 * buffer1, size_t buffer1_len, +static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, u8 * buffer2, size_t buffer2_len, struct dvb_demux_filter *dvbdmxfilter, enum dmx_success success, @@ -346,9 +358,8 @@ static void debiirq (unsigned long data) // dprintk(4, "%p\n",av7110); print_time("debi"); - saa7146_write(av7110->dev, IER, - saa7146_read(av7110->dev, IER) & ~MASK_19 ); - saa7146_write(av7110->dev, ISR, MASK_19 ); + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); if (type==-1) { printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", @@ -484,9 +495,8 @@ static void gpioirq (unsigned long data) ARM_ClearIrq(av7110); - saa7146_write(av7110->dev, IER, - saa7146_read(av7110->dev, IER) & ~MASK_19 ); - saa7146_write(av7110->dev, ISR, MASK_19 ); + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); @@ -572,7 +582,7 @@ static void gpioirq (unsigned long data) wake_up(&cibuf->queue); iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - saa7146_wait_for_debi_done(av7110->dev); + saa7146_wait_for_debi_done(av7110->dev, 0); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19 ); if (len < 5) @@ -610,7 +620,7 @@ static void gpioirq (unsigned long data) dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - saa7146_wait_for_debi_done(av7110->dev); + saa7146_wait_for_debi_done(av7110->dev, 0); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19 ); @@ -637,7 +647,7 @@ static void gpioirq (unsigned long data) memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); av7110->bmpp+=len; av7110->bmplen-=len; - saa7146_wait_for_debi_done(av7110->dev); + saa7146_wait_for_debi_done(av7110->dev, 0); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19 ); if (len < 5) @@ -659,7 +669,7 @@ static void gpioirq (unsigned long data) case DATA_TS_RECORD: case DATA_PES_RECORD: - saa7146_wait_for_debi_done(av7110->dev); + saa7146_wait_for_debi_done(av7110->dev, 0); saa7146_write(av7110->dev, IER, saa7146_read(av7110->dev, IER) | MASK_19); irdebi(av7110, DEBISWAB, DPRAM_BASE+rxbuf, 0, len); @@ -667,7 +677,7 @@ static void gpioirq (unsigned long data) return; case DATA_DEBUG_MESSAGE: - saa7146_wait_for_debi_done(av7110->dev); + saa7146_wait_for_debi_done(av7110->dev, 0); if (!len || len>0xff) { iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); break; @@ -813,9 +823,13 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) buf[3] = mode; ret = av7110_fw_request(av7110, buf, 20, &handle, 1); - if (ret < 0) { - dprintk(1, "StartHWFilter error\n"); - return ret; + if (ret != 0 || handle >= 32) { + printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " + "ret %x handle %04x\n", + __FUNCTION__, buf[0], buf[1], buf[2], buf[3], + ret, handle); + dvbdmxfilter->hw_handle = 0xffff; + return -1; } av7110->handle2filter[handle] = dvbdmxfilter; @@ -835,8 +849,9 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) dprintk(4, "%p\n", av7110); handle = dvbdmxfilter->hw_handle; - if (handle > 32) { - dprintk(1, "StopHWFilter tried to stop invalid filter %d, filter type = %d\n", handle, dvbdmxfilter->type); + if (handle >= 32) { + printk("%s tried to stop invalid filter %04x, filter type = %x\n", + __FUNCTION__, handle, dvbdmxfilter->type); return 0; } @@ -846,11 +861,11 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) buf[1] = 1; buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); - if (ret) - dprintk(1, "StopHWFilter error\n"); - - if (answ[1] != handle) { - dprintk(2, "filter %d shutdown error :%d\n", handle, answ[1]); + if (ret != 0 || answ[1] != handle) { + printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " + "resp %04x %04x pid %d\n", + __FUNCTION__, buf[0], buf[1], buf[2], ret, + answ[0], answ[1], dvbdmxfilter->feed->pid); ret = -1; } return ret; @@ -928,7 +943,7 @@ static void dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) static int av7110_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct av7110 *av7110 = demux->priv; dprintk(4, "%p\n", av7110); @@ -986,7 +1001,7 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) static int av7110_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct av7110 *av7110 = demux->priv; dprintk(4, "%p\n", av7110); @@ -1090,71 +1105,44 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, * SEC device file operations ******************************************************************************/ -static int av7110_diseqc_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct av7110 *av7110 = fe->before_after_data; - dprintk(4, "%p\n", av7110); +static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct av7110* av7110 = (struct av7110*) fe->dvb->priv; - switch (cmd) { - case FE_SET_TONE: - switch ((fe_sec_tone_mode_t) arg) { + switch (tone) { case SEC_TONE_ON: Set22K(av7110, 1); break; case SEC_TONE_OFF: Set22K(av7110, 0); break; + default: return -EINVAL; -}; - break; - - case FE_DISEQC_SEND_MASTER_CMD: - { - struct dvb_diseqc_master_cmd *cmd = arg; - av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); - break; } - case FE_DISEQC_SEND_BURST: - av7110_diseqc_send(av7110, 0, NULL, (unsigned long) arg); - break; - - default: - return -EOPNOTSUPP; -}; - return 0; } - -static void av7110_before_after_tune (fe_status_t s, void *data) +static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) { - struct av7110 *av7110 = data; - - dprintk(4, "%p\n", av7110); + struct av7110* av7110 = fe->dvb->priv; - av7110->fe_synced = (s & FE_HAS_LOCK) ? 1 : 0; + av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); - if (av7110->playing) - return; + return 0; +} - if (down_interruptible(&av7110->pid_mutex)) - return; +static int av7110_diseqc_send_burst(struct dvb_frontend* fe, + fe_sec_mini_cmd_t minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; - if (av7110->fe_synced) { - SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], - av7110->pids[DMX_PES_TELETEXT], 0, - av7110->pids[DMX_PES_PCR]); - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - } else { - SetPIDs(av7110, 0, 0, 0, 0, 0); - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, FlushTSQueue, 0); - } + av7110_diseqc_send(av7110, 0, NULL, minicmd); - up(&av7110->pid_mutex); + return 0; } @@ -1170,15 +1158,6 @@ static int av7110_register(struct av7110 *av7110) av7110->registered=1; - dvb_add_frontend_notifier (av7110->dvb_adapter, - av7110_before_after_tune, av7110); - - /** - * init DiSEqC stuff - */ - dvb_add_frontend_ioctls (av7110->dvb_adapter, - av7110_diseqc_ioctl, NULL, av7110); - dvbdemux->priv = (void *) av7110; for (i=0; i<32; i++) @@ -1252,12 +1231,8 @@ static void dvb_unregister(struct av7110 *av7110) dvb_dmxdev_release(&av7110->dmxdev); dvb_dmx_release(&av7110->demux); - dvb_remove_frontend_notifier (av7110->dvb_adapter, - av7110_before_after_tune); - - dvb_remove_frontend_ioctls (av7110->dvb_adapter, - av7110_diseqc_ioctl, NULL); - + if (av7110->fe != NULL) + dvb_unregister_frontend(av7110->fe); dvb_unregister_device(av7110->osd_dev); av7110_av_unregister(av7110); av7110_ca_unregister(av7110); @@ -1280,6 +1255,7 @@ int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) return i2c_transfer(&av7110->i2c_adap, &msgs, 1); } +#if 0 u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) { u8 mm1[] = {0x00}; @@ -1296,6 +1272,7 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) return mm2[0]; } +#endif /**************************************************************************** * INITIALIZATION @@ -1408,28 +1385,561 @@ static int get_firmware(struct av7110* av7110) #endif -static int client_register(struct i2c_client *client) +static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (params->frequency + 479500) / 125; + + if (params->frequency > 2000000) pwr = 3; + else if (params->frequency > 1800000) pwr = 2; + else if (params->frequency > 1600000) pwr = 1; + else if (params->frequency > 1200000) pwr = 0; + else if (params->frequency >= 1100000) pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, + .pll_set = alps_bsrv2_pll_set, +}; + + +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_bsru6_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 alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + int ret; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + data[3] = 0xC4; + + if (params->frequency > 1530000) data[3] = 0xc0; + + ret = i2c_transfer (&av7110->i2c_adap, &msg, 1); + if (ret != 1) + return -EIO; + return 0; +} + +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; + + + +static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { - struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter); - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - /* fixme: check for "type" (ie. frontend type) */ - if (client->driver->command) - return client->driver->command(client, FE_REGISTER, av7110->dvb_adapter); + div = (params->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } -static int client_unregister(struct i2c_client *client) +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, + .pll_set = alps_tdbe2_pll_set, +}; + + + + +static int grundig_29504_451_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { - struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter); - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = params->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, + .pll_set = grundig_29504_451_pll_set, +}; + - /* fixme: check for "type" (ie. frontend type) */ - if (client->driver->command) - return client->driver->command(client, FE_UNREGISTER, av7110->dvb_adapter); + +static int philips_cd1516_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u32 f = params->frequency; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (f + 36125000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); + + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1820_config philips_cd1516_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, + .pll_set = philips_cd1516_pll_set, +}; + + + +static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = fe->dvb->priv; + u32 div, pwr; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (params->frequency + 36200000) / 166666; + + if (params->frequency <= 782000000) + pwr = 1; + else + pwr = 2; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85; + data[3] = pwr << 6; + + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } +static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + + return request_firmware(fw, name, &av7110->dev->pci->dev); +} + +static struct sp8870_config alps_tdlb7_config = { + + .demod_address = 0x71, + .pll_set = alps_tdlb7_pll_set, + .request_firmware = alps_tdlb7_request_firmware, +}; + + + +static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; + int i; + + div = (params->frequency + 36150000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (params->frequency < 45000000) + return -EINVAL; + else if (params->frequency < 137000000) + data[3] = 0x01; + else if (params->frequency < 403000000) + data[3] = 0x02; + else if (params->frequency < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + stv0297_enable_plli2c(fe); + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) { + printk("nexusca: pll transfer failed!\n"); + return -EIO; + } + + // wait for PLL lock + for(i=0; i< 20; i++) { + + stv0297_enable_plli2c(fe); + if (i2c_transfer (&av7110->i2c_adap, &readmsg, 1) == 1) + if (data[0] & 0x40) break; + msleep(10); + } + + return 0; +} + +static struct stv0297_config nexusca_stv0297_config = { + + .demod_address = 0x1C, + .pll_set = nexusca_stv0297_pll_set, +}; + + +static void av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) +{ + int synced = (status & FE_HAS_LOCK) ? 1 : 0; + + av7110->fe_status = status; + + if (av7110->fe_synced == synced) + return; + + av7110->fe_synced = synced; + + if (av7110->playing) + return; + + if (down_interruptible(&av7110->pid_mutex)) + return; + + if (av7110->fe_synced) { + SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], 0, + av7110->pids[DMX_PES_PCR]); + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + } else { + SetPIDs(av7110, 0, 0, 0, 0, 0); + av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + av7110_wait_msgstate(av7110, GPMQBusy); + } + + up(&av7110->pid_mutex); +} + +static int av7110_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct av7110* av7110 = fe->dvb->priv; + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_set_frontend(fe, params); +} + +static int av7110_fe_init(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_init(fe); +} + +static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct av7110* av7110 = fe->dvb->priv; + int ret; + + /* call the real implementation */ + ret = av7110->fe_read_status(fe, status); + if (ret) + return ret; + + if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) { + av7110_fe_lock_fix(av7110, *status); + } + + return 0; +} + +static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_diseqc_reset_overload(fe); +} + +static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_diseqc_send_master_cmd(fe, cmd); +} + +static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_diseqc_send_burst(fe, minicmd); +} + +static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_set_tone(fe, tone); +} + +static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_set_voltage(fe, voltage); +} + +static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned int cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + av7110_fe_lock_fix(av7110, 0); + return av7110->fe_dishnetwork_send_legacy_command(fe, cmd); +} + +static u8 read_pwm(struct av7110* av7110) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static void frontend_init(struct av7110 *av7110) +{ + if (av7110->dev->pci->subsystem_vendor == 0x110a) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) + av7110->fe = ves1820_attach(&philips_cd1516_config, + &av7110->i2c_adap, read_pwm(av7110)); + break; + } + + } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X + case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE + + // try the ALPS BSRV2 first of all + av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops->set_tone = av7110_set_tone; + break; + } + + // try the ALPS BSRU6 now + av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops->set_tone = av7110_set_tone; + break; + } + + // Try the grundig 29504-451 + av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops->set_tone = av7110_set_tone; + break; + } + + /* Try DVB-C cards */ + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: + /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ + av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, + read_pwm(av7110)); + break; + case 0x0003: + /* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, + read_pwm(av7110)); + break; + } + break; + + case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X + + // ALPS TDLB7 + av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); + break; + + case 0x0002: // Hauppauge/TT DVB-C premium rev2.X + + av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + break; + + case 0x000A: // Hauppauge/TT Nexus-CA rev1.X + + av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap, 0x7b); + if (av7110->fe) { + /* set TDA9819 into DVB mode */ + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + + /* tuner on this needs a slower i2c bus speed */ + av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + break; + } + } + } + + if (av7110->fe == NULL) { + printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + av7110->dev->pci->vendor, + av7110->dev->pci->device, + av7110->dev->pci->subsystem_vendor, + av7110->dev->pci->subsystem_device); + } else { + FE_FUNC_OVERRIDE(av7110->fe->ops->init, av7110->fe_init, av7110_fe_init); + FE_FUNC_OVERRIDE(av7110->fe->ops->read_status, av7110->fe_read_status, av7110_fe_read_status); + FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); + FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); + FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); + FE_FUNC_OVERRIDE(av7110->fe->ops->set_tone, av7110->fe_set_tone, av7110_fe_set_tone); + FE_FUNC_OVERRIDE(av7110->fe->ops->set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage;) + FE_FUNC_OVERRIDE(av7110->fe->ops->dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); + FE_FUNC_OVERRIDE(av7110->fe->ops->set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); + + if (dvb_register_frontend(av7110->dvb_adapter, av7110->fe)) { + printk("av7110: Frontend registration failed!\n"); + if (av7110->fe->ops->release) + av7110->fe->ops->release(av7110->fe); + av7110->fe = NULL; + } + } +} + static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *pci_ext) { struct av7110 *av7110 = NULL; @@ -1446,7 +1956,7 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d memset(av7110, 0, sizeof(struct av7110)); av7110->card_name = (char*)pci_ext->ext_priv; - av7110->dev=(struct saa7146_dev *)dev; + av7110->dev = dev; dev->ext_priv = av7110; if ((ret = get_firmware(av7110))) { @@ -1460,15 +1970,11 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d get recognized before the main driver is fully loaded */ saa7146_write(dev, GPIO_CTRL, 0x500000); - av7110->i2c_adap = (struct i2c_adapter) { - .client_register = client_register, - .client_unregister = client_unregister, #ifdef I2C_ADAP_CLASS_TV_DIGITAL - .class = I2C_ADAP_CLASS_TV_DIGITAL, + av7110->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL; #else - .class = I2C_CLASS_TV_DIGITAL, + av7110->i2c_adap.class = I2C_CLASS_TV_DIGITAL; #endif - }; strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ @@ -1501,7 +2007,6 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d /* locks for data transfers from/to AV7110 */ spin_lock_init (&av7110->debilock); sema_init(&av7110->dcomlock, 1); - av7110->debilock=SPIN_LOCK_UNLOCKED; av7110->debitype=-1; /* default OSD window */ @@ -1567,6 +2072,9 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d if (ret) goto err3; + av7110->dvb_adapter->priv = av7110; + frontend_init(av7110); + printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); av7110->device_initialized = 1; av7110_num++; @@ -1615,11 +2123,8 @@ static int av7110_detach (struct saa7146_dev* saa) dvb_unregister(av7110); - IER_DISABLE(saa, (MASK_19 | MASK_03)); -// saa7146_write (av7110->dev, IER, -// saa7146_read(av7110->dev, IER) & ~(MASK_19 | MASK_03)); - - saa7146_write(av7110->dev, ISR,(MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); av7110_ca_exit(av7110); av7110_av_exit(av7110); @@ -1646,7 +2151,7 @@ static int av7110_detach (struct saa7146_dev* saa) static void av7110_irq(struct saa7146_dev* dev, u32 *isr) { - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110 *av7110 = dev->ext_priv; if (*isr & MASK_19) tasklet_schedule (&av7110->debi_tasklet); @@ -1663,33 +2168,29 @@ static struct saa7146_pci_extension_data x_var = { \ .ext_priv = x_name, \ .ext = &av7110_extension } -MAKE_AV7110_INFO(fs_1_5, "Siemens cable card PCI rev1.5"); -MAKE_AV7110_INFO(fs_1_3, "Siemens/Technotrend/Hauppauge PCI rev1.3"); -MAKE_AV7110_INFO(tt_1_6, "Technotrend/Hauppauge PCI rev1.3 or 1.6"); -MAKE_AV7110_INFO(tt_2_1, "Technotrend/Hauppauge PCI rev2.1 or 2.2"); -MAKE_AV7110_INFO(tt_t, "Technotrend/Hauppauge PCI DVB-T"); -MAKE_AV7110_INFO(unkwn0, "Technotrend/Hauppauge PCI rev?(unknown0)?"); -MAKE_AV7110_INFO(unkwn1, "Technotrend/Hauppauge PCI rev?(unknown1)?"); -MAKE_AV7110_INFO(unkwn2, "Technotrend/Hauppauge PCI rev?(unknown2)?"); -MAKE_AV7110_INFO(nexus, "Technotrend/Hauppauge Nexus PCI DVB-S"); -MAKE_AV7110_INFO(dvboc11,"Octal/Technotrend DVB-C for iTV"); +MAKE_AV7110_INFO(tts_1_X, "Technotrend/Hauppauge WinTV DVB-S rev1.X"); +MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); +MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); +MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); +MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); +MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV Nexus-S rev1.3"); +MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(fs_1_5, 0x110a, 0xffff), - MAKE_EXTENSION_PCI(fs_1_5, 0x110a, 0x0000), - MAKE_EXTENSION_PCI(fs_1_3, 0x13c2, 0x0000), - MAKE_EXTENSION_PCI(unkwn0, 0x13c2, 0x1002), - MAKE_EXTENSION_PCI(tt_1_6, 0x13c2, 0x0001), - MAKE_EXTENSION_PCI(tt_2_1, 0x13c2, 0x0002), - MAKE_EXTENSION_PCI(tt_2_1, 0x13c2, 0x0003), - MAKE_EXTENSION_PCI(tt_2_1, 0x13c2, 0x0004), - MAKE_EXTENSION_PCI(tt_1_6, 0x13c2, 0x0006), - MAKE_EXTENSION_PCI(tt_t, 0x13c2, 0x0008), - MAKE_EXTENSION_PCI(tt_2_1, 0x13c2, 0x1102), - MAKE_EXTENSION_PCI(unkwn1, 0xffc2, 0x0000), - MAKE_EXTENSION_PCI(unkwn2, 0x00a1, 0x00a1), - MAKE_EXTENSION_PCI(nexus, 0x00a1, 0xa1a0), - MAKE_EXTENSION_PCI(dvboc11,0x13c2, 0x000a), + MAKE_EXTENSION_PCI(tts_1_X, 0x13c2, 0x0000), + MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), + MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), + MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), + MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), + MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), + MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), + +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0004), UNDEFINED CARD */ // Galaxis DVB PC-Sat-Carte +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0006), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-S v???? +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0008), UNDEFINED CARD */ // TT/Hauppauge WinTV DVB-T v???? +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? + { .vendor = 0, } diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index 1681154ba8f0..f25a825d7f41 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -24,9 +24,21 @@ #include "dvb_filter.h" #include "dvb_net.h" #include "dvb_ringbuffer.h" +#include "dvb_frontend.h" +#include "ves1820.h" +#include "ves1x93.h" +#include "stv0299.h" +#include "tda8083.h" +#include "sp8870.h" +#include "stv0297.h" #include <media/saa7146_vv.h> + +#define ANALOG_TUNER_VES1820 1 +#define ANALOG_TUNER_STV0297 2 +#define ANALOG_TUNER_VBI 0x100 + extern int av7110_debug; #define dprintk(level,args...) \ @@ -75,7 +87,7 @@ struct av7110 { char *card_name; /* support for analog module of dvb-c */ - int has_analog_tuner; + int analog_tuner_flags; int current_input; u32 current_freq; @@ -115,8 +127,8 @@ struct av7110 { spinlock_t debilock; struct semaphore dcomlock; - int debitype; - int debilen; + volatile int debitype; + volatile int debilen; /* Recording and playback flags */ @@ -217,6 +229,18 @@ struct av7110 { unsigned char *bin_root; unsigned long size_root; + + struct dvb_frontend* fe; + fe_status_t fe_status; + int (*fe_init)(struct dvb_frontend* fe); + int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); + int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); + int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); + int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); + int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); + int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd); + int (*fe_set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 7030633c89b1..f875efcf5de6 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -674,7 +674,7 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t } -int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) +static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) { int i; int c = 0; @@ -936,7 +936,7 @@ static ssize_t dvb_audio_write(struct file *file, const char __user *buf, return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); } -u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; +static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; #define MIN_IFRAME 400000 diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index da7e1c541020..0bd0da28dfad 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -89,20 +89,20 @@ void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) * CI link layer file ops ******************************************************************************/ -int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) +static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) { dvb_ringbuffer_init(cirbuf, vmalloc(size), size); dvb_ringbuffer_init(ciwbuf, vmalloc(size), size); return 0; } -void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) { dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); } -void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) { vfree(cirbuf->data); cirbuf->data = NULL; @@ -110,7 +110,7 @@ void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) ciwbuf->data = NULL; } -int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, +static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, int slots, ca_slot_info_t *slot) { int i; diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 6d104cb10abd..c6f0160f82f4 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -53,10 +53,14 @@ int av7110_debiwrite(struct av7110 *av7110, u32 config, { struct saa7146_dev *dev = av7110->dev; - if (count <= 0 || count > 32764) + if (count <= 0 || count > 32764) { + printk("%s: invalid count %d\n", __FUNCTION__, count); return -1; - if (saa7146_wait_for_debi_done(av7110->dev) < 0) + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done failed\n", __FUNCTION__); return -1; + } saa7146_write(dev, DEBI_CONFIG, config); if (count <= 4) /* immediate transfer */ saa7146_write(dev, DEBI_AD, val); @@ -72,10 +76,14 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) struct saa7146_dev *dev = av7110->dev; u32 result = 0; - if (count > 32764 || count <= 0) + if (count > 32764 || count <= 0) { + printk("%s: invalid count %d\n", __FUNCTION__, count); return 0; - if (saa7146_wait_for_debi_done(av7110->dev) < 0) + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #1 failed\n", __FUNCTION__); return 0; + } saa7146_write(dev, DEBI_AD, av7110->debi_bus); saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); @@ -83,7 +91,11 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) saa7146_write(dev, MC2, (2 << 16) | 2); if (count > 4) return count; - saa7146_wait_for_debi_done(av7110->dev); + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #2 failed\n", __FUNCTION__); + return 0; + } + result = saa7146_read(dev, DEBI_AD); result &= (0xffffffffUL >> ((4 - count) * 8)); return result; @@ -98,16 +110,16 @@ void av7110_reset_arm(struct av7110 *av7110) saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); /* Disable DEBI and GPIO irq */ - IER_DISABLE(av7110->dev, (MASK_19 | MASK_03)); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); msleep(30); /* the firmware needs some time to initialize */ ARM_ResetMailBox(av7110); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); - IER_ENABLE(av7110->dev, MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); av7110->arm_ready = 1; dprintk(1, "reset ARM\n"); @@ -211,8 +223,8 @@ int av7110_bootarm(struct av7110 *av7110) saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); /* Disable DEBI and GPIO irq */ - IER_DISABLE(av7110->dev, MASK_03 | MASK_19); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); + SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); /* enable DEBI */ saa7146_write(av7110->dev, MC1, 0x08800880); @@ -240,7 +252,7 @@ int av7110_bootarm(struct av7110 *av7110) mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode)); iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - if (saa7146_wait_for_debi_done(av7110->dev)) { + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " "saa7146_wait_for_debi_done() timed out\n"); return -1; @@ -258,7 +270,7 @@ int av7110_bootarm(struct av7110 *av7110) dprintk(1, "load dpram code\n"); mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); - if (saa7146_wait_for_debi_done(av7110->dev)) { + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); return -1; @@ -268,8 +280,8 @@ int av7110_bootarm(struct av7110 *av7110) //ARM_ClearIrq(av7110); ARM_ResetMailBox(av7110); - saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03)); - IER_ENABLE(av7110->dev, MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); av7110->arm_errors = 0; av7110->arm_ready = 1; @@ -281,13 +293,44 @@ int av7110_bootarm(struct av7110 *av7110) * DEBI command polling ****************************************************************************/ +int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) +{ + unsigned long start; + u32 stat; + + if (FW_VERSION(av7110->arm_app) <= 0x261c) { + /* not supported by old firmware */ + msleep(50); + return 0; + } + + /* new firmware */ + start = jiffies; + for (;;) { + if (down_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + up(&av7110->dcomlock); + if ((stat & flags) == 0) { + break; + } + if (time_after(jiffies, start + ARM_WAIT_FREE)) { + printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", + __FUNCTION__, stat & flags); + return -1; + } + msleep(1); + } + return 0; +} + int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) { int i; unsigned long start; -#ifdef COM_DEBUG + char *type = NULL; + u16 flags[2] = {0, 0}; u32 stat; -#endif // dprintk(4, "%p\n", av7110); @@ -305,6 +348,8 @@ int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) } } + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); + #ifndef _NOHANDSHAKE start = jiffies; while (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2 )) { @@ -316,14 +361,45 @@ int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) } #endif + switch ((buf[0] >> 8) & 0xff) { + case COMTYPE_PIDFILTER: + case COMTYPE_ENCODER: + case COMTYPE_REC_PLAY: + case COMTYPE_MPEGDECODER: + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQFull; + break; + case COMTYPE_OSD: + type = "OSD"; + flags[0] = OSDQOver; + flags[1] = OSDQFull; + break; + default: + break; + } + + if (type != NULL) { + /* non-immediate COMMAND type */ start = jiffies; - while (rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2) & OSDQFull) { - msleep(1); - if (time_after(jiffies, start + ARM_WAIT_OSD)) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for !OSDQFull\n", __FUNCTION__); + for (;;) { + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & flags[0]) { + printk(KERN_ERR "%s: %s QUEUE overflow\n", + __FUNCTION__, type); + return -1; + } + if ((stat & flags[1]) == 0) + break; + if (time_after(jiffies, start + ARM_WAIT_FREE)) { + printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", + __FUNCTION__, type); return -1; } + msleep(1); + } } + for (i = 2; i < length; i++) wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); @@ -334,6 +410,8 @@ int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); + #ifdef COM_DEBUG start = jiffies; while (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2 )) { @@ -956,7 +1034,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) goto out; } else { int i, len = dc->x0-dc->color+1; - u8 __user *colors = dc->data; + u8 __user *colors = (u8 *)dc->data; u8 r, g, b, blend; for (i = 0; i<len; i++) { diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index 049f23f2c03e..bf901c624682 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -65,6 +65,9 @@ enum av7110_video_output_mode #define HPQOver 0x0008 #define OSDQFull 0x0010 /* OSD Queue Full */ #define OSDQOver 0x0020 +#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ +#define HPQBusy 0x0080 +#define OSDQBusy 0x0100 /* hw section filter flags */ #define SECTION_EIT 0x01 @@ -368,6 +371,7 @@ extern int av7110_firmversion(struct av7110 *av7110); #define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) #define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) +extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); extern int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length); extern int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length); diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index db152517ed20..822ccda93a76 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -92,10 +92,8 @@ static struct v4l2_input inputs[2] = { } }; -/* for Siemens DVB-C analog module: (taken from ves1820.c) */ -static int ves1820_writereg(struct saa7146_dev *dev, u8 reg, u8 data) +static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) { - u8 addr = 0x09; u8 buf[] = { 0x00, reg, data }; struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; @@ -106,6 +104,17 @@ static int ves1820_writereg(struct saa7146_dev *dev, u8 reg, u8 data) return 0; } +static int stv0297_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) +{ + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 2 }; + + if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) + return -1; + return 0; +} + + static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) { struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; @@ -117,12 +126,7 @@ static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) return 0; } - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 62.5 kHz. - */ -static int tuner_set_tv_freq(struct saa7146_dev *dev, u32 freq) +static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) { u32 div; u8 config; @@ -151,6 +155,34 @@ static int tuner_set_tv_freq(struct saa7146_dev *dev, u32 freq) return tuner_write(dev, 0x61, buf); } +static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + u32 div; + u8 data[4]; + + div = (freq + 38900000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (freq < 45000000) + return -EINVAL; + else if (freq < 137000000) + data[3] = 0x01; + else if (freq < 403000000) + data[3] = 0x02; + else if (freq < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + stv0297_writereg(dev, 0x1C, 0x87, 0x78); + stv0297_writereg(dev, 0x1C, 0x86, 0xc8); + return tuner_write(dev, 0x63, data); +} + + static struct saa7146_standard analog_standard[]; static struct saa7146_standard dvb_standard[]; @@ -162,13 +194,12 @@ static struct v4l2_audio msp3400_v4l2_audio = { .capability = V4L2_AUDCAP_STEREO }; -int av7110_dvb_c_switch(struct saa7146_fh *fh) +static int av7110_dvb_c_switch(struct saa7146_fh *fh) { struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; struct av7110 *av7110 = (struct av7110*)dev->ext_priv; u16 adswitch; - u8 band = 0; int source, sync, err; dprintk(4, "%p\n", av7110); @@ -184,7 +215,6 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) if (0 != av7110->current_input) { adswitch = 1; - band = 0x60; /* analog band */ source = SAA7146_HPS_SOURCE_PORT_B; sync = SAA7146_HPS_SYNC_PORT_B; memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); @@ -195,9 +225,16 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9198 pin30(VIF) + } } else { adswitch = 0; - band = 0x20; /* digital band */ source = SAA7146_HPS_SOURCE_PORT_A; sync = SAA7146_HPS_SYNC_PORT_A; memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); @@ -208,15 +245,20 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + } } /* hmm, this does not do anything!? */ if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) dprintk(1, "ADSwitch error\n"); - if (ves1820_writereg(dev, 0x0f, band)) - dprintk(1, "setting band in demodulator failed.\n"); - saa7146_set_hps_source_and_sync(dev, source, sync); if (vv->ov_suspend != NULL) { @@ -227,7 +269,7 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) return 0; } -int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) +static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct saa7146_dev *dev = fh->dev; struct av7110 *av7110 = (struct av7110*) dev->ext_priv; @@ -242,7 +284,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); - if (!av7110->has_analog_tuner || t->index != 0) + if (!av7110->analog_tuner_flags || t->index != 0) return -EINVAL; memset(t, 0, sizeof(*t)); @@ -285,7 +327,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) u16 fm_matrix, src; dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; switch (t->audmode) { @@ -322,7 +364,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; memset(f, 0, sizeof(*f)); @@ -336,7 +378,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency); - if (!av7110->has_analog_tuner || av7110->current_input != 1) + if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; if (V4L2_TUNER_ANALOG_TV != f->type) @@ -346,7 +388,11 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); /* tune in desired frequency */ - tuner_set_tv_freq(dev, f->frequency); + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + ves1820_set_tv_freq(dev, f->frequency); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + stv0297_set_tv_freq(dev, f->frequency); + } av7110->current_freq = f->frequency; msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); // start stereo detection @@ -361,7 +407,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); - if (av7110->has_analog_tuner ) { + if (av7110->analog_tuner_flags) { if (i->index < 0 || i->index >= 2) return -EINVAL; } else { @@ -386,7 +432,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(2, "VIDIOC_S_INPUT: %d\n", input); - if (!av7110->has_analog_tuner ) + if (!av7110->analog_tuner_flags) return 0; if (input < 0 || input >= 2) @@ -424,7 +470,7 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) * INITIALIZATION ****************************************************************************/ -struct saa7146_extension_ioctls ioctls[] = { +static struct saa7146_extension_ioctls ioctls[] = { { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, @@ -528,7 +574,27 @@ int av7110_init_analog_module(struct av7110 *av7110) INFO(("saa7113 not accessible.\n")); } else { u8 *i = saa7113_init_regs; - av7110->has_analog_tuner = 1; + + if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { + /* Fujitsu/Siemens DVB-Cable */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; + } + + /* setup for DVB by default */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed.\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) + } + /* init the saa7113 */ while (*i != 0xff) { if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { @@ -579,7 +645,7 @@ int av7110_init_v4l(struct av7110 *av7110) /* special case DVB-C: these cards have an analog tuner plus need some special handling, so we have separate saa7146_ext_vv data for these... */ - if (av7110->has_analog_tuner) + if (av7110->analog_tuner_flags) ret = saa7146_vv_init(dev, &av7110_vv_data_c); else ret = saa7146_vv_init(dev, &av7110_vv_data_st); @@ -594,12 +660,12 @@ int av7110_init_v4l(struct av7110 *av7110) saa7146_vv_release(dev); return -ENODEV; } - if (av7110->has_analog_tuner) { + if (av7110->analog_tuner_flags) { if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) { ERR(("cannot register vbi v4l2 device. skipping.\n")); - } else - /* we use this to remember that this dvb-c card can do vbi */ - av7110->has_analog_tuner = 2; + } else { + av7110->analog_tuner_flags |= ANALOG_TUNER_VBI; + } } return 0; } @@ -607,7 +673,7 @@ int av7110_init_v4l(struct av7110 *av7110) int av7110_exit_v4l(struct av7110 *av7110) { saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); - if (2 == av7110->has_analog_tuner) + if (av7110->analog_tuner_flags & ANALOG_TUNER_VBI) saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); return 0; } @@ -684,7 +750,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = { .flags = 0, .stds = &standard[0], - .num_stds = sizeof(standard) / sizeof(struct saa7146_standard), + .num_stds = ARRAY_SIZE(standard), .std_callback = &std_callback, .ioctls = &ioctls[0], @@ -698,7 +764,7 @@ static struct saa7146_ext_vv av7110_vv_data_c = { .flags = SAA7146_USE_PORT_B_FOR_VBI, .stds = &standard[0], - .num_stds = sizeof(standard) / sizeof(struct saa7146_standard), + .num_stds = ARRAY_SIZE(standard), .std_callback = &std_callback, .ioctls = &ioctls[0], diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 48969fb949a2..ae9260094d76 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -4,6 +4,9 @@ * * Compiled from various sources by Michael Hunold <michael@mihu.de> * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> * * Copyright (C) 1999-2002 Ralph Metzler @@ -31,15 +34,34 @@ */ #include "budget.h" +#include "stv0299.h" +#include "tda10021.h" +#include "tda1004x.h" #include <media/saa7146_vv.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/spinlock.h> + +#include "dvb_ca_en50221.h" + +#define DEBICICAM 0x02420000 struct budget_av { struct budget budget; struct video_device *vd; int cur_input; int has_saa7113; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + struct dvb_ca_en50221 ca; }; +int enable_ci = 0; + + /**************************************************************************** * INITIALIZATION ****************************************************************************/ @@ -55,8 +77,10 @@ static u8 i2c_readreg (struct i2c_adapter *i2c, u8 id, u8 reg) msgs[1].flags = I2C_M_RD; msgs[0].addr = msgs[1].addr=id/2; mm1[0] = reg; - msgs[0].len = 1; msgs[1].len = 1; - msgs[0].buf = mm1; msgs[1].buf = mm2; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; i2c_transfer(i2c, msgs, 2); @@ -90,6 +114,211 @@ static int i2c_writereg (struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) return i2c_transfer(i2c, &msgs, 1); } +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 0); + + if (result == -ETIMEDOUT) + budget_av->slot_status = 0; + return result; +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 0); + + if (result == -ETIMEDOUT) + budget_av->slot_status = 0; + return result; +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); + + if (result == -ETIMEDOUT) + budget_av->slot_status = 0; + return result; +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); + + if (result == -ETIMEDOUT) + budget_av->slot_status = 0; + return result; +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_reset\n"); + + /* reset the card */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); + msleep(100); + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + msleep(2000); /* horrendous I know, but its the only way to be absolutely sure without an IRQ line! */ + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_shutdown\n"); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + budget_av->slot_status = 0; + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + return 0; +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + int cam = 0; + + if (slot != 0) + return -EINVAL; + + if (!budget_av->slot_status) { + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + udelay(1); + cam = saa7146_read(saa, PSR) & MASK_06; + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + + if (cam) + budget_av->slot_status = 1; + } else if (!open) { + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + if (ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1) == -ETIMEDOUT) + budget_av->slot_status = 0; + } + + if (budget_av->slot_status == 1) + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + + return 0; +} + +static int ciintf_init(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); + + /* setup GPIOs */ + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + + /* Reset the card */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); + msleep(50); + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + msleep(100); + + /* Enable DEBI pins */ + saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); + + /* register CI interface */ + budget_av->ca.owner = THIS_MODULE; + budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_av->ca.read_cam_control = ciintf_read_cam_control; + budget_av->ca.write_cam_control = ciintf_write_cam_control; + budget_av->ca.slot_reset = ciintf_slot_reset; + budget_av->ca.slot_shutdown = ciintf_slot_shutdown; + budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_av->ca.poll_slot_status = ciintf_poll_slot_status; + budget_av->ca.data = budget_av; + if ((result = dvb_ca_en50221_init(budget_av->budget.dvb_adapter, + &budget_av->ca, 0, 1)) != 0) { + printk("budget_av: CI interface detected, but initialisation failed.\n"); + goto error; + } + // success! + printk("ciintf_init: CI interface initialised\n"); + budget_av->budget.ci_present = 1; + return 0; + +error: + saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); + return result; +} + +static void ciintf_deinit(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + + /* release the CA device */ + dvb_ca_en50221_release(&budget_av->ca); + + /* disable DEBI pins */ + saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); +} + static const u8 saa7113_tab[] = { 0x01, 0x08, @@ -165,6 +394,385 @@ static int saa7113_setinput (struct budget_av *budget_av, int input) } +static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + 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); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct budget_av *budget_av = (struct budget_av *) fe->dvb->priv; + u32 div; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0x20; + + if (params->u.qpsk.symbol_rate < 4000000) + buf[3] |= 1; + + if (params->frequency < 1250000) + buf[3] |= 0; + else if (params->frequency < 1550000) + buf[3] |= 0x40; + else if (params->frequency < 2050000) + buf[3] |= 0x80; + else if (params->frequency < 2150000) + buf[3] |= 0xC0; + + if (i2c_transfer(&budget_av->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static u8 typhoon_cinergy1200s_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x92, + 0xff, 0xff +}; + +static struct stv0299_config typhoon_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, + .pll_set = philips_su1278_ty_ci_pll_set, +}; + + +static struct stv0299_config cinergy_1200s_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, + .pll_set = philips_su1278_ty_ci_pll_set, +}; + + +static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + +#define TUNER_MUL 62500 + + u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + buf[3] = (params->frequency < 174500000 ? 0xa1 : + params->frequency < 454000000 ? 0x92 : 0x34); + + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda10021_config philips_cu1216_config = { + .demod_address = 0x0c, + .pll_set = philips_cu1216_pll_set, +}; + + + + +static int philips_tu1216_pll_init(struct dvb_frontend *fe) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; + + // setup PLL configuration + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + return 0; +} + +static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = + sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = params->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (params->frequency < 49000000) + return -EINVAL; + else if (params->frequency < 161000000) + band = 1; + else if (params->frequency < 444000000) + band = 2; + else if (params->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + filter = 0; + break; + + case BANDWIDTH_7_MHZ: + filter = 0; + break; + + case BANDWIDTH_8_MHZ: + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000; + + // setup tuner buffer + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tu1216_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + +struct tda1004x_config philips_tu1216_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 1, + .pll_init = philips_tu1216_pll_init, + .pll_set = philips_tu1216_pll_set, + .request_firmware = philips_tu1216_request_firmware, +}; + + + + +static u8 read_pwm(struct budget_av *budget_av) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, + {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} + }; + + if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + + +static void frontend_init(struct budget_av *budget_av) +{ + switch (budget_av->budget.dev->pci->subsystem_device) { + case 0x4f56: // Typhoon/KNC1 DVB-S budget (stv0299/Philips SU1278(tsa5059)) + budget_av->budget.dvb_frontend = + stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); + if (budget_av->budget.dvb_frontend != NULL) { + break; + } + break; + + case 0x0020: // KNC1 DVB-C budget (tda10021/Philips CU1216(tua6034)) + budget_av->budget.dvb_frontend = + tda10021_attach(&philips_cu1216_config, + &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (budget_av->budget.dvb_frontend != NULL) { + break; + } + break; + + case 0x0030: // KNC1 DVB-T budget (tda10046/Philips TU1216(tda6651tt)) + budget_av->budget.dvb_frontend = + tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (budget_av->budget.dvb_frontend != NULL) { + break; + } + break; + + case 0x1154: // TerraTec Cinergy 1200 DVB-S (stv0299/Philips SU1278(tsa5059)) + budget_av->budget.dvb_frontend = + stv0299_attach(&cinergy_1200s_config, &budget_av->budget.i2c_adap); + if (budget_av->budget.dvb_frontend != NULL) { + break; + } + break; + + case 0x1156: // Terratec Cinergy 1200 DVB-C (tda10021/Philips CU1216(tua6034)) + budget_av->budget.dvb_frontend = + tda10021_attach(&philips_cu1216_config, + &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (budget_av->budget.dvb_frontend) { + break; + } + break; + + case 0x1157: // Terratec Cinergy 1200 DVB-T (tda10046/Philips TU1216(tda6651tt)) + budget_av->budget.dvb_frontend = + tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (budget_av->budget.dvb_frontend) { + break; + } + break; + } + + if (budget_av->budget.dvb_frontend == NULL) { + printk("budget_av: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + budget_av->budget.dev->pci->vendor, + budget_av->budget.dev->pci->device, + budget_av->budget.dev->pci->subsystem_vendor, + budget_av->budget.dev->pci->subsystem_device); + } else { + if (dvb_register_frontend + (budget_av->budget.dvb_adapter, budget_av->budget.dvb_frontend)) { + printk("budget-av: Frontend registration failed!\n"); + if (budget_av->budget.dvb_frontend->ops->release) + budget_av->budget.dvb_frontend->ops->release(budget_av->budget.dvb_frontend); + budget_av->budget.dvb_frontend = NULL; + } + } +} + + +static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); +} + static int budget_av_detach (struct saa7146_dev *dev) { struct budget_av *budget_av = (struct budget_av*) dev->ext_priv; @@ -180,6 +788,11 @@ static int budget_av_detach (struct saa7146_dev *dev) saa7146_unregister_device (&budget_av->vd, dev); } + if (budget_av->budget.ci_present) + ciintf_deinit(budget_av); + + if (budget_av->budget.dvb_frontend != NULL) + dvb_unregister_frontend(budget_av->budget.dvb_frontend); err = ttpci_budget_deinit (&budget_av->budget); kfree (budget_av); @@ -189,28 +802,24 @@ static int budget_av_detach (struct saa7146_dev *dev) static struct saa7146_ext_vv vv_data; -static int budget_av_attach (struct saa7146_dev* dev, - struct saa7146_pci_extension_data *info) +static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { struct budget_av *budget_av; - struct budget_info *bi = info->ext_priv; u8 *mac; int err; dprintk(2, "dev: %p\n", dev); - if (bi->type != BUDGET_KNC1 && bi->type != BUDGET_CIN1200) { - return -ENODEV; - } - if (!(budget_av = kmalloc(sizeof(struct budget_av), GFP_KERNEL))) return -ENOMEM; memset(budget_av, 0, sizeof(struct budget_av)); + budget_av->budget.ci_present = 0; + dev->ext_priv = budget_av; - if ((err = ttpci_budget_init(&budget_av->budget, dev, info))) { + if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) { kfree(budget_av); return err; } @@ -220,8 +829,6 @@ static int budget_av_attach (struct saa7146_dev* dev, saa7146_write(dev, DD1_INIT, 0x07000600); saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); - //test_knc_ci(av7110); - saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI); msleep(500); @@ -234,9 +841,7 @@ static int budget_av_attach (struct saa7146_dev* dev, return err; } - if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", - VFL_TYPE_GRABBER))) - { + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { /* fixme: proper cleanup here */ ERR(("cannot register capture v4l2 device.\n")); return err; @@ -261,15 +866,20 @@ static int budget_av_attach (struct saa7146_dev* dev, printk("KNC1-%d: Could not read MAC from KNC1 card\n", budget_av->budget.dvb_adapter->num); memset(mac, 0, 6); - } - else + } else { printk("KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", budget_av->budget.dvb_adapter->num, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return 0; } + budget_av->budget.dvb_adapter->priv = budget_av; + frontend_init(budget_av); + + if (enable_ci) + ciintf_init(budget_av); + return 0; +} #define KNC1_INPUTS 2 static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { @@ -290,12 +900,9 @@ static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct saa7146_dev *dev = fh->dev; struct budget_av *budget_av = (struct budget_av*) dev->ext_priv; -/* - struct saa7146_vv *vv = dev->vv_data; -*/ + switch(cmd) { - case VIDIOC_ENUMINPUT: - { + case VIDIOC_ENUMINPUT:{ struct v4l2_input *i = arg; dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index); @@ -305,8 +912,7 @@ static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); return 0; } - case VIDIOC_G_INPUT: - { + case VIDIOC_G_INPUT:{ int *input = (int *)arg; *input = budget_av->cur_input; @@ -314,8 +920,7 @@ static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) dprintk(1, "VIDIOC_G_INPUT %d.\n", *input); return 0; } - case VIDIOC_S_INPUT: - { + case VIDIOC_S_INPUT:{ int input = *(int *)arg; dprintk(1, "VIDIOC_S_INPUT %d.\n", input); return saa7113_setinput (budget_av, input); @@ -327,17 +932,15 @@ static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) } static struct saa7146_standard standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL, + {.name = "PAL",.id = V4L2_STD_PAL, .v_offset = 0x17, .v_field = 288, .h_offset = 0x14, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768 - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, + .v_max_out = 576,.h_max_out = 768 }, + + {.name = "NTSC",.id = V4L2_STD_NTSC, .v_offset = 0x16, .v_field = 240, .h_offset = 0x06, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } + .v_max_out = 480,.h_max_out = 640, }, }; static struct saa7146_ext_vv vv_data = { @@ -350,17 +953,22 @@ static struct saa7146_ext_vv vv_data = { .ioctl = av_ioctl, }; - - static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(knc1, "KNC1 DVB-S", BUDGET_KNC1); -MAKE_BUDGET_INFO(cin1200, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200); +MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); +MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); static struct pci_device_id pci_tbl [] = { - MAKE_EXTENSION_PCI(knc1, 0x1131, 0x4f56), - MAKE_EXTENSION_PCI(cin1200, 0x153b, 0x1154), + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), + MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), + MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), + MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), + MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), { .vendor = 0, } @@ -377,7 +985,7 @@ static struct saa7146_extension budget_extension = { .detach = budget_av_detach, .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, + .irq_func = budget_av_irq, }; static int __init budget_av_init(void) @@ -396,5 +1004,6 @@ module_exit(budget_av_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); MODULE_DESCRIPTION("driver for the SAA7146 based so-called " - "budget PCI DVB w/ analog input (e.g. the KNC cards)"); - + "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); +module_param_named(enable_ci, enable_ci, int, 0644); +MODULE_PARM_DESC(enable_ci, "Turn on/off CI module (default:off)."); diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 9cb30667ba3b..898a8bf3b466 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -39,6 +39,8 @@ #include <linux/spinlock.h> #include "dvb_ca_en50221.h" +#include "stv0299.h" +#include "tda1004x.h" #define DEBIADDR_IR 0x1234 #define DEBIADDR_CICONTROL 0x0000 @@ -64,72 +66,11 @@ struct budget_ci { struct input_dev input_dev; struct tasklet_struct msp430_irq_tasklet; struct tasklet_struct ciintf_irq_tasklet; - spinlock_t debilock; int slot_status; struct dvb_ca_en50221 ca; char ir_dev_name[50]; }; -static u32 budget_debiread (struct budget_ci* budget_ci, u32 config, int addr, int count) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - u32 result = 0; - unsigned long flags; - - if (count > 4 || count <= 0) - return 0; - - spin_lock_irqsave(&budget_ci->debilock, flags); - - if (saa7146_wait_for_debi_done(saa) < 0) { - spin_unlock_irqrestore(&budget_ci->debilock, flags); - return 0; - } - - saa7146_write (saa, DEBI_COMMAND, - (count << 17) | 0x10000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, MC2, (2 << 16) | 2); - - saa7146_wait_for_debi_done(saa); - - result = saa7146_read(saa, 0x88); - result &= (0xffffffffUL >> ((4 - count) * 8)); - - spin_unlock_irqrestore(&budget_ci->debilock, flags); - return result; -} - -static u8 budget_debiwrite (struct budget_ci* budget_ci, u32 config, int addr, int count, u32 value) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - unsigned long flags; - - if (count > 4 || count <= 0) - return 0; - - spin_lock_irqsave(&budget_ci->debilock, flags); - - if (saa7146_wait_for_debi_done(saa) < 0) { - spin_unlock_irqrestore(&budget_ci->debilock, flags); - return 0; - } - - saa7146_write (saa, DEBI_COMMAND, - (count << 17) | 0x00000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, DEBI_AD, value); - saa7146_write(saa, MC2, (2 << 16) | 2); - - saa7146_wait_for_debi_done(saa); - - spin_unlock_irqrestore(&budget_ci->debilock, flags); - return 0; -} - - /* from reading the following remotes: Zenith Universal 7 / TV Mode 807 / VCR Mode 837 Hauppauge (from NOVA-CI-s box product) @@ -206,7 +147,8 @@ static void msp430_ir_interrupt (unsigned long data) { struct budget_ci *budget_ci = (struct budget_ci*) data; struct input_dev *dev = &budget_ci->input_dev; - unsigned int code = budget_debiread(budget_ci, DEBINOSWAP, DEBIADDR_IR, 2) >> 8; + unsigned int code = + ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; if (code & 0x40) { code &= 0x3f; @@ -221,8 +163,7 @@ static void msp430_ir_interrupt (unsigned long data) } if (!key_map[code]) { - printk ("DVB (%s): no key for %02x!\n", - __FUNCTION__, code); + printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code); return; } @@ -281,79 +222,98 @@ static void msp430_ir_deinit (struct budget_ci *budget_ci) input_unregister_device(dev); } -static int ciintf_read_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address) { +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; - return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1); + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); } -static int ciintf_write_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address, u8 value) { +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; - return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1, value); + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); } -static int ciintf_read_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address) { +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; - return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1); + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, 1, 0); } -static int ciintf_write_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value) { +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; - return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1, value); + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, value, 1, 0); } -static int ciintf_slot_reset(struct dvb_ca_en50221* ca, int slot) { +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; // trigger on RISING edge during reset so we know when READY is re-asserted saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); budget_ci->slot_status = SLOTSTATUS_RESET; - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); msleep(1); - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); return 0; } -static int ciintf_slot_shutdown(struct dvb_ca_en50221* ca, int slot) { +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; - if (slot != 0) return -EINVAL; + if (slot != 0) + return -EINVAL; saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); return 0; } -static int ciintf_slot_ts_enable(struct dvb_ca_en50221* ca, int slot) { +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ struct budget_ci* budget_ci = (struct budget_ci*) ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; int tmp; - if (slot != 0) return -EINVAL; - + if (slot != 0) + return -EINVAL; saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); - tmp = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1); - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, tmp | CICONTROL_ENABLETS); + tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + tmp | CICONTROL_ENABLETS, 1, 0); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); return 0; @@ -367,10 +327,11 @@ static void ciintf_interrupt (unsigned long data) unsigned int flags; // ensure we don't get spurious IRQs during initialisation - if (!budget_ci->budget.ci_present) return; + if (!budget_ci->budget.ci_present) + return; // read the CAM status - flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1); + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); if (flags & CICONTROL_CAMDETECT) { // GPIO should be set to trigger on falling edge if a CAM is present @@ -379,7 +340,8 @@ static void ciintf_interrupt (unsigned long data) if (budget_ci->slot_status & SLOTSTATUS_NONE) { // CAM insertion IRQ budget_ci->slot_status = SLOTSTATUS_PRESENT; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_INSERTED); + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { // CAM ready (reset completed) @@ -401,7 +363,8 @@ static void ciintf_interrupt (unsigned long data) if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { // CAM removal IRQ budget_ci->slot_status = SLOTSTATUS_NONE; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_REMOVED); + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); } } } @@ -418,18 +381,18 @@ static int ciintf_init(struct budget_ci* budget_ci) saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); // test if it is there - if ((budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CIVERSION, 1) & 0xa0) != 0xa0) { + if ((ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0) & 0xa0) != 0xa0) { result = -ENODEV; goto error; } - // determine whether a CAM is present or not - flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1); + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); budget_ci->slot_status = SLOTSTATUS_NONE; - if (flags & CICONTROL_CAMDETECT) budget_ci->slot_status = SLOTSTATUS_PRESENT; - + if (flags & CICONTROL_CAMDETECT) + budget_ci->slot_status = SLOTSTATUS_PRESENT; // register CI interface + budget_ci->ca.owner = THIS_MODULE; budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; budget_ci->ca.read_cam_control = ciintf_read_cam_control; @@ -442,8 +405,7 @@ static int ciintf_init(struct budget_ci* budget_ci) &budget_ci->ca, DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | DVB_CA_EN50221_FLAG_IRQ_FR | - DVB_CA_EN50221_FLAG_IRQ_DA, - 1)) != 0) { + DVB_CA_EN50221_FLAG_IRQ_DA, 1)) != 0) { printk("budget_ci: CI interface detected, but initialisation failed.\n"); goto error; } @@ -456,7 +418,8 @@ static int ciintf_init(struct budget_ci* budget_ci) saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); } saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03); - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); // success! printk("budget_ci: CI interface initialised\n"); @@ -464,7 +427,8 @@ static int ciintf_init(struct budget_ci* budget_ci) // forge a fake CI IRQ so the CAM state is setup correctly flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; - if (budget_ci->slot_status != SLOTSTATUS_NONE) flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; + if (budget_ci->slot_status != SLOTSTATUS_NONE) + flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); return 0; @@ -482,9 +446,10 @@ static void ciintf_deinit(struct budget_ci* budget_ci) saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ciintf_irq_tasklet); - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); msleep(1); - budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); // disable TS data stream to CI interface saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); @@ -513,9 +478,426 @@ static void budget_ci_irq (struct saa7146_dev *dev, u32 *isr) } +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_bsru6_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 alps_bsru6_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0xC4; + + if (params->frequency > 1530000) + buf[3] = 0xc0; + + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; + + + + +static u8 philips_su1278_tt_inittab[] = { + 0x01, 0x0f, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x5b, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x02, + 0x09, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0f, 0x14, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x97, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + stv0299_writereg(fe, 0x0e, 0x44); + if (srate >= 10000000) { + stv0299_writereg(fe, 0x13, 0x97); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x17, 0x8c); + stv0299_writereg(fe, 0x1a, 0xfe); + stv0299_writereg(fe, 0x1c, 0x7f); + stv0299_writereg(fe, 0x2d, 0x09); + } else { + stv0299_writereg(fe, 0x13, 0x99); + stv0299_writereg(fe, 0x14, 0x8d); + stv0299_writereg(fe, 0x15, 0xce); + stv0299_writereg(fe, 0x17, 0x43); + stv0299_writereg(fe, 0x1a, 0x1d); + stv0299_writereg(fe, 0x1c, 0x12); + stv0299_writereg(fe, 0x2d, 0x05); + } + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x15, 0xc9); + + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u32 div; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; + + div = (params->frequency + (500 - 1)) / 500; // round correctly + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; + buf[3] = 0x20; + + if (params->u.qpsk.symbol_rate < 4000000) + buf[3] |= 1; + + if (params->frequency < 1250000) + buf[3] |= 0; + else if (params->frequency < 1550000) + buf[3] |= 0x40; + else if (params->frequency < 2050000) + buf[3] |= 0x80; + else if (params->frequency < 2150000) + buf[3] |= 0xC0; + + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct stv0299_config philips_su1278_tt_config = { + + .demod_address = 0x68, + .inittab = philips_su1278_tt_inittab, + .mclk = 64000000UL, + .invert = 0, + .enhanced_tuning = 1, + .skip_reinit = 1, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 50, + .set_symbol_rate = philips_su1278_tt_set_symbol_rate, + .pll_set = philips_su1278_tt_pll_set, +}; + + -static int budget_ci_attach (struct saa7146_dev* dev, - struct saa7146_pci_extension_data *info) +static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = td1316_init,.len = + sizeof(td1316_init) }; + + // setup PLL configuration + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = params->frequency + 36130000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (params->frequency < 49000000) + return -EINVAL; + else if (params->frequency < 159000000) + band = 1; + else if (params->frequency < 444000000) + band = 2; + else if (params->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter and TDA9889 + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + tda1004x_write_byte(fe, 0x0C, 0x14); + filter = 0; + break; + + case BANDWIDTH_7_MHZ: + tda1004x_write_byte(fe, 0x0C, 0x80); + filter = 0; + break; + + case BANDWIDTH_8_MHZ: + tda1004x_write_byte(fe, 0x0C, 0x14); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + + return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 0, + .invert_oclk = 0, + .pll_init = philips_tdm1316l_pll_init, + .pll_set = philips_tdm1316l_pll_set, + .request_firmware = philips_tdm1316l_request_firmware, +}; + + + +static void frontend_init(struct budget_ci *budget_ci) +{ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) + budget_ci->budget.dvb_frontend = + stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + break; + } + break; + + case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) + budget_ci->budget.dvb_frontend = + stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + break; + } + break; + + case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) + budget_ci->budget.dvb_frontend = + tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + break; + } + break; + } + + if (budget_ci->budget.dvb_frontend == NULL) { + printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + budget_ci->budget.dev->pci->vendor, + budget_ci->budget.dev->pci->device, + budget_ci->budget.dev->pci->subsystem_vendor, + budget_ci->budget.dev->pci->subsystem_device); + } else { + if (dvb_register_frontend + (budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { + printk("budget-ci: Frontend registration failed!\n"); + if (budget_ci->budget.dvb_frontend->ops->release) + budget_ci->budget.dvb_frontend->ops->release(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } +} + +static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { struct budget_ci *budget_ci; int err; @@ -525,12 +907,11 @@ static int budget_ci_attach (struct saa7146_dev* dev, dprintk(2, "budget_ci: %p\n", budget_ci); - spin_lock_init(&budget_ci->debilock); budget_ci->budget.ci_present = 0; dev->ext_priv = budget_ci; - if ((err = ttpci_budget_init (&budget_ci->budget, dev, info))) { + if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) { kfree (budget_ci); return err; } @@ -542,6 +923,9 @@ static int budget_ci_attach (struct saa7146_dev* dev, ciintf_init(budget_ci); + budget_ci->budget.dvb_adapter->priv = budget_ci; + frontend_init(budget_ci); + return 0; } @@ -553,8 +937,10 @@ static int budget_ci_detach (struct saa7146_dev* dev) struct saa7146_dev *saa = budget_ci->budget.dev; int err; - if (budget_ci->budget.ci_present) ciintf_deinit(budget_ci); - + if (budget_ci->budget.ci_present) + ciintf_deinit(budget_ci); + if (budget_ci->budget.dvb_frontend) + dvb_unregister_frontend(budget_ci->budget.dvb_frontend); err = ttpci_budget_deinit (&budget_ci->budget); tasklet_kill (&budget_ci->msp430_irq_tasklet); diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index 0a8c897b494e..bba2822e9d6b 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -56,7 +56,7 @@ static int stop_ts_capture(struct budget *budget) return budget->feeding; saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off - IER_DISABLE(budget->dev, MASK_10); + SAA7146_IER_DISABLE(budget->dev, MASK_10); return 0; } @@ -74,8 +74,7 @@ static int start_ts_capture (struct budget *budget) memset(budget->grabbing, 0x00, TS_HEIGHT*TS_WIDTH); - saa7146_write(dev, PCI_BT_V1, 0x001c0000 | - (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); budget->tsf=0xff; budget->ttbp=0; @@ -125,7 +124,7 @@ static int start_ts_capture (struct budget *budget) saa7146_write(dev, MC2, (MASK_04 | MASK_20)); saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on - IER_ENABLE(budget->dev, MASK_10); // VPE + SAA7146_IER_ENABLE(budget->dev, MASK_10); // VPE return ++budget->feeding; } @@ -150,14 +149,87 @@ static void vpeirq (unsigned long data) return; if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, - mem+olddma, (newdma-olddma) / 188); + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188); } else { /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, - mem+olddma, (TS_BUFLEN-olddma) / 188); - dvb_dmx_swfilter_packets(&budget->demux, - mem, newdma / 188); + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); + } } + + +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result = 0; + unsigned long flags = 0; + + if (count > 4 || count <= 0) + return 0; + + if (uselocks) + spin_lock_irqsave(&budget->debilock, flags); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, MC2, (2 << 16) | 2); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + result = saa7146_read(saa, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + + return result; +} + +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + unsigned long flags = 0; + int result; + + if (count > 4 || count <= 0) + return 0; + + if (uselocks) + spin_lock_irqsave(&budget->debilock, flags); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, DEBI_AD, value); + saa7146_write(saa, MC2, (2 << 16) | 2); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return 0; } @@ -231,13 +303,11 @@ static int budget_register(struct budget *budget) return ret; budget->mem_frontend.source = DMX_MEMORY_FE; - ret=dvbdemux->dmx.add_frontend (&dvbdemux->dmx, - &budget->mem_frontend); + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); if (ret<0) return ret; - ret=dvbdemux->dmx.connect_frontend (&dvbdemux->dmx, - &budget->hw_frontend); + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) return ret; @@ -263,30 +333,9 @@ static void budget_unregister(struct budget *budget) dvb_dmx_release(&budget->demux); } -/* fixme: can this be unified among all saa7146 based dvb cards? */ -static int client_register(struct i2c_client *client) -{ - struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter); - struct budget *budget = (struct budget*)dev->ext_priv; - - if (client->driver->command) - return client->driver->command(client, FE_REGISTER, budget->dvb_adapter); - return 0; -} - -static int client_unregister(struct i2c_client *client) -{ - struct saa7146_dev *dev = (struct saa7146_dev*)i2c_get_adapdata(client->adapter); - struct budget *budget = (struct budget*)dev->ext_priv; - - if (client->driver->command) - return client->driver->command(client, FE_UNREGISTER, budget->dvb_adapter); - return 0; -} - -int ttpci_budget_init (struct budget *budget, - struct saa7146_dev* dev, - struct saa7146_pci_extension_data *info) +int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner) { int length = TS_WIDTH*TS_HEIGHT; int ret = 0; @@ -299,7 +348,7 @@ int ttpci_budget_init (struct budget *budget, budget->card = bi; budget->dev = (struct saa7146_dev *) dev; - dvb_register_adapter(&budget->dvb_adapter, budget->card->name, THIS_MODULE); + dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner); /* set dd1 stream a & b */ saa7146_write(dev, DD1_STREAM_B, 0x00000000); @@ -313,21 +362,18 @@ int ttpci_budget_init (struct budget *budget, else budget->video_port = BUDGET_VIDEO_PORTA; spin_lock_init(&budget->feedlock); + spin_lock_init(&budget->debilock); /* the Siemens DVB needs this if you want to have the i2c chips get recognized before the main driver is loaded */ if (bi->type != BUDGET_FS_ACTIVY) saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ - budget->i2c_adap = (struct i2c_adapter) { - .client_register = client_register, - .client_unregister = client_unregister, #ifdef I2C_ADAP_CLASS_TV_DIGITAL - .class = I2C_ADAP_CLASS_TV_DIGITAL, + budget->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL; #else - .class = I2C_CLASS_TV_DIGITAL, + budget->i2c_adap.class = I2C_CLASS_TV_DIGITAL; #endif - }; strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); @@ -341,7 +387,8 @@ int ttpci_budget_init (struct budget *budget, ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter->proposed_mac); - if( NULL == (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci,length,&budget->pt))) { + if (NULL == + (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) { ret = -ENOMEM; goto err; } @@ -420,8 +467,8 @@ void ttpci_budget_set_video_port(struct saa7146_dev* dev, int video_port) spin_unlock(&budget->feedlock); } - - +EXPORT_SYMBOL_GPL(ttpci_budget_debiread); +EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); EXPORT_SYMBOL_GPL(ttpci_budget_init); EXPORT_SYMBOL_GPL(ttpci_budget_deinit); EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c index 2c1a6f6a6c6e..7a9c68291fa6 100644 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ b/drivers/media/dvb/ttpci/budget-patch.c @@ -33,6 +33,9 @@ #include "av7110.h" #include "av7110_hw.h" #include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "tda8083.h" #define budget_patch budget @@ -121,16 +124,11 @@ static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, return 0; } - -int budget_patch_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int budget_patch_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - struct budget_patch *budget = fe->before_after_data; - - dprintk(2, "budget: %p\n", budget); + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - switch (cmd) { - case FE_SET_TONE: - switch ((fe_sec_tone_mode_t) arg) { + switch (tone) { case SEC_TONE_ON: av7110_set22k (budget, 1); break; @@ -140,27 +138,232 @@ int budget_patch_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void * default: return -EINVAL; } - break; - case FE_DISEQC_SEND_MASTER_CMD: + return 0; +} + +static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) { - struct dvb_diseqc_master_cmd *cmd = arg; + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); - break; + + return 0; } - case FE_DISEQC_SEND_BURST: - av7110_send_diseqc_msg (budget, 0, NULL, (int) (long) arg); - break; +static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + + av7110_send_diseqc_msg (budget, 0, NULL, minicmd); - default: - return -EOPNOTSUPP; + return 0; } +static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (params->frequency + 479500) / 125; + + if (params->frequency > 2000000) pwr = 3; + else if (params->frequency > 1800000) pwr = 2; + else if (params->frequency > 1600000) pwr = 1; + else if (params->frequency > 1200000) pwr = 0; + else if (params->frequency >= 1100000) pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, + .pll_set = alps_bsrv2_pll_set, +}; + +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_bsru6_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 alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + data[3] = 0xC4; + + if (params->frequency > 1530000) data[3] = 0xc0; + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; + +static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = params->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, + .pll_set = grundig_29504_451_pll_set, +}; + +static void frontend_init(struct budget_patch* budget) +{ + switch(budget->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + + // try the ALPS BSRV2 first of all + budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; + budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst; + budget->dvb_frontend->ops->set_tone = budget_patch_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; + budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst; + budget->dvb_frontend->ops->set_tone = budget_patch_set_tone; + break; + } + + // Try the grundig 29504-451 + budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; + budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst; + budget->dvb_frontend->ops->set_tone = budget_patch_set_tone; + break; + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(budget->dvb_adapter, budget->dvb_frontend)) { + printk("budget-av: Frontend registration failed!\n"); + if (budget->dvb_frontend->ops->release) + budget->dvb_frontend->ops->release(budget->dvb_frontend); + budget->dvb_frontend = NULL; + } + } +} static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) { @@ -173,7 +376,7 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte dprintk(2, "budget: %p\n", budget); - if ((err = ttpci_budget_init (budget, dev, info))) { + if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { kfree (budget); return err; } @@ -221,11 +424,11 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte // Enable RPS1 (rFC p33) saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - dvb_add_frontend_ioctls (budget->dvb_adapter, - budget_patch_diseqc_ioctl, NULL, budget); - dev->ext_priv = budget; + budget->dvb_adapter->priv = budget; + frontend_init(budget); + return 0; } @@ -235,8 +438,7 @@ static int budget_patch_detach (struct saa7146_dev* dev) struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; int err; - dvb_remove_frontend_ioctls (budget->dvb_adapter, - budget_patch_diseqc_ioctl, NULL); + if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend); err = ttpci_budget_deinit (budget); diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index 52db81ba0275..9488c72383a1 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -35,6 +35,11 @@ */ #include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "ves1820.h" +#include "l64781.h" +#include "tda8083.h" static void Set22K (struct budget *budget, int state) { @@ -105,89 +110,368 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long return 0; } - -int budget_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +/* + * Routines for the Fujitsu Siemens Activy budget card + * 22 kHz tone and DiSEqC are handled by the frontend. + * Voltage must be set here. + */ +static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage) { - struct budget *budget = fe->before_after_data; + struct saa7146_dev *dev=budget->dev; dprintk(2, "budget: %p\n", budget); - switch (cmd) { - case FE_SET_TONE: - switch ((fe_sec_tone_mode_t) arg) { + switch (voltage) { + case SEC_VOLTAGE_13: + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); + break; + case SEC_VOLTAGE_18: + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + return SetVoltage_Activy (budget, voltage); +} + +static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + switch (tone) { case SEC_TONE_ON: Set22K (budget, 1); break; case SEC_TONE_OFF: Set22K (budget, 0); break; + default: return -EINVAL; - }; - break; + } - case FE_DISEQC_SEND_MASTER_CMD: + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) { - struct dvb_diseqc_master_cmd *cmd = arg; + struct budget* budget = (struct budget*) fe->dvb->priv; SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - break; + + return 0; } - case FE_DISEQC_SEND_BURST: - SendDiSEqCMsg (budget, 0, NULL, (unsigned long)arg); - break; +static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; - default: - return -EOPNOTSUPP; - }; + SendDiSEqCMsg (budget, 0, NULL, minicmd); return 0; } +static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (params->frequency + 479500) / 125; + + if (params->frequency > 2000000) pwr = 3; + else if (params->frequency > 1800000) pwr = 2; + else if (params->frequency > 1600000) pwr = 1; + else if (params->frequency > 1200000) pwr = 0; + else if (params->frequency >= 1100000) pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} -/* - * Routines for the Fujitsu Siemens Activy budget card - * 22 kHz tone and DiSEqC are handled by the frontend. - * Voltage must be set here. - */ -static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage) +static struct ves1x93_config alps_bsrv2_config = { - struct saa7146_dev *dev=budget->dev; + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, + .pll_set = alps_bsrv2_pll_set, +}; - dprintk(2, "budget: %p\n", budget); +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; - switch (voltage) { - case SEC_VOLTAGE_13: - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); - break; - case SEC_VOLTAGE_18: - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - break; - default: - return -EINVAL; +static int alps_bsru6_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 alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; + + div = (params->frequency + (125 - 1)) / 125; // round correctly + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + data[3] = 0xC4; + + if (params->frequency > 1530000) data[3] = 0xc0; + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; -static int budget_ioctl_activy (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { - struct budget *budget = fe->before_after_data; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - dprintk(2, "budget: %p\n", budget); + div = (params->frequency + 35937500 + 31250) / 62500; - switch (cmd) { - case FE_SET_VOLTAGE: - return SetVoltage_Activy (budget, (fe_sec_voltage_t) arg); - default: - return -EOPNOTSUPP; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, + .pll_set = alps_tdbe2_pll_set, +}; + +static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (36125000 + params->frequency) / 166666; + + cfg = 0x88; + + if (params->frequency < 175000000) cpump = 2; + else if (params->frequency < 390000000) cpump = 1; + else if (params->frequency < 470000000) cpump = 2; + else if (params->frequency < 750000000) cpump = 1; + else cpump = 3; + + if (params->frequency < 175000000) band_select = 0x0e; + else if (params->frequency < 470000000) band_select = 0x05; + else band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; } +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, + .pll_set = grundig_29504_401_pll_set, +}; + +static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = params->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, + .pll_set = grundig_29504_451_pll_set, +}; + +static u8 read_pwm(struct budget* budget) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static void frontend_init(struct budget *budget) +{ + switch(budget->dev->pci->subsystem_device) { + case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) + + // try the ALPS BSRV2 first of all + budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops->set_tone = budget_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops->set_tone = budget_set_tone; + break; + } + break; + + case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) + + budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); + if (budget->dvb_frontend) break; + break; + + case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) + + budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap); + if (budget->dvb_frontend) break; + break; + + case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI (tda8083/Grundig 29504-451(tsa5522)) + + // grundig 29504-451 + budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage; + break; + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("budget: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(budget->dvb_adapter, budget->dvb_frontend)) { + printk("budget: Frontend registration failed!\n"); + if (budget->dvb_frontend->ops->release) + budget->dvb_frontend->ops->release(budget->dvb_frontend); + budget->dvb_frontend = NULL; + } + } +} static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) { @@ -203,18 +487,14 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_ dev->ext_priv = budget; - if ((err = ttpci_budget_init (budget, dev, info))) { + if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { printk("==> failed\n"); kfree (budget); return err; } - if (budget->card->type == BUDGET_FS_ACTIVY) - dvb_add_frontend_ioctls (budget->dvb_adapter, - budget_ioctl_activy, NULL, budget); - else - dvb_add_frontend_ioctls (budget->dvb_adapter, - budget_diseqc_ioctl, NULL, budget); + budget->dvb_adapter->priv = budget; + frontend_init(budget); return 0; } @@ -225,12 +505,7 @@ static int budget_detach (struct saa7146_dev* dev) struct budget *budget = (struct budget*) dev->ext_priv; int err; - if (budget->card->type == BUDGET_FS_ACTIVY) - dvb_remove_frontend_ioctls (budget->dvb_adapter, - budget_ioctl_activy, NULL); - else - dvb_remove_frontend_ioctls (budget->dvb_adapter, - budget_diseqc_ioctl, NULL); + if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend); err = ttpci_budget_deinit (budget); @@ -247,18 +522,14 @@ static struct saa7146_extension budget_extension; MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); -MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +/* MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); UNDEFINED HARDWARE - mail linuxtv.org list */ MAKE_BUDGET_INFO(fsacs, "Fujitsu Siemens Activy Budget-S PCI", BUDGET_FS_ACTIVY); -/* Uncomment for Budget Patch */ -/*MAKE_BUDGET_INFO(fs_1_3,"Siemens/Technotrend/Hauppauge PCI rev1.3+Budget_Patch", BUDGET_PATCH);*/ static struct pci_device_id pci_tbl[] = { - /* Uncomment for Budget Patch */ - /*MAKE_EXTENSION_PCI(fs_1_3,0x13c2, 0x0000),*/ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), - MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), +/* MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), UNDEFINED HARDWARE */ MAKE_EXTENSION_PCI(fsacs, 0x1131, 0x4f61), { .vendor = 0, diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h index a6f14e046398..726394cab8cd 100644 --- a/drivers/media/dvb/ttpci/budget.h +++ b/drivers/media/dvb/ttpci/budget.h @@ -9,6 +9,7 @@ #include "dvb_filter.h" #include "dvb_net.h" +#include <linux/module.h> #include <media/saa7146.h> extern int budget_debug; @@ -61,7 +62,10 @@ struct budget { spinlock_t feedlock; + spinlock_t debilock; + struct dvb_adapter *dvb_adapter; + struct dvb_frontend *dvb_frontend; void *priv; }; @@ -82,20 +86,27 @@ static struct saa7146_pci_extension_data x_var = { \ #define BUDGET_TT 0 #define BUDGET_TT_HW_DISEQC 1 -#define BUDGET_KNC1 2 #define BUDGET_PATCH 3 #define BUDGET_FS_ACTIVY 4 -#define BUDGET_CIN1200 5 +#define BUDGET_CIN1200S 5 +#define BUDGET_CIN1200C 6 +#define BUDGET_CIN1200T 7 +#define BUDGET_KNC1S 8 +#define BUDGET_KNC1C 9 +#define BUDGET_KNC1T 10 #define BUDGET_VIDEO_PORTA 0 #define BUDGET_VIDEO_PORTB 1 -extern int ttpci_budget_init (struct budget *budget, - struct saa7146_dev* dev, - struct saa7146_pci_extension_data *info); +extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner); extern int ttpci_budget_deinit (struct budget *budget); extern void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr); extern void ttpci_budget_set_video_port(struct saa7146_dev* dev, int video_port); +extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop); +extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, + int uselocks, int nobusyloop); #endif - diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig index c8bbbcc68f45..b4d3322b3b76 100644 --- a/drivers/media/dvb/ttusb-budget/Kconfig +++ b/drivers/media/dvb/ttusb-budget/Kconfig @@ -1,6 +1,10 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB + select DVB_CX22700 + select DVB_TDA1004X + select DVB_TDA8083 + select DVB_STV0299 help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile index a57b9aee6903..6ab97f6b53fc 100644 --- a/drivers/media/dvb/ttusb-budget/Makefile +++ b/drivers/media/dvb/ttusb-budget/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 537c7eccdebb..91a00d99f2ab 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -24,6 +24,10 @@ #include "dmxdev.h" #include "dvb_demux.h" #include "dvb_net.h" +#include "cx22700.h" +#include "tda1004x.h" +#include "stv0299.h" +#include "tda8083.h" #include <linux/dvb/frontend.h> #include <linux/dvb/dmx.h> @@ -132,6 +136,8 @@ struct ttusb { #if 0 devfs_handle_t stc_devfs_handle; #endif + + struct dvb_frontend* fe; }; /* ugly workaround ... don't know why it's neccessary to read */ @@ -461,9 +467,10 @@ static int ttusb_init_controller(struct ttusb *ttusb) } #ifdef TTUSB_DISEQC -static int ttusb_send_diseqc(struct ttusb *ttusb, +static int ttusb_send_diseqc(struct dvb_frontend* fe, const struct dvb_diseqc_master_cmd *cmd) { + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 b[12] = { 0xaa, ++ttusb->c, 0x18 }; int err; @@ -501,41 +508,24 @@ static int ttusb_update_lnb(struct ttusb *ttusb) return err; } -static int ttusb_set_voltage(struct ttusb *ttusb, fe_sec_voltage_t voltage) +static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + ttusb->voltage = voltage; return ttusb_update_lnb(ttusb); } #ifdef TTUSB_TONE -static int ttusb_set_tone(struct ttusb *ttusb, fe_sec_tone_mode_t tone) +static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + ttusb->tone = tone; return ttusb_update_lnb(ttusb); } #endif -static int ttusb_lnb_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct ttusb *ttusb = fe->before_after_data; - - switch (cmd) { - case FE_SET_VOLTAGE: - return ttusb_set_voltage(ttusb, (fe_sec_voltage_t) arg); -#ifdef TTUSB_TONE - case FE_SET_TONE: - return ttusb_set_tone(ttusb, (fe_sec_tone_mode_t) arg); -#endif -#ifdef TTUSB_DISEQC - case FE_DISEQC_SEND_MASTER_CMD: - return ttusb_send_diseqc(ttusb, - (struct dvb_diseqc_master_cmd *) - arg); -#endif - default: - return -EOPNOTSUPP; - }; -} #if 0 static void ttusb_set_led_freq(struct ttusb *ttusb, u8 freq) @@ -560,7 +550,7 @@ static void ttusb_handle_sec_data(struct ttusb_channel *channel, const u8 * data, int len); #endif -int numpkt = 0, lastj, numts, numstuff, numsec, numinvalid; +static int numpkt = 0, lastj, numts, numstuff, numsec, numinvalid; static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, int len) @@ -1071,38 +1061,347 @@ static struct file_operations stc_fops = { }; #endif -u32 functionality(struct i2c_adapter *adapter) +static u32 functionality(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; } -static struct i2c_algorithm ttusb_dec_algo = { - .name = "ttusb dec i2c algorithm", - .id = I2C_ALGO_BIT, - .master_xfer = master_xfer, - .functionality = functionality, + + +static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 data[4]; + struct i2c_msg msg = {.addr=0x61, .flags=0, .buf=data, .len=sizeof(data) }; + u32 div; + + div = (params->frequency + 36166667) / 166667; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | 0x85; + data[3] = params->frequency < 592000000 ? 0x40 : 0x80; + + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +struct cx22700_config alps_tdmb7_config = { + .demod_address = 0x43, + .pll_set = alps_tdmb7_pll_set, +}; + + + + + +static int philips_tdm1316l_pll_init(struct dvb_frontend* fe) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = { .addr=0x60, .flags=0, .buf=td1316_init, .len=sizeof(td1316_init) }; + + // setup PLL configuration + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr=0x60, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = params->frequency + 36130000; + if (tuner_frequency < 87000000) return -EINVAL; + else if (tuner_frequency < 130000000) cp = 3; + else if (tuner_frequency < 160000000) cp = 5; + else if (tuner_frequency < 200000000) cp = 6; + else if (tuner_frequency < 290000000) cp = 3; + else if (tuner_frequency < 420000000) cp = 5; + else if (tuner_frequency < 480000000) cp = 6; + else if (tuner_frequency < 620000000) cp = 3; + else if (tuner_frequency < 830000000) cp = 5; + else if (tuner_frequency < 895000000) cp = 7; + else return -EINVAL; + + // determine band + if (params->frequency < 49000000) return -EINVAL; + else if (params->frequency < 159000000) band = 1; + else if (params->frequency < 444000000) band = 2; + else if (params->frequency < 861000000) band = 4; + else return -EINVAL; + + // setup PLL filter + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + tda1004x_write_byte(fe, 0x0C, 0); + filter = 0; + break; + + case BANDWIDTH_7_MHZ: + tda1004x_write_byte(fe, 0x0C, 0); + filter = 0; + break; + + case BANDWIDTH_8_MHZ: + tda1004x_write_byte(fe, 0x0C, 0xFF); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + + return request_firmware(fw, name, &ttusb->dev->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .pll_init = philips_tdm1316l_pll_init, + .pll_set = philips_tdm1316l_pll_set, + .request_firmware = philips_tdm1316l_request_firmware, +}; + + +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff }; -static int client_register(struct i2c_client *client) +static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { - struct ttusb *ttusb = (struct ttusb*)i2c_get_adapdata(client->adapter); + 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; + } - if (client->driver->command) - return client->driver->command(client, FE_REGISTER, ttusb->adapter); + 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 client_unregister(struct i2c_client *client) +static int alps_bsru6_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { - struct ttusb *ttusb = (struct ttusb*)i2c_get_adapdata(client->adapter); + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((params->frequency < 950000) || (params->frequency > 2150000)) + return -EINVAL; - if (client->driver->command) - return client->driver->command(client, FE_UNREGISTER, ttusb->adapter); + div = (params->frequency + (125 - 1)) / 125; // round correctly + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0xC4; + + if (params->frequency > 1530000) + buf[3] = 0xc0; + + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } +static struct stv0299_config alps_bsru6_config = { + + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .enhanced_tuning = 0, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, + .pll_set = alps_bsru6_pll_set, +}; + +static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + div = params->frequency / 125; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + buf[3] = 0x00; + + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda8083_config ttusb_novas_grundig_29504_491_config = { + + .demod_address = 0x68, + .pll_set = ttusb_novas_grundig_29504_491_pll_set, +}; + + + +static void frontend_init(struct ttusb* ttusb) +{ + switch(ttusb->dev->descriptor.idProduct) { + case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6(tsa5059) + // try the ALPS BSRU6 first + ttusb->fe = stv0299_attach(&alps_bsru6_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops->set_voltage = ttusb_set_voltage; + break; + } + + // Grundig 29504-491 + ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops->set_voltage = ttusb_set_voltage; + break; + } + + break; + + case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) + // try the ALPS TDMB7 first + ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) + break; + + // Philips td1316 + ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) + break; + break; + } + + if (ttusb->fe == NULL) { + printk("dvb-ttusb-budget: A frontend driver was not found for device %04x/%04x\n", + ttusb->dev->descriptor.idVendor, + ttusb->dev->descriptor.idProduct); + } else { + if (dvb_register_frontend(ttusb->adapter, ttusb->fe)) { + printk("dvb-ttusb-budget: Frontend registration failed!\n"); + if (ttusb->fe->ops->release) + ttusb->fe->ops->release(ttusb->fe); + ttusb->fe = NULL; + } + } +} + + + +static struct i2c_algorithm ttusb_dec_algo = { + .name = "ttusb dec i2c algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = master_xfer, + .functionality = functionality, +}; + static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; @@ -1140,6 +1439,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i up(&ttusb->sem); dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE); + ttusb->adapter->priv = ttusb; /* i2c */ memset(&ttusb->i2c_adap, 0, sizeof(struct i2c_adapter)); @@ -1155,8 +1455,6 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i ttusb->i2c_adap.algo = &ttusb_dec_algo; ttusb->i2c_adap.algo_data = NULL; ttusb->i2c_adap.id = I2C_ALGO_BIT; - ttusb->i2c_adap.client_register = client_register; - ttusb->i2c_adap.client_unregister = client_unregister; result = i2c_add_adapter(&ttusb->i2c_adap); if (result) { @@ -1164,9 +1462,6 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i return result; } - dvb_add_frontend_ioctls(ttusb->adapter, ttusb_lnb_ioctl, NULL, - ttusb); - memset(&ttusb->dvb_demux, 0, sizeof(ttusb->dvb_demux)); ttusb->dvb_demux.dmx.capabilities = @@ -1218,9 +1513,10 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, &stc_fops, ttusb); #endif - usb_set_intfdata(intf, (void *) ttusb); + frontend_init(ttusb); + return 0; } @@ -1238,7 +1534,7 @@ static void ttusb_disconnect(struct usb_interface *intf) dvb_net_release(&ttusb->dvbnet); dvb_dmxdev_release(&ttusb->dmxdev); dvb_dmx_release(&ttusb->dvb_demux); - + if (ttusb->fe != NULL) dvb_unregister_frontend(ttusb->fe); i2c_del_adapter(&ttusb->i2c_adap); dvb_unregister_adapter(ttusb->adapter); @@ -1251,7 +1547,7 @@ static void ttusb_disconnect(struct usb_interface *intf) static struct usb_device_id ttusb_table[] = { {USB_DEVICE(0xb48, 0x1003)}, - {USB_DEVICE(0xb48, 0x1004)}, /* to be confirmed ???? */ +/* {USB_DEVICE(0xb48, 0x1004)},UNDEFINED HARDWARE - mail linuxtv.org list*/ /* to be confirmed ???? */ {USB_DEVICE(0xb48, 0x1005)}, {} }; diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h index 287645d8a9e6..95ee7995455e 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h @@ -1,7 +1,7 @@ #include <asm/types.h> -u8 dsp_bootcode [] = { +static u8 dsp_bootcode [] = { 0x08, 0xaa, 0x00, 0x18, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x80, 0x18, 0x5f, 0x00, 0x00, 0x01, 0x80, 0x77, 0x18, 0x2a, 0xeb, diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile index bf4e38740299..b41bf1f06a9f 100644 --- a/drivers/media/dvb/ttusb-dec/Makefile +++ b/drivers/media/dvb/ttusb-dec/Makefile @@ -1,3 +1,3 @@ -obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o +obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index b85f7e9d5ab5..b51a96141392 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -30,11 +30,7 @@ #include <linux/version.h> #include <linux/interrupt.h> #include <linux/firmware.h> -#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE) #include <linux/crc32.h> -#else -#warning "CRC checking of firmware not available" -#endif #include <linux/init.h> #include "dmxdev.h" @@ -42,6 +38,7 @@ #include "dvb_filter.h" #include "dvb_frontend.h" #include "dvb_net.h" +#include "ttusbdecfe.h" static int debug; static int output_pva; @@ -69,9 +66,6 @@ MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)"); #define MAX_PVA_LENGTH 6144 -#define LOF_HI 10600000 -#define LOF_LO 9750000 - enum ttusb_dec_model { TTUSB_DEC2000T, TTUSB_DEC2540T, @@ -102,12 +96,9 @@ struct ttusb_dec { struct dvb_demux demux; struct dmx_frontend frontend; struct dvb_net dvb_net; - struct dvb_frontend_info *frontend_info; - int (*frontend_ioctl) (struct dvb_frontend *, unsigned int, void *); + struct dvb_frontend* fe; u16 pid[DMX_PES_OTHER]; - int hi_band; - int voltage; /* USB bits */ struct usb_device *udev; @@ -166,32 +157,6 @@ struct filter_info { struct list_head filter_info_list; }; -static struct dvb_frontend_info dec2000t_frontend_info = { - .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", - .type = FE_OFDM, - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; - -static struct dvb_frontend_info dec3000s_frontend_info = { - .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; - static void ttusb_dec_set_model(struct ttusb_dec *dec, enum ttusb_dec_model model); @@ -1170,10 +1135,9 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) u16 firmware_csum = 0; u16 firmware_csum_ns; u32 firmware_size_nl; -#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE) u32 crc32_csum, crc32_check, tmp; -#endif const struct firmware *fw_entry = NULL; + dprintk("%s\n", __FUNCTION__); if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) { @@ -1194,7 +1158,6 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) /* a 32 bit checksum over the first 56 bytes of the DSP Code is stored at offset 56 of file, so use it to check if the firmware file is valid. */ -#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE) crc32_csum = crc32(~0L, firmware, 56) ^ ~0L; memcpy(&tmp, &firmware[56], 4); crc32_check = htonl(tmp); @@ -1204,7 +1167,6 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) __FUNCTION__, crc32_csum, crc32_check); return -1; } -#endif memcpy(idstring, &firmware[36], 20); idstring[20] = '\0'; printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring); @@ -1404,6 +1366,7 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec) dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); dvb_dmxdev_release(&dec->dmxdev); dvb_dmx_release(&dec->demux); + if (dec->fe) dvb_unregister_frontend(dec->fe); dvb_unregister_adapter(dec->adapter); } @@ -1435,264 +1398,6 @@ static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec) } } -static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int cmd, - void *arg) -{ - struct ttusb_dec *dec = fe->data; - - dprintk("%s\n", __FUNCTION__); - - switch (cmd) { - - case FE_GET_INFO: - dprintk("%s: FE_GET_INFO\n", __FUNCTION__); - memcpy(arg, dec->frontend_info, - sizeof (struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: { - fe_status_t *status = (fe_status_t *)arg; - dprintk("%s: FE_READ_STATUS\n", __FUNCTION__); - *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; - break; - } - - case FE_READ_BER: { - u32 *ber = (u32 *)arg; - dprintk("%s: FE_READ_BER\n", __FUNCTION__); - *ber = 0; - return -ENOSYS; - break; - } - - case FE_READ_SIGNAL_STRENGTH: { - dprintk("%s: FE_READ_SIGNAL_STRENGTH\n", __FUNCTION__); - *(s32 *)arg = 0xFF; - return -ENOSYS; - break; - } - - case FE_READ_SNR: - dprintk("%s: FE_READ_SNR\n", __FUNCTION__); - *(s32 *)arg = 0; - return -ENOSYS; - break; - - case FE_READ_UNCORRECTED_BLOCKS: - dprintk("%s: FE_READ_UNCORRECTED_BLOCKS\n", __FUNCTION__); - *(u32 *)arg = 0; - return -ENOSYS; - break; - - case FE_SET_FRONTEND: { - struct dvb_frontend_parameters *p = - (struct dvb_frontend_parameters *)arg; - u8 b[] = { 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff }; - u32 freq; - - dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__); - - dprintk(" frequency->%d\n", p->frequency); - dprintk(" symbol_rate->%d\n", - p->u.qam.symbol_rate); - dprintk(" inversion->%d\n", p->inversion); - - freq = htonl(p->frequency / 1000); - memcpy(&b[4], &freq, sizeof (u32)); - ttusb_dec_send_command(dec, 0x71, sizeof(b), b, NULL, NULL); - - break; - } - - case FE_GET_FRONTEND: - dprintk("%s: FE_GET_FRONTEND\n", __FUNCTION__); - break; - - case FE_SLEEP: - dprintk("%s: FE_SLEEP\n", __FUNCTION__); - return -ENOSYS; - break; - - case FE_INIT: - dprintk("%s: FE_INIT\n", __FUNCTION__); - break; - - default: - dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd); - return -EINVAL; - - } - - return 0; -} - -static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, - unsigned int cmd, void *arg) -{ - struct ttusb_dec *dec = fe->data; - - dprintk("%s\n", __FUNCTION__); - - switch (cmd) { - - case FE_GET_INFO: - dprintk("%s: FE_GET_INFO\n", __FUNCTION__); - memcpy(arg, dec->frontend_info, - sizeof (struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: { - fe_status_t *status = (fe_status_t *)arg; - dprintk("%s: FE_READ_STATUS\n", __FUNCTION__); - *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; - break; - } - - case FE_READ_BER: { - u32 *ber = (u32 *)arg; - dprintk("%s: FE_READ_BER\n", __FUNCTION__); - *ber = 0; - return -ENOSYS; - break; - } - - case FE_READ_SIGNAL_STRENGTH: { - dprintk("%s: FE_READ_SIGNAL_STRENGTH\n", __FUNCTION__); - *(s32 *)arg = 0xFF; - return -ENOSYS; - break; - } - - case FE_READ_SNR: - dprintk("%s: FE_READ_SNR\n", __FUNCTION__); - *(s32 *)arg = 0; - return -ENOSYS; - break; - - case FE_READ_UNCORRECTED_BLOCKS: - dprintk("%s: FE_READ_UNCORRECTED_BLOCKS\n", __FUNCTION__); - *(u32 *)arg = 0; - return -ENOSYS; - break; - - case FE_SET_FRONTEND: { - struct dvb_frontend_parameters *p = - (struct dvb_frontend_parameters *)arg; - u8 b[] = { 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }; - u32 freq; - u32 sym_rate; - u32 band; - u32 lnb_voltage; - - dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__); - - dprintk(" frequency->%d\n", p->frequency); - dprintk(" symbol_rate->%d\n", - p->u.qam.symbol_rate); - dprintk(" inversion->%d\n", p->inversion); - - freq = htonl(p->frequency + - (dec->hi_band ? LOF_HI : LOF_LO)); - memcpy(&b[4], &freq, sizeof(u32)); - sym_rate = htonl(p->u.qam.symbol_rate); - memcpy(&b[12], &sym_rate, sizeof(u32)); - band = htonl(dec->hi_band ? LOF_HI : LOF_LO); - memcpy(&b[24], &band, sizeof(u32)); - lnb_voltage = htonl(dec->voltage); - memcpy(&b[28], &lnb_voltage, sizeof(u32)); - - ttusb_dec_send_command(dec, 0x71, sizeof(b), b, NULL, NULL); - - break; - } - - case FE_GET_FRONTEND: - dprintk("%s: FE_GET_FRONTEND\n", __FUNCTION__); - break; - - case FE_SLEEP: - dprintk("%s: FE_SLEEP\n", __FUNCTION__); - return -ENOSYS; - break; - - case FE_INIT: - dprintk("%s: FE_INIT\n", __FUNCTION__); - break; - - case FE_DISEQC_SEND_MASTER_CMD: { - u8 b[] = { 0x00, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 }; - struct dvb_diseqc_master_cmd *cmd = arg; - memcpy(&b[4], cmd->msg, cmd->msg_len); - dprintk("%s: FE_DISEQC_SEND_MASTER_CMD\n", __FUNCTION__); - ttusb_dec_send_command(dec, 0x72, - sizeof(b) - (6 - cmd->msg_len), b, - NULL, NULL); - break; - } - - case FE_DISEQC_SEND_BURST: - dprintk("%s: FE_DISEQC_SEND_BURST\n", __FUNCTION__); - break; - - case FE_SET_TONE: { - fe_sec_tone_mode_t tone = (fe_sec_tone_mode_t)arg; - dprintk("%s: FE_SET_TONE\n", __FUNCTION__); - dec->hi_band = (SEC_TONE_ON == tone); - break; - } - - case FE_SET_VOLTAGE: - dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__); - switch ((fe_sec_voltage_t) arg) { - case SEC_VOLTAGE_13: - dec->voltage = 13; - break; - case SEC_VOLTAGE_18: - dec->voltage = 18; - break; - default: - return -EINVAL; - break; - } - break; - - default: - dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd); - return -EINVAL; - - } - - return 0; -} - -static void ttusb_dec_init_frontend(struct ttusb_dec *dec) -{ - int ret; - ret = dvb_register_frontend(dec->frontend_ioctl, dec->adapter, dec, dec->frontend_info, THIS_MODULE); -} - -static void ttusb_dec_exit_frontend(struct ttusb_dec *dec) -{ - dvb_unregister_frontend(dec->frontend_ioctl, dec->adapter); -} - static void ttusb_dec_init_filters(struct ttusb_dec *dec) { INIT_LIST_HEAD(&dec->filter_info_list); @@ -1711,6 +1416,18 @@ static void ttusb_dec_exit_filters(struct ttusb_dec *dec) } } +int fe_send_command(struct dvb_frontend* fe, const u8 command, + int param_length, const u8 params[], + int *result_length, u8 cmd_result[]) +{ + struct ttusb_dec* dec = (struct ttusb_dec*) fe->dvb->priv; + return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result); +} + +struct ttusbdecfe_config fe_config = { + .send_command = fe_send_command +}; + static int ttusb_dec_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1752,7 +1469,32 @@ static int ttusb_dec_probe(struct usb_interface *intf, return 0; } ttusb_dec_init_dvb(dec); - ttusb_dec_init_frontend(dec); + + dec->adapter->priv = dec; + switch (id->idProduct) { + case 0x1006: + dec->fe = ttusbdecfe_dvbs_attach(&fe_config); + break; + + case 0x1008: + case 0x1009: + dec->fe = ttusbdecfe_dvbt_attach(&fe_config); + break; + } + + if (dec->fe == NULL) { + printk("dvb-ttusb-dec: A frontend driver was not found for device %04x/%04x\n", + dec->udev->descriptor.idVendor, + dec->udev->descriptor.idProduct); + } else { + if (dvb_register_frontend(dec->adapter, dec->fe)) { + printk("budget-ci: Frontend registration failed!\n"); + if (dec->fe->ops->release) + dec->fe->ops->release(dec->fe); + dec->fe = NULL; + } + } + ttusb_dec_init_v_pes(dec); ttusb_dec_init_filters(dec); ttusb_dec_init_tasklet(dec); @@ -1776,7 +1518,6 @@ static void ttusb_dec_disconnect(struct usb_interface *intf) ttusb_dec_exit_tasklet(dec); ttusb_dec_exit_filters(dec); ttusb_dec_exit_usb(dec); - ttusb_dec_exit_frontend(dec); ttusb_dec_exit_dvb(dec); } @@ -1792,22 +1533,16 @@ static void ttusb_dec_set_model(struct ttusb_dec *dec, case TTUSB_DEC2000T: dec->model_name = "DEC2000-t"; dec->firmware_name = "dvb-ttusb-dec-2000t.fw"; - dec->frontend_info = &dec2000t_frontend_info; - dec->frontend_ioctl = ttusb_dec_2000t_frontend_ioctl; break; case TTUSB_DEC2540T: dec->model_name = "DEC2540-t"; dec->firmware_name = "dvb-ttusb-dec-2540t.fw"; - dec->frontend_info = &dec2000t_frontend_info; - dec->frontend_ioctl = ttusb_dec_2000t_frontend_ioctl; break; case TTUSB_DEC3000S: dec->model_name = "DEC3000-s"; dec->firmware_name = "dvb-ttusb-dec-3000s.fw"; - dec->frontend_info = &dec3000s_frontend_info; - dec->frontend_ioctl = ttusb_dec_3000s_frontend_ioctl; break; } } diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c new file mode 100644 index 000000000000..ff0e5212a1f4 --- /dev/null +++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c @@ -0,0 +1,255 @@ +/* + * TTUSB DEC Frontend Driver + * + * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org> + * + * 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 "dvb_frontend.h" +#include "ttusbdecfe.h" + + +#define LOF_HI 10600000 +#define LOF_LO 9750000 + +struct ttusbdecfe_state { + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct ttusbdecfe_config* config; + + struct dvb_frontend frontend; + + u8 hi_band; + u8 voltage; +}; + + +static int ttusbdecfe_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; + + return 0; +} + +static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + u8 b[] = { 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff }; + + u32 freq = htonl(p->frequency / 1000); + memcpy(&b[4], &freq, sizeof (u32)); + state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); + + return 0; +} + +static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + u8 b[] = { 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + u32 freq; + u32 sym_rate; + u32 band; + u32 lnb_voltage; + + freq = htonl(p->frequency + + (state->hi_band ? LOF_HI : LOF_LO)); + memcpy(&b[4], &freq, sizeof(u32)); + sym_rate = htonl(p->u.qam.symbol_rate); + memcpy(&b[12], &sym_rate, sizeof(u32)); + band = htonl(state->hi_band ? LOF_HI : LOF_LO); + memcpy(&b[24], &band, sizeof(u32)); + lnb_voltage = htonl(state->voltage); + memcpy(&b[28], &lnb_voltage, sizeof(u32)); + + state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); + + return 0; +} + +static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + u8 b[] = { 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; + + memcpy(&b[4], cmd->msg, cmd->msg_len); + + state->config->send_command(fe, 0x72, + sizeof(b) - (6 - cmd->msg_len), b, + NULL, NULL); + + return 0; +} + + +static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + state->hi_band = (SEC_TONE_ON == tone); + + return 0; +} + + +static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + state->voltage = 13; + break; + case SEC_VOLTAGE_18: + state->voltage = 18; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void ttusbdecfe_release(struct dvb_frontend* fe) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops ttusbdecfe_dvbt_ops; + +struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config) +{ + struct ttusbdecfe_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + memcpy(&state->ops, &ttusbdecfe_dvbt_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops ttusbdecfe_dvbs_ops; + +struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config) +{ + struct ttusbdecfe_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->voltage = 0; + state->hi_band = 0; + memcpy(&state->ops, &ttusbdecfe_dvbs_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = { + + .info = { + .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = ttusbdecfe_release, + + .set_frontend = ttusbdecfe_dvbt_set_frontend, + + .read_status = ttusbdecfe_read_status, +}; + +static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = { + + .info = { + .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = ttusbdecfe_release, + + .set_frontend = ttusbdecfe_dvbs_set_frontend, + + .read_status = ttusbdecfe_read_status, + + .diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd, + .set_voltage = ttusbdecfe_dvbs_set_voltage, + .set_tone = ttusbdecfe_dvbs_set_tone, +}; + +MODULE_DESCRIPTION("TTUSB DEC DVB-T/S Demodulator driver"); +MODULE_AUTHOR("Alex Woods/Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(ttusbdecfe_dvbt_attach); +EXPORT_SYMBOL(ttusbdecfe_dvbs_attach); diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h new file mode 100644 index 000000000000..15ccc3d1a20e --- /dev/null +++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h @@ -0,0 +1,38 @@ +/* + * TTUSB DEC Driver + * + * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org> + * + * 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 TTUSBDECFE_H +#define TTUSBDECFE_H + +#include <linux/dvb/frontend.h> + +struct ttusbdecfe_config +{ + int (*send_command)(struct dvb_frontend* fe, const u8 command, + int param_length, const u8 params[], + int *result_length, u8 cmd_result[]); +}; + +extern struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config); + +extern struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config); + +#endif // TTUSBDECFE_H diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index c852d6acc769..9332b9c8b020 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -220,7 +220,7 @@ config VIDEO_ZR36120 config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on VIDEO_DEV && PCI && SONYPI && !HIGHMEM64G + depends on VIDEO_DEV && PCI && SONYPI ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 79bea86560db..2618f20af8ed 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -110,19 +110,20 @@ static void rvfree(void * mem, unsigned long size) /* * return a page table pointing to N pages of locked memory * - * NOTE: The meye device expects dma_addr_t size to be 32 bits - * (the toc must be exactly 1024 entries each of them being 4 bytes - * in size, the whole result being 4096 bytes). We're using here - * dma_addr_t for correctness but the compilation of this driver is - * disabled for HIGHMEM64G=y, where sizeof(dma_addr_t) != 4 + * NOTE: The meye device expects DMA addresses on 32 bits, we build + * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. */ static int ptable_alloc(void) { - dma_addr_t *pt; + u32 *pt; int i; memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + /* give only 32 bit DMA addresses */ + if (dma_set_mask(&meye.mchip_dev->dev, 0xffffffff)) + return -1; + meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, PAGE_SIZE, &meye.mchip_dmahandle, @@ -134,17 +135,19 @@ static int ptable_alloc(void) pt = meye.mchip_ptable_toc; for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma; meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, PAGE_SIZE, - pt, + &dma, GFP_KERNEL); if (!meye.mchip_ptable[i]) { int j; pt = meye.mchip_ptable_toc; for (j = 0; j < i; ++j) { + dma = (dma_addr_t) *pt; dma_free_coherent(&meye.mchip_dev->dev, PAGE_SIZE, - meye.mchip_ptable[j], *pt); + meye.mchip_ptable[j], dma); pt++; } dma_free_coherent(&meye.mchip_dev->dev, @@ -155,6 +158,7 @@ static int ptable_alloc(void) meye.mchip_dmahandle = 0; return -1; } + *pt = (u32) dma; pt++; } return 0; @@ -162,15 +166,16 @@ static int ptable_alloc(void) static void ptable_free(void) { - dma_addr_t *pt; + u32 *pt; int i; pt = meye.mchip_ptable_toc; for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma = (dma_addr_t) *pt; if (meye.mchip_ptable[i]) dma_free_coherent(&meye.mchip_dev->dev, PAGE_SIZE, - meye.mchip_ptable[i], *pt); + meye.mchip_ptable[i], dma); pt++; } @@ -520,11 +525,11 @@ static void mchip_vrj_setup(u8 mode) } /* sets the DMA parameters into the chip */ -static void mchip_dma_setup(u32 dma_addr) +static void mchip_dma_setup(dma_addr_t dma_addr) { int i; - mchip_set(MCHIP_MM_PT_ADDR, dma_addr); + mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); for (i = 0; i < 4; i++) mchip_set(MCHIP_MM_FIR(i), 0); meye.mchip_fnum = 0; diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index f1b291d9d3b4..e8cd897b0d20 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -31,7 +31,7 @@ #define _MEYE_PRIV_H_ #define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 12 +#define MEYE_DRIVER_MINORVERSION 13 #define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ __stringify(MEYE_DRIVER_MINORVERSION) @@ -294,7 +294,7 @@ struct meye { u8 mchip_fnum; /* current mchip frame number */ unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ - dma_addr_t *mchip_ptable_toc; /* mchip: ptable toc */ + void *mchip_ptable_toc; /* mchip: ptable toc */ dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ unsigned char *grab_fbuffer; /* capture framebuffer */ unsigned char *grab_temp; /* temporary buffer */ diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index 3a5fe51fbd18..386963798f34 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -1511,9 +1511,12 @@ static int msp_attach(struct i2c_adapter *adap, int addr, int kind) msp->opmode = opmode; if (OPMODE_AUTO == msp->opmode) { +#if 0 /* seems to work for ivtv only, disable by default for now ... */ if (HAVE_SIMPLER(msp)) msp->opmode = OPMODE_SIMPLER; - else if (HAVE_SIMPLE(msp)) + else +#endif + if (HAVE_SIMPLE(msp)) msp->opmode = OPMODE_SIMPLE; else msp->opmode = OPMODE_MANUAL; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 9abd90b0eb29..10699b49037a 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -87,7 +87,7 @@ MODULE_PARM_DESC(card, "card type"); static DECLARE_MUTEX(devlist_lock); LIST_HEAD(saa7134_devlist); static LIST_HEAD(mops_list); -static unsigned int saa7134_devcount; +unsigned int saa7134_devcount; #define dprintk(fmt, arg...) if (core_debug) \ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 6c20dc37f558..e13a041cedd2 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -155,7 +155,6 @@ static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS]; static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS]; static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS]; -static int FusionInitCalled = 0; static int mpt_base_index = -1; static int last_drv_idx = -1; @@ -337,15 +336,13 @@ mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); - if ((int)ioc->chip_type <= (int)FC929) + if (ioc->bus_type == FC) mpt_fc_log_info(ioc, log_info); - else + else if (ioc->bus_type == SCSI) mpt_sp_log_info(ioc, log_info); } if (ioc_stat & MPI_IOCSTATUS_MASK) { - if ((int)ioc->chip_type <= (int)FC929) - ; - else + if (ioc->bus_type == SCSI) mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf); } } else { @@ -392,7 +389,7 @@ mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) } #ifdef MPT_DEBUG_IRQ - if ((int)ioc->chip_type > (int)FC929) { + if (ioc->bus_type == SCSI) { /* Verify mf, mr are reasonable. */ if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth)) @@ -603,22 +600,6 @@ mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass) last_drv_idx = -1; -#ifndef MODULE - /* - * Handle possibility of the mptscsih_detect() routine getting - * called *before* fusion_init! - */ - if (!FusionInitCalled) { - dprintk((KERN_INFO MYNAM ": Hmmm, calling fusion_init from mpt_register!\n")); - /* - * NOTE! We'll get recursion here, as fusion_init() - * calls mpt_register()! - */ - fusion_init(); - FusionInitCalled++; - } -#endif - /* * Search for empty callback slot in this order: {N,...,7,6,5,...,1} * (slot/handle 0 is reserved!) @@ -1220,22 +1201,21 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->pio_chip = (SYSIF_REGS __iomem *)pmem; } - ioc->chip_type = FCUNK; if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) { - ioc->chip_type = FC909; ioc->prod_name = "LSIFC909"; + ioc->bus_type = FC; } if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) { - ioc->chip_type = FC929; ioc->prod_name = "LSIFC929"; + ioc->bus_type = FC; } else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919) { - ioc->chip_type = FC919; ioc->prod_name = "LSIFC919"; + ioc->bus_type = FC; } else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929X) { - ioc->chip_type = FC929X; pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + ioc->bus_type = FC; if (revision < XL_929) { ioc->prod_name = "LSIFC929X"; /* 929X Chip Fix. Set Split transactions level @@ -1254,8 +1234,8 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) } } else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919X) { - ioc->chip_type = FC919X; ioc->prod_name = "LSIFC919X"; + ioc->bus_type = FC; /* 919X Chip Fix. Set Split transactions level * for PCIX. Set MOST bits to zero. */ @@ -1264,8 +1244,8 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_write_config_byte(pdev, 0x6a, pcixcmd); } else if (pdev->device == MPI_MANUFACTPAGE_DEVID_53C1030) { - ioc->chip_type = C1030; ioc->prod_name = "LSI53C1030"; + ioc->bus_type = SCSI; /* 1030 Chip Fix. Disable Split transactions * for PCIX. Set MOST bits to zero if Rev < C0( = 8). */ @@ -1277,8 +1257,8 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) } } else if (pdev->device == MPI_MANUFACTPAGE_DEVID_1030_53C1035) { - ioc->chip_type = C1035; ioc->prod_name = "LSI53C1035"; + ioc->bus_type = SCSI; } sprintf(ioc->name, "ioc%d", ioc->id); @@ -1326,9 +1306,7 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* NEW! 20010220 -sralston * Check for "bound ports" (929, 929X, 1030, 1035) to reduce redundant resets. */ - if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) - || (ioc->chip_type == C1035) || (ioc->chip_type == FC929X)) - mpt_detect_bound_ports(ioc, pdev); + mpt_detect_bound_ports(ioc, pdev); if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) { @@ -1760,7 +1738,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) * and we try GetLanConfigPages again... */ if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) { - if ((int)ioc->chip_type <= (int)FC929) { + if (ioc->bus_type == FC) { /* * Pre-fetch FC port WWN and stuff... * (FCPortPage0_t stuff) @@ -2103,20 +2081,8 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag) } /* Is it already READY? */ - if (!statefault && (ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY) { - if ((int)ioc->chip_type <= (int)FC929) - return 0; - else { - return 0; - /* Workaround from broken 1030 FW. - * Force a diagnostic reset if fails. - */ -/* if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0) - return 0; - else - statefault = 4; */ - } - } + if (!statefault && (ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY) + return 0; /* * Check to see if IOC is in FAULT state. @@ -2513,11 +2479,11 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag) ddlprintk((MYIOC_s_INFO_FMT "upload_fw %d facts.Flags=%x\n", ioc->name, ioc->upload_fw, ioc->facts.Flags)); - if ((int)ioc->chip_type <= (int)FC929) { + if (ioc->bus_type == FC) ioc_init.MaxDevices = MPT_MAX_FC_DEVICES; - } else { + else ioc_init.MaxDevices = MPT_MAX_SCSI_DEVICES; - } + ioc_init.MaxBuses = MPT_MAX_BUS; ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */ @@ -2622,7 +2588,7 @@ SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag) /* RAID FW may take a long time to enable */ - if ((int)ioc->chip_type <= (int)FC929) { + if (ioc->bus_type == FC) { ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, reply_sz, (u16*)&reply_buf, 65 /*seconds*/, sleepFlag); } else { @@ -2992,7 +2958,7 @@ KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag) int cnt,cntdn; dinitprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); - if ((int)ioc->chip_type > (int)FC929) { + if (ioc->bus_type == SCSI) { /* Always issue a Msg Unit Reset first. This will clear some * SCSI bus hang conditions. */ @@ -3420,7 +3386,7 @@ initChainBuffers(MPT_ADAPTER *ioc) dinitprintk((KERN_INFO MYNAM ": %s Now numSGE=%d num_sge=%d num_chain=%d\n", ioc->name, numSGE, num_sge, num_chain)); - if ((int) ioc->chip_type > (int) FC929) + if (ioc->bus_type == SCSI) num_chain *= MPT_SCSI_CAN_QUEUE; else num_chain *= MPT_FC_CAN_QUEUE; @@ -5277,7 +5243,7 @@ procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eo len += sprintf(buf+len, " PortNumber = %d (of %d)\n", p+1, ioc->facts.NumberOfPorts); - if ((int)ioc->chip_type <= (int)FC929) { + if (ioc->bus_type == FC) { if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", @@ -5921,11 +5887,6 @@ fusion_init(void) int i; int r; - if (FusionInitCalled++) { - dprintk((KERN_INFO MYNAM ": INFO - Driver late-init entry point called\n")); - return 0; - } - show_mptmod_ver(my_NAME, my_VERSION); printk(KERN_INFO COPYRIGHT "\n"); diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 1e37574c7a6b..d9184ac01662 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -83,8 +83,8 @@ #define COPYRIGHT "Copyright (c) 1999-2004 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.01.17" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.01.17" +#define MPT_LINUX_VERSION_COMMON "3.01.18" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.01.18" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -296,23 +296,6 @@ typedef struct _MPT_SGL64_HDR { SGESimple64_t sge[1]; } MPT_SGL64_HDR; - -/* - * Chip-specific stuff... FC929 delineates break between - * FC and Parallel SCSI parts. Do NOT re-order. - */ - -typedef enum { - FC919X = 0x0819, - FC929X = 0x0829, - FC909 = 0x0909, - FC919 = 0x0919, - FC929 = 0x0929, - C1030 = 0x1030, - C1035 = 0x1035, - FCUNK = 0xFBAD -} CHIP_TYPE; - /* * System interface register set */ @@ -517,6 +500,7 @@ typedef struct _MPT_ADAPTER char *prod_name; /* "LSIFC9x9" */ SYSIF_REGS __iomem *chip; /* == c8817000 (mmap) */ SYSIF_REGS __iomem *pio_chip; /* Programmed IO (downloadboot) */ + u8 bus_type; u32 mem_phys; /* == f4020000 (mmap) */ u32 pio_mem_phys; /* Programmed IO (downloadboot) */ int mem_size; /* mmap memory size */ @@ -543,7 +527,6 @@ typedef struct _MPT_ADAPTER dma_addr_t ChainBufferDMA; struct list_head FreeChainQ; spinlock_t FreeChainQlock; - CHIP_TYPE chip_type; /* We (host driver) get to manage our own RequestQueue! */ dma_addr_t req_frames_dma; MPT_FRAME_HDR *req_frames; /* Request msg frames - rounded up! */ @@ -573,12 +556,6 @@ typedef struct _MPT_ADAPTER int eventTypes; /* Event logging parameters */ int eventContext; /* Next event context */ int eventLogSize; /* Max number of cached events */ -#ifdef MPTSCSIH_DBG_TIMEOUT - int timeout_hard; - int timeout_delta; - int timeout_cnt; - int timeout_maxcnt; -#endif struct _mpt_ioctl_events *events; /* pointer to event log */ u8 *cached_fw; /* Pointer to FW */ dma_addr_t cached_fw_dma; @@ -894,6 +871,12 @@ typedef struct _MPT_LOCAL_REPLY { #define TM_STATE_IN_PROGRESS (1) #define TM_STATE_ERROR (2) +typedef enum { + FC, + SCSI, + SAS +} BUS_TYPE; + typedef struct _MPT_SCSI_HOST { MPT_ADAPTER *ioc; int port; @@ -909,7 +892,6 @@ typedef struct _MPT_SCSI_HOST { */ u8 tmPending; u8 resetPending; - u8 is_spi; /* Parallel SCSI i/f */ u8 negoNvram; /* DV disabled, nego NVRAM */ u8 pad1; u8 tmState; diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index bb608fa5afcc..91153cf47fa9 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -1218,7 +1218,7 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) /* Fill in the data and return the structure to the calling * program */ - if ((int)ioc->chip_type <= (int) FC929) + if (ioc->bus_type == FC) karg->adapterType = MPT_IOCTL_INTERFACE_FC; else karg->adapterType = MPT_IOCTL_INTERFACE_SCSI; @@ -1518,7 +1518,7 @@ mptctl_readtest (unsigned long arg) #ifdef MFCNT karg.chip_type = ioc->mfcnt; #else - karg.chip_type = ioc->chip_type; + karg.chip_type = ioc->pcidev->device; #endif strncpy (karg.name, ioc->name, MPT_MAX_NAME); karg.name[MPT_MAX_NAME-1]='\0'; @@ -2470,7 +2470,7 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) karg.base_io_addr = pci_resource_start(pdev, 0); - if ((int)ioc->chip_type <= (int) FC929) + if (ioc->bus_type == FC) karg.bus_phys_width = HP_BUS_WIDTH_UNK; else karg.bus_phys_width = HP_BUS_WIDTH_16; @@ -2559,7 +2559,7 @@ mptctl_hp_targetinfo(unsigned long arg) /* There is nothing to do for FCP parts. */ - if ((int) ioc->chip_type <= (int) FC929) + if (ioc->bus_type == FC) return 0; if ((ioc->spi_data.sdp0length == 0) || (ioc->sh == NULL)) diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index e08689a96d31..65ffd15fa03c 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -96,10 +96,22 @@ MODULE_AUTHOR(MODULEAUTHOR); MODULE_DESCRIPTION(my_NAME); MODULE_LICENSE("GPL"); -/* Set string for command line args from insmod */ #ifdef MODULE -char *mptscsih = NULL; -module_param(mptscsih, charp, 0); +static int dv = MPTSCSIH_DOMAIN_VALIDATION; +module_param(dv, int, 0); +MODULE_PARM_DESC(dv, "DV Algorithm: enhanced = 1, basic = 0 (default=MPTSCSIH_DOMAIN_VALIDATION=1)"); + +static int width = MPTSCSIH_MAX_WIDTH; +module_param(width, int, 0); +MODULE_PARM_DESC(width, "Max Bus Width: wide = 1, narrow = 0 (default=MPTSCSIH_MAX_WIDTH=1)"); + +static ushort factor = MPTSCSIH_MIN_SYNC; +module_param(factor, ushort, 0); +MODULE_PARM_DESC(factor, "Min Sync Factor: (default=MPTSCSIH_MIN_SYNC=0x08)"); + +static int saf_te = MPTSCSIH_SAF_TE; +module_param(saf_te, int, 0); +MODULE_PARM_DESC(saf_te, "Force enabling SEP Processor: (default=MPTSCSIH_SAF_TE=0)"); #endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -200,9 +212,6 @@ static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target); static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage); static void mptscsih_fillbuf(char *buffer, int size, int index, int width); #endif -#ifdef MODULE -static int mptscsih_setup(char *str); -#endif /* module entry point */ static int __init mptscsih_init (void); static void __exit mptscsih_exit (void); @@ -245,15 +254,9 @@ static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq); static int scandv_wait_done = 1; -/* Driver default setup +/* Driver command line structure */ -static struct mptscsih_driver_setup - driver_setup = MPTSCSIH_DRIVER_SETUP; - -#ifdef MPTSCSIH_DBG_TIMEOUT -static struct scsi_cmnd *foo_to[8]; -#endif - +static struct mptscsih_driver_setup driver_setup; static struct scsi_host_template driver_template; /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -653,27 +656,6 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) pScsiReq = (SCSIIORequest_t *) mf; pScsiReply = (SCSIIOReply_t *) mr; -#ifdef MPTSCSIH_DBG_TIMEOUT - if (ioc->timeout_cnt > 0) { - int ii, left = 0; - - for (ii=0; ii < 8; ii++) { - if (sc == foo_to[ii]) { - printk(MYIOC_s_INFO_FMT "complete (%p, %ld)\n", - ioc->name, sc, jiffies); - foo_to[ii] = NULL; - } - if (foo_to[ii] != NULL) - left++; - } - - if (left == 0) { - ioc->timeout_maxcnt = 0; - ioc->timeout_cnt = 0; - } - } -#endif - if (pScsiReply == NULL) { /* special context reply handling */ ; @@ -686,13 +668,14 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) scsi_state = pScsiReply->SCSIState; scsi_status = pScsiReply->SCSIStatus; xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); + sc->resid = sc->request_bufflen - xfer_cnt; - dreplyprintk((KERN_NOTICE " Reply (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", - ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], - mf, mr, sc)); - dreplyprintk((KERN_NOTICE "IOCStatus=%04xh SCSIState=%02xh" - " SCSIStatus=%02xh xfer_cnt=%08xh\n", - status, scsi_state, scsi_status, xfer_cnt)); + dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n" + "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n" + "resid=%d bufflen=%d xfer_cnt=%d\n", + ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], + status, scsi_state, scsi_status, sc->resid, + sc->request_bufflen, xfer_cnt)); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) copy_sense_data(sc, hd, mf, pScsiReply); @@ -737,12 +720,11 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) sc->result = DID_RESET << 16; /* GEM Workaround. */ - if (hd->is_spi) + if (ioc->bus_type == SCSI) mptscsih_no_negotiate(hd, sc->device->id); break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ - sc->resid = sc->request_bufflen - xfer_cnt; if ( xfer_cnt >= sc->underflow ) { /* Sufficient data transfer occurred */ sc->result = (DID_OK << 16) | scsi_status; @@ -771,7 +753,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) */ ; } else { - if ( (xfer_cnt == 0) || (sc->underflow > xfer_cnt)) { + if (xfer_cnt < sc->underflow) { sc->result = DID_SOFT_ERROR << 16; } if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { @@ -785,15 +767,9 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) } } - /* Give report and update residual count. - */ dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow)); dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt)); - - sc->resid = sc->request_bufflen - xfer_cnt; - dreplyprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid)); - /* Report Queue Full */ if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL) @@ -848,7 +824,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) break; case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */ - sc->result = DID_SOFT_ERROR << 16; + sc->result = DID_SOFT_ERROR << 16; break; case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */ @@ -1132,7 +1108,7 @@ mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) * max_lun = 1 + actual last lun, * see hosts.h :o( */ - if ((int)ioc->chip_type > (int)FC929) { + if (ioc->bus_type == SCSI) { sh->max_id = MPT_MAX_SCSI_DEVICES; } else { /* For FC, increase the queue depth @@ -1191,9 +1167,6 @@ mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) hd = (MPT_SCSI_HOST *) sh->hostdata; hd->ioc = ioc; - if ((int)ioc->chip_type > (int)FC929) - hd->is_spi = 1; - /* SCSI needs scsi_cmnd lookup table! * (with size equal to req_depth*PtrSz!) */ @@ -1259,15 +1232,7 @@ mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Moved Earlier Pam D */ /* ioc->sh = sh; */ -#ifdef MPTSCSIH_DBG_TIMEOUT - ioc->timeout_hard = 0; - ioc->timeout_delta = 30 * HZ; - ioc->timeout_maxcnt = 0; - ioc->timeout_cnt = 0; - for (ii=0; ii < 8; ii++) - foo_to[ii] = NULL; -#endif - if (hd->is_spi) { + if (ioc->bus_type == SCSI) { /* Update with the driver setup * values. */ @@ -1278,9 +1243,9 @@ mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) } if (ioc->spi_data.minSyncFactor < - driver_setup.min_sync_fac) { + driver_setup.min_sync_factor) { ioc->spi_data.minSyncFactor = - driver_setup.min_sync_fac; + driver_setup.min_sync_factor; } if (ioc->spi_data.minSyncFactor == MPT_ASYNC) { @@ -1307,7 +1272,7 @@ mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) "dv %x width %x factor %x saf_te %x\n", ioc->name, driver_setup.dv, driver_setup.max_width, - driver_setup.min_sync_fac, + driver_setup.min_sync_factor, driver_setup.saf_te)); } @@ -1374,11 +1339,11 @@ mptscsih_remove(struct pci_dev *pdev) hd = (MPT_SCSI_HOST *)host->hostdata; if (hd != NULL) { - int sz1, sz3, sztarget=0; + int sz1; mptscsih_shutdown(&pdev->dev); - sz1 = sz3 = 0; + sz1=0; if (hd->ScsiLookup != NULL) { sz1 = hd->ioc->req_depth * sizeof(void *); @@ -1387,36 +1352,16 @@ mptscsih_remove(struct pci_dev *pdev) } if (hd->Targets != NULL) { - int max, ii; - - /* - * Free any target structures that were allocated. - */ - if (hd->is_spi) { - max = MPT_MAX_SCSI_DEVICES; - } else { - max = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; - } - for (ii=0; ii < max; ii++) { - if (hd->Targets[ii]) { - kfree(hd->Targets[ii]); - hd->Targets[ii] = NULL; - sztarget += sizeof(VirtDevice); - } - } - /* * Free pointer array. */ - sz3 = max * sizeof(void *); kfree(hd->Targets); hd->Targets = NULL; } dprintk((MYIOC_s_INFO_FMT - "Free'd ScsiLookup (%d) Target (%d+%d) memory\n", - hd->ioc->name, sz1, sz3, sztarget)); - dprintk(("Free'd done and free Q (%d) memory\n", szQ)); + "Free'd ScsiLookup (%d) memory\n", + hd->ioc->name, sz1)); /* NULL the Scsi_Host pointer */ @@ -1545,9 +1490,15 @@ mptscsih_init(void) } #ifdef MODULE - /* Evaluate the command line arguments, if any */ - if (mptscsih) - mptscsih_setup(mptscsih); + dinitprintk((KERN_INFO MYNAM + ": Command Line Args: dv=%d max_width=%d " + "factor=0x%x saf_te=%d\n", + dv, width, factor, saf_te)); + + driver_setup.dv = (dv) ? 1 : 0; + driver_setup.max_width = (width) ? 1 : 0; + driver_setup.min_sync_factor = factor; + driver_setup.saf_te = (saf_te) ? 1 : 0;; #endif if(mpt_device_driver_register(&mptscsih_driver, @@ -1676,129 +1627,6 @@ static int mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int le return ((info.pos > info.offset) ? info.pos - info.offset : 0); } -#ifndef MPTSCSIH_DBG_TIMEOUT -static int mptscsih_user_command(MPT_ADAPTER *ioc, char *pbuf, int len) -{ - /* Not yet implemented */ - return len; -} -#else -#define is_digit(c) ((c) >= '0' && (c) <= '9') -#define digit_to_bin(c) ((c) - '0') -#define is_space(c) ((c) == ' ' || (c) == '\t') - -#define UC_DBG_TIMEOUT 0x01 -#define UC_DBG_HARDRESET 0x02 - -static int skip_spaces(char *ptr, int len) -{ - int cnt, c; - - for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt --); - - return (len - cnt); -} - -static int get_int_arg(char *ptr, int len, ulong *pv) -{ - int cnt, c; - ulong v; - for (v = 0, cnt = len; cnt > 0 && (c=*ptr++) && is_digit(c); cnt --) { - v = (v * 10) + digit_to_bin(c); - } - - if (pv) - *pv = v; - - return (len - cnt); -} - - -static int is_keyword(char *ptr, int len, char *verb) -{ - int verb_len = strlen(verb); - - if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len)) - return verb_len; - else - return 0; -} - -#define SKIP_SPACES(min_spaces) \ - if ((arg_len = skip_spaces(ptr,len)) < (min_spaces)) \ - return -EINVAL; \ - ptr += arg_len; \ - len -= arg_len; - -#define GET_INT_ARG(v) \ - if (!(arg_len = get_int_arg(ptr,len, &(v)))) \ - return -EINVAL; \ - ptr += arg_len; \ - len -= arg_len; - -static int mptscsih_user_command(MPT_ADAPTER *ioc, char *buffer, int length) -{ - char *ptr = buffer; - char btmp[24]; /* REMOVE */ - int arg_len; - int len = length; - int cmd; - ulong number = 1; - ulong delta = 10; - - if ((len > 0) && (ptr[len -1] == '\n')) - --len; - - if (len < 22) { - strncpy(btmp, buffer, len); - btmp[len+1]='\0'; - } else { - strncpy(btmp, buffer, 22); - btmp[23]='\0'; - } - printk("user_command: ioc %d, buffer %s, length %d\n", - ioc->id, btmp, length); - - if ((arg_len = is_keyword(ptr, len, "timeout")) != 0) - cmd = UC_DBG_TIMEOUT; - else if ((arg_len = is_keyword(ptr, len, "hardreset")) != 0) - cmd = UC_DBG_HARDRESET; - else - return -EINVAL; - - ptr += arg_len; - len -= arg_len; - - switch(cmd) { - case UC_DBG_TIMEOUT: - SKIP_SPACES(1); - GET_INT_ARG(number); - SKIP_SPACES(1); - GET_INT_ARG(delta); - break; - } - - printk("user_command: cnt=%ld delta=%ld\n", number, delta); - - if (len) - return -EINVAL; - else { - if (cmd == UC_DBG_HARDRESET) { - ioc->timeout_hard = 1; - } else if (cmd == UC_DBG_TIMEOUT) { - /* process this command ... - */ - ioc->timeout_maxcnt = 0; - ioc->timeout_delta = delta < 2 ? 2 : delta; - ioc->timeout_cnt = 0; - ioc->timeout_maxcnt = number < 8 ? number: 8; - } - } - /* Not yet implemented */ - return length; -} -#endif - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mptscsih_proc_info - Return information about MPT adapter @@ -1812,7 +1640,7 @@ static int mptscsih_user_command(MPT_ADAPTER *ioc, char *buffer, int length) * hostno: scsi host number * func: if write = 1; if read = 0 */ -static int +static int mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int func) { @@ -1821,7 +1649,9 @@ mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t off int size = 0; if (func) { - size = mptscsih_user_command(ioc, buffer, length); + /* + * write is not supported + */ } else { if (start) *start = buffer; @@ -1832,7 +1662,6 @@ mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t off return size; } - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #define ADD_INDEX_LOG(req_ent) do { } while(0) @@ -1863,11 +1692,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) u32 cmd_len; int my_idx; int ii; - int rc; - int did_errcode; - int issueCmd; - did_errcode = 0; hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata; target = SCpnt->device->id; lun = SCpnt->device->lun; @@ -1966,84 +1791,66 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) /* Now add the SG list * Always have a SGE even if null length. */ - rc = SUCCESS; if (datalen == 0) { /* Add a NULL SGE */ mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); } else { /* Add a 32 or 64 bit SGE */ - rc = mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx); + if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS) + goto fail; } - - if (rc == SUCCESS) { - hd->ScsiLookup[my_idx] = SCpnt; - SCpnt->host_scribble = NULL; - - /* SCSI specific processing */ - issueCmd = 1; - if (hd->is_spi) { - int dvStatus = hd->ioc->spi_data.dvStatus[target]; - - if (dvStatus || hd->ioc->spi_data.forceDv) { + hd->ScsiLookup[my_idx] = SCpnt; + SCpnt->host_scribble = NULL; #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION - if ((dvStatus & MPT_SCSICFG_NEED_DV) || - (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { - unsigned long lflags; - /* Schedule DV if necessary */ - spin_lock_irqsave(&dvtaskQ_lock, lflags); - if (!dvtaskQ_active) { - dvtaskQ_active = 1; - spin_unlock_irqrestore(&dvtaskQ_lock, lflags); - INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd); - - schedule_work(&mptscsih_dvTask); - } else { - spin_unlock_irqrestore(&dvtaskQ_lock, lflags); - } - hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV; - } - - /* Trying to do DV to this target, extend timeout. - * Wait to issue until flag is clear - */ - if (dvStatus & MPT_SCSICFG_DV_PENDING) { - mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); - issueCmd = 0; + if (hd->ioc->bus_type == SCSI) { + int dvStatus = hd->ioc->spi_data.dvStatus[target]; + int issueCmd = 1; + + if (dvStatus || hd->ioc->spi_data.forceDv) { + + if ((dvStatus & MPT_SCSICFG_NEED_DV) || + (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { + unsigned long lflags; + /* Schedule DV if necessary */ + spin_lock_irqsave(&dvtaskQ_lock, lflags); + if (!dvtaskQ_active) { + dvtaskQ_active = 1; + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); + INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd); + + schedule_work(&mptscsih_dvTask); + } else { + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); } + hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV; + } - /* Set the DV flags. - */ - if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) - mptscsih_set_dvflags(hd, pScsiReq); -#endif + /* Trying to do DV to this target, extend timeout. + * Wait to issue until flag is clear + */ + if (dvStatus & MPT_SCSICFG_DV_PENDING) { + mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); + issueCmd = 0; } - } -#ifdef MPTSCSIH_DBG_TIMEOUT - if (hd->ioc->timeout_cnt < hd->ioc->timeout_maxcnt) { - foo_to[hd->ioc->timeout_cnt] = SCpnt; - hd->ioc->timeout_cnt++; - //mod_timer(&SCpnt->eh_timeout, jiffies + hd->ioc->timeout_delta); - issueCmd = 0; - printk(MYIOC_s_WARN_FMT - "to pendingQ: (sc=%p, mf=%p, time=%ld)\n", - hd->ioc->name, SCpnt, mf, jiffies); + /* Set the DV flags. + */ + if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) + mptscsih_set_dvflags(hd, pScsiReq); + + if (!issueCmd) + goto fail; } + } #endif - if (issueCmd) { - mpt_put_msg_frame(ScsiDoneCtx, hd->ioc, mf); - dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", - hd->ioc->name, SCpnt, mf, my_idx)); - DBG_DUMP_REQUEST_FRAME(mf) - } else - goto fail; - } else - goto fail; - + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc, mf); + dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", + hd->ioc->name, SCpnt, mf, my_idx)); + DBG_DUMP_REQUEST_FRAME(mf) return 0; fail: @@ -2216,11 +2023,6 @@ mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, in } } -#ifdef MPTSCSIH_DBG_TIMEOUT - if (hd->ioc->timeout_hard) - rc = 1; -#endif - /* Only fall through to the HRH if this is a bus reset */ if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc || @@ -2457,9 +2259,9 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt) printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n", hd->ioc->name, SCpnt); - /* Unsupported for SCSI. Supported for FCP + /* Supported for FC only. */ - if (hd->is_spi) + if (hd->ioc->bus_type == SCSI) return FAILED; spin_unlock_irq(host_lock); @@ -2767,39 +2569,56 @@ static int mptscsih_slave_alloc(struct scsi_device *device) { struct Scsi_Host *host = device->host; - MPT_SCSI_HOST *hd; + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; VirtDevice *vdev; - - hd = (MPT_SCSI_HOST *)host->hostdata; + uint target = device->id; if (hd == NULL) return -ENODEV; - if ((vdev = hd->Targets[device->id]) == NULL) { - if ((vdev = kmalloc(sizeof(VirtDevice), GFP_ATOMIC)) == NULL) { - printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%d) FAILED!\n", - hd->ioc->name, (int)sizeof(VirtDevice)); - return -ENOMEM; - } else { - memset(vdev, 0, sizeof(VirtDevice)); - vdev->tflags = MPT_TARGET_FLAGS_Q_YES; - vdev->ioc_id = hd->ioc->id; - vdev->target_id = device->id; - vdev->bus_id = device->channel; - vdev->raidVolume = 0; - hd->Targets[device->id] = vdev; - if (hd->is_spi) { - if (hd->ioc->spi_data.isRaid & (1 << device->id)) { - vdev->raidVolume = 1; - ddvtprintk((KERN_INFO - "RAID Volume @ id %d\n", device->id)); - } - } else { - vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; - } + if ((vdev = hd->Targets[target]) != NULL) + goto out; + + vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); + if (!vdev) { + printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", + hd->ioc->name, sizeof(VirtDevice)); + return -ENOMEM; + } + + memset(vdev, 0, sizeof(VirtDevice)); + vdev->tflags = MPT_TARGET_FLAGS_Q_YES; + vdev->ioc_id = hd->ioc->id; + vdev->target_id = device->id; + vdev->bus_id = device->channel; + vdev->raidVolume = 0; + hd->Targets[device->id] = vdev; + if (hd->ioc->bus_type == SCSI) { + if (hd->ioc->spi_data.isRaid & (1 << device->id)) { + vdev->raidVolume = 1; + ddvtprintk((KERN_INFO + "RAID Volume @ id %d\n", device->id)); } + } else { + vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; } + + out: vdev->num_luns++; + return 0; +} + +static int mptscsih_is_raid_volume(MPT_SCSI_HOST *hd, uint id) +{ + int i; + + if (!hd->ioc->spi_data.isRaid || !hd->ioc->spi_data.pIocPg3) + return 0; + + for (i = 0; i < hd->ioc->spi_data.pIocPg3->NumPhysDisks; i++) { + if (id == hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID) + return 1; + } return 0; } @@ -2812,59 +2631,37 @@ static void mptscsih_slave_destroy(struct scsi_device *device) { struct Scsi_Host *host = device->host; - MPT_SCSI_HOST *hd; + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; VirtDevice *vdev; - int raid_volume=0; - - hd = (MPT_SCSI_HOST *)host->hostdata; + uint target = device->id; + uint lun = device->lun; if (hd == NULL) return; - mptscsih_search_running_cmds(hd, device->id, device->lun); + mptscsih_search_running_cmds(hd, target, lun); - /* Free memory and reset all flags for this target - */ - if ((vdev = hd->Targets[device->id]) != NULL) { - vdev->num_luns--; - - if (vdev->luns[0] & (1 << device->lun)) - vdev->luns[0] &= ~(1 << device->lun); - - /* Free device structure only if number of luns is 0. - */ - if (vdev->num_luns == 0) { - kfree(hd->Targets[device->id]); - hd->Targets[device->id] = NULL; - - if (!hd->is_spi) - return; - - if((hd->ioc->spi_data.isRaid) && (hd->ioc->spi_data.pIocPg3)) { - int i; - for(i=0;i<hd->ioc->spi_data.pIocPg3->NumPhysDisks && - raid_volume==0;i++) - - if(device->id == - hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID) { - raid_volume=1; - hd->ioc->spi_data.forceDv |= - MPT_SCSICFG_RELOAD_IOC_PG3; - } - } + vdev = hd->Targets[target]; + vdev->luns[0] &= ~(1 << lun); + if (--vdev->num_luns) + return; - if(!raid_volume){ - hd->ioc->spi_data.dvStatus[device->id] = + kfree(hd->Targets[target]); + hd->Targets[target] = NULL; + + if (hd->ioc->bus_type == SCSI) { + if (mptscsih_is_raid_volume(hd, target)) { + hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3; + } else { + hd->ioc->spi_data.dvStatus[target] = MPT_SCSICFG_NEGOTIATE; - if (hd->negoNvram == 0) - hd->ioc->spi_data.dvStatus[device->id] - |= MPT_SCSICFG_DV_NOT_DONE; + if (!hd->negoNvram) { + hd->ioc->spi_data.dvStatus[target] |= + MPT_SCSICFG_DV_NOT_DONE; } } } - - return; } static void @@ -2874,7 +2671,7 @@ mptscsih_set_queue_depth(struct scsi_device *device, MPT_SCSI_HOST *hd, int max_depth; int tagged; - if ( hd->is_spi ) { + if (hd->ioc->bus_type == SCSI) { if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) max_depth = 1; @@ -2989,7 +2786,6 @@ mptscsih_store_queue_depth(struct device *dev, const char *buf, size_t count) return count; } - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * Private routines... @@ -3125,10 +2921,6 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) mpt_free_msg_frame(ioc, hd->tmPtr); } -#ifdef MPTSCSIH_DBG_TIMEOUT - ioc->timeout_hard = 0; -#endif - dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name)); } else { @@ -3158,7 +2950,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) /* 4. Renegotiate to all devices, if SCSI */ - if (hd->is_spi) { + if (ioc->bus_type == SCSI) { dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n")); mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM); } @@ -3187,7 +2979,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) /* 7. Set flag to force DV and re-read IOC Page 3 */ - if (hd->is_spi) { + if (ioc->bus_type == SCSI) { ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; ddvtprintk(("Set reload IOC Pg3 Flag\n")); } @@ -3218,7 +3010,7 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) hd = NULL; if (ioc->sh) { hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - if (hd && (hd->is_spi) && (hd->soft_resets < -1)) + if (hd && (ioc->bus_type == SCSI) && (hd->soft_resets < -1)) hd->soft_resets++; } break; @@ -3247,7 +3039,7 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) if (ioc->sh) hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - if (hd && (hd->is_spi) && (hd->negoNvram == 0)) { + if (hd && (ioc->bus_type == SCSI) && (hd->negoNvram == 0)) { ScsiCfgData *pSpi; Ioc3PhysDisk_t *pPDisk; int numPDisk; @@ -3394,7 +3186,7 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char * indexed_lun = (lun % 32); vdev->luns[lun_index] |= (1 << indexed_lun); - if (hd->is_spi) { + if (hd->ioc->bus_type == SCSI) { if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) { /* Treat all Processors as SAF-TE if * command line option is set */ @@ -4675,7 +4467,7 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum) /* Write SDP1 for all SCSI devices * Alloc memory and set up config buffer */ - if (hd->is_spi) { + if (ioc->bus_type == SCSI) { if (ioc->spi_data.sdp1length > 0) { pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev, ioc->spi_data.sdp1length * 4, &cfg1_dma_addr); @@ -4818,7 +4610,7 @@ mptscsih_domainValidation(void *arg) msleep(250); /* DV only to SCSI adapters */ - if ((int)ioc->chip_type <= (int)FC929) + if (ioc->bus_type != SCSI) continue; /* Make sure everything looks ok */ @@ -6289,107 +6081,6 @@ mptscsih_fillbuf(char *buffer, int size, int index, int width) #endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* Commandline Parsing routines and defines. - * - * insmod format: - * insmod mptscsih mptscsih="width:1 dv:n factor:0x09 saf-te:1" - * boot format: - * mptscsih=width:1,dv:n,factor:0x8,saf-te:1 - * - */ -#ifdef MODULE -#define ARG_SEP ' ' -#else -#define ARG_SEP ',' -#endif - -#ifdef MODULE -static char setup_token[] __initdata = - "dv:" - "width:" - "factor:" - "saf-te:" - ; /* DO NOT REMOVE THIS ';' */ -#endif - -#define OPT_DV 1 -#define OPT_MAX_WIDTH 2 -#define OPT_MIN_SYNC_FACTOR 3 -#define OPT_SAF_TE 4 - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -#ifdef MODULE -static int -get_setup_token(char *p) -{ - char *cur = setup_token; - char *pc; - int i = 0; - - while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { - ++pc; - ++i; - if (!strncmp(p, cur, pc - cur)) - return i; - cur = pc; - } - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static int -mptscsih_setup(char *str) -{ - char *cur = str; - char *pc, *pv; - unsigned long val; - int c; - - while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { - char *pe; - - val = 0; - pv = pc; - c = *++pv; - - if (c == 'n') - val = 0; - else if (c == 'y') - val = 1; - else - val = (int) simple_strtoul(pv, &pe, 0); - - printk("Found Token: %s, value %x\n", cur, (int)val); - switch (get_setup_token(cur)) { - case OPT_DV: - driver_setup.dv = val; - break; - - case OPT_MAX_WIDTH: - driver_setup.max_width = val; - break; - - case OPT_MIN_SYNC_FACTOR: - driver_setup.min_sync_fac = val; - break; - - case OPT_SAF_TE: - driver_setup.saf_te = val; - break; - - default: - printk("mptscsih_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur); - break; - } - - if ((cur = strchr(cur, ARG_SEP)) != NULL) - ++cur; - } - return 1; -} -#endif -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - module_init(mptscsih_init); module_exit(mptscsih_exit); diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index 2c002b537067..893f3c761f28 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -95,17 +95,8 @@ struct mptscsih_driver_setup { u8 dv; u8 max_width; - u8 min_sync_fac; + u8 min_sync_factor; u8 saf_te; }; - -#define MPTSCSIH_DRIVER_SETUP \ -{ \ - MPTSCSIH_DOMAIN_VALIDATION, \ - MPTSCSIH_MAX_WIDTH, \ - MPTSCSIH_MIN_SYNC, \ - MPTSCSIH_SAF_TE, \ -} - #endif diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 91a92a59557b..6eea3a8accb7 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -457,6 +457,8 @@ static struct ethtool_ops ace_ethtool_ops = { .get_drvinfo = ace_get_drvinfo, }; +static void ace_watchdog(struct net_device *dev); + static int __devinit acenic_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -485,7 +487,6 @@ static int __devinit acenic_probe_one(struct pci_dev *pdev, dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid; #endif if (1) { - static void ace_watchdog(struct net_device *dev); dev->tx_timeout = &ace_watchdog; dev->watchdog_timeo = 5*HZ; } diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index e02b6aec703a..0199073aaee6 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -2328,6 +2328,8 @@ static int eepro100_suspend(struct pci_dev *pdev, u32 state) outl(PortPartialReset, ioaddr + SCBPort); /* XXX call pci_set_power_state ()? */ + pci_disable_device(pdev); + pci_set_power_state (pdev, 3); return 0; } @@ -2337,7 +2339,10 @@ static int eepro100_resume(struct pci_dev *pdev) struct speedo_private *sp = netdev_priv(dev); long ioaddr = dev->base_addr; + pci_set_power_state(pdev, 0); pci_restore_state(pdev); + pci_enable_device(pdev); + pci_set_master(pdev); if (!netif_running(dev)) return 0; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 47cddd729190..03a7295a3fe6 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -96,6 +96,7 @@ static void ibmveth_proc_unregister_driver(void); static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static inline void ibmveth_schedule_replenishing(struct ibmveth_adapter*); #ifdef CONFIG_PROC_FS #define IBMVETH_PROC_DIR "ibmveth" @@ -104,7 +105,7 @@ static struct proc_dir_entry *ibmveth_proc_dir; static const char ibmveth_driver_name[] = "ibmveth"; static const char ibmveth_driver_string[] = "IBM i/pSeries Virtual Ethernet Driver"; -#define ibmveth_driver_version "1.02" +#define ibmveth_driver_version "1.03" MODULE_AUTHOR("Santiago Leon <santil@us.ibm.com>"); MODULE_DESCRIPTION("IBM i/pSeries Virtual Ethernet Driver"); @@ -271,6 +272,8 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); atomic_inc(&adapter->not_replenishing); + + ibmveth_schedule_replenishing(adapter); } /* kick the replenish tasklet if we need replenishing and it isn't already running */ diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 62f9853893ba..84292c0e0464 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -52,7 +52,7 @@ /*------------------------------------------------------------------*/ #include <linux/module.h> - +#include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> @@ -88,10 +88,10 @@ static struct usb_device_id dongles[] = { /* * Important note : - * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not compliant - * with the USB-IrDA specification (and actually very very different), and - * there is no way this driver can support those devices, apart from - * a complete rewrite... + * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed + * using the "USB-IrDA specification" (yes, there exist such a thing), and + * therefore not supported by this driver (don't add them above). + * There is a Linux driver, stir4200, that support those USB devices. * Jean II */ @@ -1007,9 +1007,9 @@ static int irda_usb_net_close(struct net_device *netdev) } /* Cancel Tx and speed URB - need to be synchronous to avoid races */ self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK; - usb_unlink_urb(self->tx_urb); + usb_kill_urb(self->tx_urb); self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK; - usb_unlink_urb(self->speed_urb); + usb_kill_urb(self->speed_urb); /* Stop and remove instance of IrLAP */ if (self->irlap) @@ -1520,9 +1520,9 @@ static void irda_usb_disconnect(struct usb_interface *intf) /* Cancel Tx and speed URB. * Toggle flags to make sure it's synchronous. */ self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK; - usb_unlink_urb(self->tx_urb); + usb_kill_urb(self->tx_urb); self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK; - usb_unlink_urb(self->speed_urb); + usb_kill_urb(self->speed_urb); } /* Cleanup the device stuff */ @@ -1593,7 +1593,7 @@ module_exit(usb_irda_cleanup); /* * Module parameters */ -MODULE_PARM(qos_mtt_bits, "i"); +module_param(qos_mtt_bits, int, 0); MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); MODULE_AUTHOR("Roman Weissgaerber <weissg@vienna.at>, Dag Brattli <dag@brattli.net> and Jean Tourrilhes <jt@hpl.hp.com>"); MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index c4b7b2a27e7c..89f5096cab74 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -39,19 +39,7 @@ #include <asm/irq.h> #include <asm/dma.h> #include <asm/hardware.h> -#include <asm/mach-types.h> - -#include <asm/arch/assabet.h> -#include <asm/arch/h3600.h> -#include <asm/arch/yopy.h> - -#ifndef GPIO_IRDA_FIR -#define GPIO_IRDA_FIR (0) -#endif - -#ifndef GPIO_IRDA_POWER -#define GPIO_IRDA_POWER (0) -#endif +#include <asm/mach/irda.h> static int power_level = 3; static int tx_lpm; @@ -75,6 +63,7 @@ struct sa1100_irda { struct net_device_stats stats; struct device *dev; + struct irda_platform_data *pdata; struct irlap_cb *irlap; struct qos_info qos; @@ -170,12 +159,8 @@ static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - if (machine_is_assabet()) - ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL); - if (machine_is_h3xxx()) - clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); - if (machine_is_yopy()) - PPSR &= ~GPIO_IRDA_FIR; + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); si->speed = speed; @@ -194,12 +179,8 @@ static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) si->speed = speed; - if (machine_is_assabet()) - ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL); - if (machine_is_h3xxx()) - set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); - if (machine_is_yopy()) - PPSR |= GPIO_IRDA_FIR; + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); sa1100_irda_rx_alloc(si); sa1100_irda_rx_dma_start(si); @@ -216,51 +197,6 @@ static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) } /* - * This sets the IRDA power level on the Assabet. - */ -static inline int -sa1100_irda_set_power_assabet(struct sa1100_irda *si, unsigned int state) -{ - static unsigned int bcr_state[4] = { - ASSABET_BCR_IRDA_MD0, - ASSABET_BCR_IRDA_MD1|ASSABET_BCR_IRDA_MD0, - ASSABET_BCR_IRDA_MD1, - 0 - }; - - if (state < 4) { - state = bcr_state[state]; - ASSABET_BCR_clear(state ^ (ASSABET_BCR_IRDA_MD1| - ASSABET_BCR_IRDA_MD0)); - ASSABET_BCR_set(state); - } - return 0; -} - -/* - * This turns the IRDA power on or off on the Compaq H3600 - */ -static inline int -sa1100_irda_set_power_h3600(struct sa1100_irda *si, unsigned int state) -{ - assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); - return 0; -} - -/* - * This turns the IRDA power on or off on the Yopy - */ -static inline int -sa1100_irda_set_power_yopy(struct sa1100_irda *si, unsigned int state) -{ - if (state) - PPSR &= ~GPIO_IRDA_POWER; - else - PPSR |= GPIO_IRDA_POWER; - return 0; -} - -/* * Control the power state of the IrDA transmitter. * State: * 0 - off @@ -274,14 +210,8 @@ static int __sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) { int ret = 0; - - if (machine_is_assabet()) - ret = sa1100_irda_set_power_assabet(si, state); - if (machine_is_h3xxx()) - ret = sa1100_irda_set_power_h3600(si, state); - if (machine_is_yopy()) - ret = sa1100_irda_set_power_yopy(si, state); - + if (si->pdata->set_power) + ret = si->pdata->set_power(si->dev, state); return ret; } @@ -304,11 +234,8 @@ static int sa1100_irda_startup(struct sa1100_irda *si) /* * Ensure that the ports for this device are setup correctly. */ - if (machine_is_yopy()) { - PPDR |= GPIO_IRDA_POWER | GPIO_IRDA_FIR; - PPSR |= GPIO_IRDA_POWER | GPIO_IRDA_FIR; - PSDR |= GPIO_IRDA_POWER | GPIO_IRDA_FIR; - } + if (si->pdata->startup) + si->pdata->startup(si->dev); /* * Configure PPC for IRDA - we want to drive TXD2 low. @@ -333,10 +260,15 @@ static int sa1100_irda_startup(struct sa1100_irda *si) Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; ret = sa1100_irda_set_speed(si, si->speed = 9600); - if (ret) - return ret; + if (ret) { + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; - return 0; + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + } + + return ret; } static void sa1100_irda_shutdown(struct sa1100_irda *si) @@ -350,6 +282,9 @@ static void sa1100_irda_shutdown(struct sa1100_irda *si) /* Disable the port. */ Ser2UTCR3 = 0; Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); } #ifdef CONFIG_PM @@ -959,6 +894,9 @@ static int sa1100_irda_probe(struct device *_dev) unsigned int baudrate_mask; int err; + if (!pdev->dev.platform_data) + return -EINVAL; + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; if (err) goto err_mem_1; @@ -975,6 +913,7 @@ static int sa1100_irda_probe(struct device *_dev) si = dev->priv; si->dev = &pdev->dev; + si->pdata = pdev->dev.platform_data; /* * Initialise the HP-SIR buffers @@ -1028,7 +967,7 @@ static int sa1100_irda_probe(struct device *_dev) err = register_netdev(dev); if (err == 0) - dev_set_drvdata(&pdev->dev, si); + dev_set_drvdata(&pdev->dev, dev); if (err) { err_mem_5: @@ -1074,15 +1013,8 @@ static struct device_driver sa1100ir_driver = { .resume = sa1100_irda_resume, }; -static struct platform_device sa1100ir_device = { - .name = "sa11x0-ir", - .id = 0, -}; - static int __init sa1100_irda_init(void) { - int ret; - /* * Limit power level a sensible range. */ @@ -1091,19 +1023,12 @@ static int __init sa1100_irda_init(void) if (power_level > 3) power_level = 3; - ret = driver_register(&sa1100ir_driver); - if (ret == 0) { - ret = platform_device_register(&sa1100ir_device); - if (ret) - driver_unregister(&sa1100ir_driver); - } - return ret; + return driver_register(&sa1100ir_driver); } static void __exit sa1100_irda_exit(void) { driver_unregister(&sa1100ir_driver); - platform_device_unregister(&sa1100ir_device); } module_init(sa1100_irda_init); diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 3ce69f89a8cd..f80a19e83a14 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -705,7 +705,7 @@ static int receive_start(struct stir_cb *stir) static void receive_stop(struct stir_cb *stir) { stir->receiving = 0; - usb_unlink_urb(stir->rx_urb); + usb_kill_urb(stir->rx_urb); if (stir->rx_buff.in_frame) stir->stats.collisions++; @@ -974,7 +974,7 @@ static int stir_net_close(struct net_device *netdev) kfree(stir->fifo_status); /* Mop up receive urb's */ - usb_unlink_urb(stir->rx_urb); + usb_kill_urb(stir->rx_urb); kfree(stir->io_buf); usb_free_urb(stir->rx_urb); diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index c21ed302c2de..feaaf3f9dce1 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -75,6 +75,9 @@ static int dongle_id = 0; /* default: probe */ /* We can't guess the type of connected dongle, user *must* supply it. */ MODULE_PARM(dongle_id, "i"); +/* FIXME : we should not need this, because instances should be automatically + * managed by the PCI layer. Especially that we seem to only be using the + * first entry. Jean II */ /* Max 4 instances for now */ static struct via_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; @@ -153,11 +156,9 @@ static int __init via_ircc_init(void) IRDA_DEBUG(3, "%s()\n", __FUNCTION__); rc = pci_register_driver(&via_driver); - if (rc < 1) { + if (rc < 0) { IRDA_DEBUG(0, "%s(): error rc = %d, returning -ENODEV...\n", __FUNCTION__, rc); - if (rc == 0) - pci_unregister_driver (&via_driver); return -ENODEV; } return 0; @@ -288,15 +289,27 @@ static void __exit via_remove_one (struct pci_dev *pdev) { IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + /* FIXME : This is ugly. We should use pci_get_drvdata(pdev); + * to get our driver instance and call directly via_ircc_close(). + * See vlsi_ir for details... + * Jean II */ via_ircc_clean(); + /* FIXME : This should be in via_ircc_close(), because here we may + * theoritically disable still configured devices :-( - Jean II */ + pci_disable_device(pdev); } static void __exit via_ircc_cleanup(void) { IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + /* FIXME : This should be redundant, as pci_unregister_driver() + * should call via_remove_one() on each device. + * Jean II */ via_ircc_clean(); + + /* Cleanup all instances of the driver */ pci_unregister_driver (&via_driver); } @@ -323,6 +336,10 @@ static __devinit int via_ircc_open(int i, chipio_t * info, unsigned int id) self->netdev = dev; spin_lock_init(&self->lock); + /* FIXME : We should store our driver instance in the PCI layer, + * using pci_set_drvdata(), not in this array. + * See vlsi_ir for details... - Jean II */ + /* FIXME : 'i' is always 0 (see via_init_one()) :-( - Jean II */ /* Need to store self somewhere */ dev_self[i] = self; self->index = i; diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 98cd7d8888e7..bfcb612afe19 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -150,6 +150,7 @@ const static struct { static struct pci_device_id rtl8169_pci_tbl[] = { {0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1186, 0x4300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0,}, }; diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 7e6b211bb4e5..9b75be8b971e 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1244,6 +1244,11 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_lock(&lp->lock); + /* A preamble may be used when there is a potential race + * between the interruptible transmit functions and this + * ISR. */ + SMC_INTERRUPT_PREAMBLE; + saved_pointer = SMC_GET_PTR(); mask = SMC_GET_INT_MASK(); SMC_SET_INT_MASK(0); diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index ea491e6cba78..fad8c0437334 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -193,6 +193,53 @@ SMC_outw(u16 val, unsigned long ioaddr, int reg) #define RPC_LSA_DEFAULT RPC_LED_TX_RX #define RPC_LSB_DEFAULT RPC_LED_100_10 +#elif defined(CONFIG_MACH_LPD7A400) || defined(CONFIG_MACH_LPD7A404) + +/* The LPD7A40X_IOBARRIER is necessary to overcome a mismatch between + * the way that the CPU handles chip selects and the way that the SMC + * chip expects the chip select to operate. Refer to + * Documentation/arm/Sharp-LH/IOBarrier for details. The read from + * IOBARRIER is a byte as a least-common denominator of possible + * regions to use as the barrier. It would be wasteful to read 32 + * bits from a byte oriented region. + * + * There is no explicit protection against interrupts intervening + * between the writew and the IOBARRIER. In SMC ISR there is a + * preamble that performs an IOBARRIER in the extremely unlikely event + * that the driver interrupts itself between a writew to the chip an + * the IOBARRIER that follows *and* the cache is large enough that the + * first off-chip access while handing the interrupt is to the SMC + * chip. Other devices in the same address space as the SMC chip must + * be aware of the potential for trouble and perform a similar + * IOBARRIER on entry to their ISR. + */ + +#include <asm/arch/constants.h> /* IOBARRIER_VIRT */ + +#define SMC_CAN_USE_8BIT 0 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 +#define SMC_NOWAIT 0 +#define LPD7A40X_IOBARRIER readb (IOBARRIER_VIRT) + +#define SMC_inw(a,r) readw ((void*) ((a) + (r))) +#define SMC_insw(a,r,p,l) readsw ((void*) ((a) + (r)), p, l) +#define SMC_outw(v,a,r) ({ writew ((v), (a) + (r)); LPD7A40X_IOBARRIER; }) + +static inline void SMC_outsw (unsigned long a, int r, unsigned char* p, int l) +{ + unsigned short* ps = (unsigned short*) p; + while (l-- > 0) { + writew (*ps++, a + r); + LPD7A40X_IOBARRIER; + } +} + +#define SMC_INTERRUPT_PREAMBLE LPD7A40X_IOBARRIER + +#define RPC_LSA_DEFAULT RPC_LED_TX_RX +#define RPC_LSB_DEFAULT RPC_LED_100_10 + #else #define SMC_CAN_USE_8BIT 1 @@ -894,5 +941,8 @@ static const char * chip_ids[ 16 ] = { }) #endif +#if !defined (SMC_INTERRUPT_PREAMBLE) +# define SMC_INTERRUPT_PREAMBLE +#endif #endif /* _SMC91X_H_ */ diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 2bdf9a004397..a6936a75a87e 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -217,7 +217,13 @@ static int pxa2xx_drv_pcmcia_resume(struct device *dev, u32 level) { int ret = 0; if (level == RESUME_RESTORE_STATE) + { + struct pcmcia_low_level *ops = dev->platform_data; + int nr = ops ? ops->nr : 0; + + MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0); ret = pcmcia_socket_dev_resume(dev); + } return ret; } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 6e025ba1bb53..5fd3ad867386 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -450,7 +450,7 @@ mon_open(struct inode *inode, struct file *filp) } P_INFO("open, established connection to *MONITOR service\n\n"); filp->private_data = monpriv; - return 0; + return nonseekable_open(inode, filp); out_unregister: iucv_unregister_program(monpriv->iucv_handle); diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 4a9958be9227..22cf4fec8da9 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -168,7 +168,7 @@ static int vmwdt_open(struct inode *i, struct file *f) ret = vmwdt_keepalive(); if (ret) clear_bit(0, &vmwdt_is_open); - return ret; + return ret ? ret : nonseekable_open(i, f); } static int vmwdt_close(struct inode *i, struct file *f) @@ -238,10 +238,6 @@ static int vmwdt_ioctl(struct inode *i, struct file *f, static ssize_t vmwdt_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { - /* We can't seek */ - if(ppos != &f->f_pos) - return -ESPIPE; - if(count) { if (!vmwdt_nowayout) { size_t i; diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 61e764f67add..53fcccbb424c 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -34,8 +34,7 @@ #ifndef ZFCP_DEF_H #define ZFCP_DEF_H -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_DEF_REVISION "$Revision: 1.110 $" +#define ZFCP_DEF_REVISION "$Revision: 1.111 $" /*************************** INCLUDES *****************************************/ @@ -70,6 +69,7 @@ /********************* GENERAL DEFINES *********************************/ +/* zfcp version number, it consists of major, minor, and patch-level number */ #define ZFCP_VERSION "4.2.0" /** diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 840f7b64f6eb..31bf659918e9 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -31,8 +31,7 @@ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_ERP_REVISION "$Revision: 1.83 $" +#define ZFCP_ERP_REVISION "$Revision: 1.85 $" #include "zfcp_ext.h" @@ -3482,9 +3481,8 @@ zfcp_erp_port_access_denied(struct zfcp_port *port) debug_text_event(adapter->erp_dbf, 3, "p_access_block"); debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t)); read_lock_irqsave(&zfcp_data.config_lock, flags); - zfcp_erp_modify_port_status(port, - ZFCP_STATUS_COMMON_ERP_FAILED | ZFCP_STATUS_COMMON_ACCESS_DENIED, - ZFCP_SET); + zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED | + ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET); read_unlock_irqrestore(&zfcp_data.config_lock, flags); } @@ -3500,9 +3498,8 @@ zfcp_erp_unit_access_denied(struct zfcp_unit *unit) debug_text_event(adapter->erp_dbf, 3, "u_access_block"); debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t)); - zfcp_erp_modify_unit_status(unit, - ZFCP_STATUS_COMMON_ERP_FAILED | ZFCP_STATUS_COMMON_ACCESS_DENIED, - ZFCP_SET); + zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_ERP_FAILED | + ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET); } /* @@ -3541,19 +3538,21 @@ zfcp_erp_port_access_changed(struct zfcp_port *port) debug_text_event(adapter->erp_dbf, 3, "p_access_unblock"); debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t)); - if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status)) { + if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, + &port->status)) { if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status)) list_for_each_entry(unit, &port->unit_list_head, list) zfcp_erp_unit_access_changed(unit); return; } - ZFCP_LOG_NORMAL("Trying to reopen port 0x%016Lx on adapter %s " - "due to update to access control table\n", + ZFCP_LOG_NORMAL("reopen of port 0x%016Lx on adapter %s " + "(due to ACT update)\n", port->wwpn, zfcp_get_busid_by_adapter(adapter)); if (zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED) != 0) - ZFCP_LOG_NORMAL("Reopen of port 0x%016Lx on adapter %s failed\n", - port->wwpn, zfcp_get_busid_by_adapter(adapter)); + ZFCP_LOG_NORMAL("failed reopen of port" + "(adapter %s, wwpn=0x%016Lx)\n", + zfcp_get_busid_by_adapter(adapter), port->wwpn); } /* @@ -3572,16 +3571,15 @@ zfcp_erp_unit_access_changed(struct zfcp_unit *unit) if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status)) return; - ZFCP_LOG_NORMAL("Trying to reopen unit 0x%016Lx " - "on port 0x%016Lx on adapter %s " - "due to update to access control table\n", + ZFCP_LOG_NORMAL("reopen of unit 0x%016Lx on port 0x%016Lx " + " on adapter %s (due to ACT update)\n", unit->fcp_lun, unit->port->wwpn, zfcp_get_busid_by_adapter(adapter)); if (zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED) != 0) - ZFCP_LOG_NORMAL("Reopen of unit 0x%016Lx " - "on port 0x%016Lx on adapter %s failed\n", - unit->fcp_lun, unit->port->wwpn, - zfcp_get_busid_by_adapter(adapter)); + ZFCP_LOG_NORMAL("failed reopen of unit (adapter %s, " + "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n", + zfcp_get_busid_by_adapter(adapter), + unit->port->wwpn, unit->fcp_lun); } #undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 21b59652fc5f..d5fd43352071 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -31,8 +31,8 @@ #ifndef ZFCP_EXT_H #define ZFCP_EXT_H -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_EXT_REVISION "$Revision: 1.61 $" + +#define ZFCP_EXT_REVISION "$Revision: 1.62 $" #include "zfcp_def.h" diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 6e323ca89910..bad894af1cc0 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -30,8 +30,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_FSF_C_REVISION "$Revision: 1.86 $" +#define ZFCP_FSF_C_REVISION "$Revision: 1.88 $" #include "zfcp_ext.h" @@ -1737,7 +1736,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) adapter = els->adapter; ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS, - ZFCP_WAIT_FOR_SBAL|ZFCP_REQ_AUTO_CLEANUP, + ZFCP_REQ_AUTO_CLEANUP, NULL, &lock_flags, &fsf_req); if (ret < 0) { ZFCP_LOG_INFO("error: creation of ELS request failed " @@ -3094,57 +3093,11 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) exclusive = bottom->lun_access_info & FSF_UNIT_ACCESS_EXCLUSIVE; readwrite = bottom->lun_access_info & FSF_UNIT_ACCESS_OUTBOUND_TRANSFER; - if (!adapter->supported_features & FSF_FEATURE_CFDC) - goto no_cfdc; - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | ZFCP_STATUS_UNIT_SHARED | ZFCP_STATUS_UNIT_READONLY, &unit->status); - if (!allowed) - atomic_set_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status); - - if (!adapter->supported_features & FSF_FEATURE_LUN_SHARING) - goto no_lun_sharing; - - if (!exclusive) - atomic_set_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status); - - if (!readwrite) { - atomic_set_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status); - ZFCP_LOG_NORMAL("Unit 0x%016Lx on port 0x%016Lx on adapter %s " - "accessed read-only\n", unit->fcp_lun, - unit->port->wwpn, zfcp_get_busid_by_unit(unit)); - } - - if (exclusive && !readwrite) { - ZFCP_LOG_NORMAL("Exclusive access of read-only unit not " - "supported\n"); - zfcp_erp_unit_failed(unit); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - goto skip_fsfstatus; - } - if (!exclusive && readwrite) { - ZFCP_LOG_NORMAL("Shared access of read-write unit is not " - "supported\n"); - zfcp_erp_unit_failed(unit); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - goto skip_fsfstatus; - } - - no_lun_sharing: - no_cfdc: - if (!(adapter->supported_features & FSF_FEATURE_CFDC) && - (adapter->supported_features & FSF_FEATURE_LUN_SHARING)) { - ZFCP_LOG_NORMAL("LUN sharing without access control is not " - "supported.\n"); - zfcp_erp_unit_failed(unit); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - goto skip_fsfstatus; - } - - /* evaluate FSF status in QTCB */ switch (header->fsf_status) { @@ -3196,6 +3149,8 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) } debug_text_event(adapter->erp_dbf, 1, "fsf_s_access"); zfcp_erp_unit_access_denied(unit); + atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status); + atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 002a40a8498d..e21b547fd427 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -31,8 +31,7 @@ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_SCSI_REVISION "$Revision: 1.73 $" +#define ZFCP_SCSI_REVISION "$Revision: 1.74 $" #include "zfcp_ext.h" diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 2f55683b2b28..fbd8ffa04030 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1767,7 +1767,7 @@ config SCSI_SUNESP config ZFCP tristate "FCP host bus adapter driver for IBM eServer zSeries" - depends on ARCH_S390 && SCSI + depends on ARCH_S390 && QDIO && SCSI select SCSI_FC_ATTRS help If you want to access SCSI devices attached to your IBM eServer diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index ff8da5deeb65..ef4702f23f50 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -645,9 +645,31 @@ int aac_get_adapter_info(struct aac_dev* dev) dev->adapter_info.serial[1]); dev->nondasd_support = 0; + dev->raid_scsi_mode = 0; if(dev->adapter_info.options & AAC_OPT_NONDASD){ dev->nondasd_support = 1; } + + /* + * If the firmware supports ROMB RAID/SCSI mode and we are currently + * in RAID/SCSI mode, set the flag. For now if in this mode we will + * force nondasd support on. If we decide to allow the non-dasd flag + * additional changes changes will have to be made to support + * RAID/SCSI. the function aac_scsi_cmd in this module will have to be + * changed to support the new dev->raid_scsi_mode flag instead of + * leaching off of the dev->nondasd_support flag. Also in linit.c the + * function aac_detect will have to be modified where it sets up the + * max number of channels based on the aac->nondasd_support flag only. + */ + if ((dev->adapter_info.options & AAC_OPT_SCSI_MANAGED) && + (dev->adapter_info.options & AAC_OPT_RAID_SCSI_MODE)) { + dev->nondasd_support = 1; + dev->raid_scsi_mode = 1; + } + if (dev->raid_scsi_mode != 0) + printk(KERN_INFO "%s%d: ROMB RAID/SCSI mode enabled\n", + dev->name, dev->id); + if(nondasd != -1) { dev->nondasd_support = (nondasd!=0); } @@ -1137,7 +1159,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) char *cp; dprintk((KERN_DEBUG "READ CAPACITY command.\n")); - if (fsa_dev_ptr[cid].size <= 0x100000000) + if (fsa_dev_ptr[cid].size <= 0x100000000LL) capacity = fsa_dev_ptr[cid].size - 1; else capacity = (u32)-1; @@ -1446,8 +1468,17 @@ static void aac_srb_callback(void *context, struct fib * fibptr) if( b==TYPE_TAPE || b==TYPE_WORM || b==TYPE_ROM || b==TYPE_MOD|| b==TYPE_MEDIUM_CHANGER || (b==TYPE_DISK && (b1&0x80)) ){ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + /* + * We will allow disk devices if in RAID/SCSI mode and + * the channel is 2 + */ + } else if ((dev->raid_scsi_mode) && + (scsicmd->device->channel == 2)) { + scsicmd->result = DID_OK << 16 | + COMMAND_COMPLETE << 8; } else { - scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8; + scsicmd->result = DID_NO_CONNECT << 16 | + COMMAND_COMPLETE << 8; } } else { scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; @@ -1479,8 +1510,17 @@ static void aac_srb_callback(void *context, struct fib * fibptr) if( b==TYPE_TAPE || b==TYPE_WORM || b==TYPE_ROM || b==TYPE_MOD|| b==TYPE_MEDIUM_CHANGER || (b==TYPE_DISK && (b1&0x80)) ){ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + /* + * We will allow disk devices if in RAID/SCSI mode and + * the channel is 2 + */ + } else if ((dev->raid_scsi_mode) && + (scsicmd->device->channel == 2)) { + scsicmd->result = DID_OK << 16 | + COMMAND_COMPLETE << 8; } else { - scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8; + scsicmd->result = DID_NO_CONNECT << 16 | + COMMAND_COMPLETE << 8; } break; } diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 5f9aaf2986d1..013cc89884df 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -805,6 +805,8 @@ struct aac_adapter_info #define AAC_OPT_SGMAP_HOST64 cpu_to_le32(1<<10) #define AAC_OPT_ALARM cpu_to_le32(1<<11) #define AAC_OPT_NONDASD cpu_to_le32(1<<12) +#define AAC_OPT_SCSI_MANAGED cpu_to_le32(1<<13) +#define AAC_OPT_RAID_SCSI_MODE cpu_to_le32(1<<14) struct aac_dev { @@ -877,6 +879,7 @@ struct aac_dev */ u8 nondasd_support; u8 dac_support; + u8 raid_scsi_mode; }; #define aac_adapter_interrupt(dev) \ diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 8c300e0a52d9..e0cd4ba41678 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -229,7 +229,8 @@ static struct ata_port_info ahci_port_info[] = { { .sht = &ahci_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO, + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA, .pio_mask = 0x03, /* pio3-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h index 5fee46360534..605f92b6c5ca 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.h +++ b/drivers/scsi/aic7xxx/aic79xx_osm.h @@ -540,7 +540,7 @@ struct ahd_platform_data { uint32_t irq; /* IRQ for this adapter */ uint32_t bios_address; uint32_t mem_busaddr; /* Mem Base Addr */ - dma_addr_t hw_dma_mask; + uint64_t hw_dma_mask; ahd_linux_softc_flags flags; }; diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 59b1a4ffc23f..91daf0c7fb10 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -170,24 +170,22 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (sizeof(dma_addr_t) > 4) { uint64_t memsize; - dma_addr_t mask_64bit; - dma_addr_t mask_39bit; + const uint64_t mask_39bit = 0x7FFFFFFFFFULL; memsize = ahd_linux_get_memsize(); - mask_64bit = (dma_addr_t)0xFFFFFFFFFFFFFFFFULL; - mask_39bit = (dma_addr_t)0x7FFFFFFFFFULL; + if (memsize >= 0x8000000000ULL - && pci_set_dma_mask(pdev, mask_64bit) == 0) { + && pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { ahd->flags |= AHD_64BIT_ADDRESSING; - ahd->platform_data->hw_dma_mask = mask_64bit; + ahd->platform_data->hw_dma_mask = DMA_64BIT_MASK; } else if (memsize > 0x80000000 && pci_set_dma_mask(pdev, mask_39bit) == 0) { ahd->flags |= AHD_39BIT_ADDRESSING; ahd->platform_data->hw_dma_mask = mask_39bit; } } else { - pci_set_dma_mask(pdev, 0xFFFFFFFF); - ahd->platform_data->hw_dma_mask = 0xFFFFFFFF; + pci_set_dma_mask(pdev, DMA_32BIT_MASK); + ahd->platform_data->hw_dma_mask = DMA_32BIT_MASK; } ahd->dev_softc = pci; error = ahd_pci_config(ahd, entry); diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index 51c4aaceb83d..539ae081f153 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -545,7 +545,7 @@ struct ahc_platform_data { uint32_t irq; /* IRQ for this adapter */ uint32_t bios_address; uint32_t mem_busaddr; /* Mem Base Addr */ - dma_addr_t hw_dma_mask; + uint64_t hw_dma_mask; ahc_linux_softc_flags flags; }; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index e37c7394477c..49d799ac61e8 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -175,7 +175,7 @@ static int ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { char buf[80]; - dma_addr_t mask_39bit; + const uint64_t mask_39bit = 0x7FFFFFFFFFULL; struct ahc_softc *ahc; ahc_dev_softc_t pci; struct ahc_pci_identity *entry; @@ -226,18 +226,17 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } pci_set_master(pdev); - mask_39bit = 0x7FFFFFFFFFULL; if (sizeof(dma_addr_t) > 4 && ahc_linux_get_memsize() > 0x80000000 && pci_set_dma_mask(pdev, mask_39bit) == 0) { ahc->flags |= AHC_39BIT_ADDRESSING; ahc->platform_data->hw_dma_mask = mask_39bit; } else { - if (pci_set_dma_mask(pdev, 0xFFFFFFFF)) { + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (-ENODEV); } - ahc->platform_data->hw_dma_mask = 0xFFFFFFFF; + ahc->platform_data->hw_dma_mask = DMA_32BIT_MASK; } #endif ahc->dev_softc = pci; diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 1d2b344aab01..93d987433966 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1950,8 +1950,6 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) sg->page = virt_to_page(buf); sg->offset = (unsigned long) buf & ~PAGE_MASK; sg_dma_len(sg) = buflen; - - WARN_ON(buflen > PAGE_SIZE); } void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, @@ -2693,6 +2691,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) VPRINTK("EXIT\n"); } +static inline int ata_should_dma_map(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + case ATA_PROT_ATAPI_DMA: + return 1; + + case ATA_PROT_ATAPI: + case ATA_PROT_PIO: + case ATA_PROT_PIO_MULT: + if (ap->flags & ATA_FLAG_PIO_DMA) + return 1; + + /* fall through */ + + default: + return 0; + } + + /* never reached */ +} + /** * ata_qc_issue - issue taskfile to device * @qc: command to issue to device @@ -2713,12 +2735,16 @@ int ata_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_SG) { - if (ata_sg_setup(qc)) - goto err_out; - } else if (qc->flags & ATA_QCFLAG_SINGLE) { - if (ata_sg_setup_one(qc)) - goto err_out; + if (ata_should_dma_map(qc)) { + if (qc->flags & ATA_QCFLAG_SG) { + if (ata_sg_setup(qc)) + goto err_out; + } else if (qc->flags & ATA_QCFLAG_SINGLE) { + if (ata_sg_setup_one(qc)) + goto err_out; + } + } else { + qc->flags &= ~ATA_QCFLAG_DMAMAP; } ap->ops->qc_prep(qc); diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index c928ee502813..36320e8a3e5b 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -898,7 +898,7 @@ unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, }; memcpy(rbuf, hdr, sizeof(hdr)); - if (buflen > (ATA_SERNO_LEN + 4)) + if (buflen > (ATA_SERNO_LEN + 4 - 1)) ata_dev_id_string(args->id, (unsigned char *) &rbuf[4], ATA_ID_SERNO_OFS, ATA_SERNO_LEN); @@ -927,7 +927,7 @@ unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, rbuf[3] = 4 + strlen(inq_83_str); /* page len */ /* our one and only identification descriptor (vendor-specific) */ - if (buflen > (strlen(inq_83_str) + 4 + 4)) { + if (buflen > (strlen(inq_83_str) + 4 + 4 - 1)) { rbuf[4 + 0] = 2; /* code set: ASCII */ rbuf[4 + 3] = strlen(inq_83_str); memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str)); diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 6bc7de46bdc5..4347594a6bc0 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -118,6 +118,7 @@ static struct { * Other types of devices that have special flags. * Note that all USB devices should have the BLIST_INQUIRY_36 flag. */ + {"3PARdata", "VV", NULL, BLIST_REPORTLUN2}, {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN}, {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN}, {"AFT PRO", "-IX CF", "0.0>", BLIST_FORCELUN}, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fd02a5b47678..ceee6c28318d 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -728,7 +728,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, req->sense_len = len; } } else - req->data_len -= cmd->bufflen; + req->data_len = cmd->resid; } /* diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 57fe7b126fe1..f01133b4c580 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -361,9 +361,8 @@ static ssize_t store_spi_host_signalling(struct class_device *cdev, enum spi_signal_type type = spi_signal_to_value(buf); if (type != SPI_SIGNAL_UNKNOWN) - return count; + i->f->set_signalling(shost, type); - i->f->set_signalling(shost, type); return count; } static CLASS_DEVICE_ATTR(signalling, S_IRUGO | S_IWUSR, @@ -635,7 +634,11 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) /* OK, now we have our initial speed set by the read only inquiry * test, now try an echo buffer test (if the device allows it) */ - if ((len = spi_dv_device_get_echo_buffer(sreq, buffer)) == 0) { + len = 0; + if (sdev->ppr) + len = spi_dv_device_get_echo_buffer(sreq, buffer); + + if (len == 0) { SPI_PRINTK(sdev->sdev_target, KERN_INFO, "Domain Validation skipping write tests\n"); return; } diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 0cee4b9460e9..ac57fdc8711b 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -710,6 +710,24 @@ static struct console amba_console = { .data = &amba_reg, }; +static int __init amba_console_init(void) +{ + /* + * All port initializations are done statically + */ + register_console(&amba_console); + return 0; +} +console_initcall(amba_console_init); + +static int __init amba_late_console_init(void) +{ + if (!(amba_console.flags & CON_ENABLED)) + register_console(&amba_console); + return 0; +} +late_initcall(amba_late_console_init); + #define AMBA_CONSOLE &amba_console #else #define AMBA_CONSOLE NULL diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index f489fc1bb923..8caaf2e5e47c 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -143,6 +143,11 @@ receive_chars(struct uart_sunsab_port *up, writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr); } + /* Count may be zero for BRK, so we check for it here */ + if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) && + (up->port.line == up->port.cons->index)) + saw_console_brk = 1; + for (i = 0; i < count; i++) { unsigned char ch = buf[i]; @@ -172,8 +177,6 @@ receive_chars(struct uart_sunsab_port *up, stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR | SAB82532_ISR0_FERR); up->port.icount.brk++; - if (up->port.line == up->port.cons->index) - saw_console_brk = 1; /* * We do the SysRQ and SAK checking * here because otherwise the break @@ -325,8 +328,9 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs) tty = NULL; if (status.stat) { - if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | - SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) + if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | + SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || + (status.sreg.isr1 & SAB82532_ISR1_BRK)) tty = receive_chars(up, &status, regs); if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || (status.sreg.isr1 & SAB82532_ISR1_CSC)) @@ -352,8 +356,10 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs) tty = NULL; if (status.stat) { - if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | - SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) + if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | + SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || + (status.sreg.isr1 & SAB82532_ISR1_BRK)) + tty = receive_chars(up, &status, regs); if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC))) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a50a648a556b..ff47c18a01e1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -523,13 +523,12 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_lock_device(dev); list_del_init(&ps->list); - - if (connected(dev)) { - for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) - if (test_bit(ifnum, &ps->ifclaimed)) - releaseintf(ps, ifnum); - destroy_all_async(ps); + for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); + ifnum++) { + if (test_bit(ifnum, &ps->ifclaimed)) + releaseintf(ps, ifnum); } + destroy_all_async(ps); usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; @@ -1023,7 +1022,7 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) int ret; add_wait_queue(&ps->wait, &wait); - while (connected(dev)) { + for (;;) { __set_current_state(TASK_INTERRUPTIBLE); if ((as = async_getcompleted(ps))) break; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 24452d66d576..58e3e9151dfb 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -695,7 +695,7 @@ void usbfs_add_device(struct usb_device *dev) for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) { struct usb_config_descriptor *config = (struct usb_config_descriptor *)dev->rawdescriptors[i]; - i_size += le16_to_cpu (config->wTotalLength); + i_size += le16_to_cpu ((__force __le16)config->wTotalLength); } if (dev->usbfs_dentry->d_inode) dev->usbfs_dentry->d_inode->i_size = i_size; @@ -715,6 +715,7 @@ void usbfs_remove_device(struct usb_device *dev) } while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); + wake_up_all(&ds->wait); list_del_init(&ds->list); if (ds->discsignr) { sinfo.si_signo = SIGPIPE; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f742bd95bd99..256787e33bde 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -100,14 +100,16 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. -config USB_SL811HS - tristate "SL811HS support" - depends on ARM && USB +config USB_SL811_HCD + tristate "SL811HS HCD support" + depends on USB + default N help - Say Y here if you have a SL811HS USB host controller in your system. - - If you do not know what this is, please say N. + The SL811HS is a single-port USB controller that supports either + host side or peripheral side roles. Enable this option if your + board has this chip, and you want to use it as a host controller. + If unsure, say N. To compile this driver as a module, choose M here: the - module will be called hc_sl811. + module will be called sl811-hcd. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 15ee81a6340c..a574ca06cf6b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -6,6 +6,5 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o - -obj-$(CONFIG_USB_SL811HS) += hc_sl811.o +obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 10c6ffbfb6fa..a9695086304f 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -286,7 +286,7 @@ ehci_hub_descriptor ( if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ #endif - desc->wHubCharacteristics = cpu_to_le16 (temp); + desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 34b10b794375..69107cc87f18 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -83,19 +83,59 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, /*-------------------------------------------------------------------------*/ -/* update halted (but potentially linked) qh */ - static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); qh->hw_alt_next = EHCI_LIST_END; + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { + unsigned is_out, epnum; + + is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); + epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; + if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { + qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); + usb_settoggle (qh->dev, epnum, is_out, 1); + } + } + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING); } +/* if it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void +qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + + if (list_empty (&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry (qh->qtd_list.next, + struct ehci_qtd, qtd_list); + /* first qtd may already be partially processed */ + if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current) + qtd = NULL; + } + + if (qtd) + qh_update (ehci, qh, qtd); +} + /*-------------------------------------------------------------------------*/ static void qtd_copy_status ( @@ -226,6 +266,11 @@ __acquires(ehci->lock) spin_lock (&ehci->lock); } +static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void intr_deschedule (struct ehci_hcd *ehci, + struct ehci_qh *qh, int wait); +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* * Process and free completed qtds for a qh, returning URBs to drivers. @@ -369,21 +414,27 @@ halt: /* restore original state; caller must unlink or relink */ qh->qh_state = state; - /* update qh after fault cleanup */ - if (unlikely (stopped != 0) - /* some EHCI 0.95 impls will overlay dummy qtds */ - || qh->hw_qtd_next == EHCI_LIST_END) { - if (list_empty (&qh->qtd_list)) - end = qh->dummy; - else { - end = list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list); - /* first qtd may already be partially processed */ - if (cpu_to_le32 (end->qtd_dma) == qh->hw_current) - end = NULL; + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(ehci, qh); + break; + case QH_STATE_LINKED: + /* should be rare for periodic transfers, + * except maybe high bandwidth ... + */ + if (qh->period) { + intr_deschedule (ehci, qh, 1); + (void) qh_schedule (ehci, qh); + } else + start_unlink_async (ehci, qh); + break; + /* otherwise, unlink already started */ } - if (end) - qh_update (ehci, qh, end); } return count; @@ -557,21 +608,6 @@ cleanup: /*-------------------------------------------------------------------------*/ -/* - * Hardware maintains data toggle (like OHCI) ... here we (re)initialize - * the hardware data toggle in the QH, and set the pseudo-toggle in udev - * so we can see if usb_clear_halt() was called. NOP for control, since - * we set up qh->hw_info1 to always use the QTD toggle bits. - */ -static inline void -clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) -{ - vdbg ("clear toggle, dev %d ep 0x%x-%s", - udev->devnum, ep, is_out ? "out" : "in"); - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (udev, ep, is_out, 1); -} - // Would be best to create all qh's from config descriptors, // when each interface/altsetting is established. Unlink // any previous qh and cancel its urbs first; endpoints are @@ -651,11 +687,11 @@ qh_make ( qh->period = urb->interval; } - - /* support for tt scheduling */ - qh->dev = usb_get_dev (urb->dev); } + /* support for tt scheduling, and access to toggles */ + qh->dev = usb_get_dev (urb->dev); + /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: @@ -715,8 +751,8 @@ done: qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); - qh_update (ehci, qh, qh->dummy); usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); + qh_refresh (ehci, qh); return qh; } @@ -745,7 +781,9 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } } - qh->hw_token &= ~HALT_BIT; + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + if (qh->qh_state == QH_STATE_IDLE) + qh_refresh (ehci, qh); /* splice right after start */ qh->qh_next = head->qh_next; @@ -820,27 +858,8 @@ static struct ehci_qh *qh_append_tds ( qh->hw_info1 &= ~QH_ADDR_MASK; } - /* usb_clear_halt() means qh data toggle gets reset */ - if (unlikely (!usb_gettoggle (urb->dev, - (epnum & 0x0f), !(epnum & 0x10))) - && !usb_pipecontrol (urb->pipe)) { - /* "never happens": drivers do stall cleanup right */ - if (qh->qh_state != QH_STATE_IDLE - && !list_empty (&qh->qtd_list) - && qh->qh_state != QH_STATE_COMPLETING) - ehci_warn (ehci, "clear toggle dev%d " - "ep%d%s: not idle\n", - usb_pipedevice (urb->pipe), - epnum & 0x0f, - usb_pipein (urb->pipe) - ? "in" : "out"); - /* else we know this overlay write is safe */ - clear_toggle (urb->dev, - epnum & 0x0f, !(epnum & 0x10), qh); - } - /* just one way to queue requests: swap with the dummy qtd. - * only hc or qh_completions() usually modify the overlay. + * only hc or qh_refresh() ever modify the overlay. */ if (likely (qtd != 0)) { struct ehci_qtd *dummy; @@ -936,8 +955,6 @@ submit_async ( /* the async qh for the qtds being reclaimed are now unlinked from the HC */ -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); - static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) { struct ehci_qh *qh = ehci->reclaim; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 31ec1597f49f..74bdd5bfcc25 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -325,7 +325,7 @@ static void intr_deschedule ( status = disable_periodic (ehci); else { status = 0; - vdbg ("periodic schedule still enabled"); + ehci_vdbg (ehci, "periodic schedule still enabled\n"); } /* @@ -342,7 +342,7 @@ static void intr_deschedule ( * the race is very short. then if qh also isn't * rescheduled soon, it won't matter. otherwise... */ - vdbg ("intr_deschedule..."); + ehci_vdbg (ehci, "intr_deschedule...\n"); } } else qh->hw_next = EHCI_LIST_END; @@ -450,6 +450,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) __le32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + qh_refresh(ehci, qh); qh->hw_next = EHCI_LIST_END; frame = qh->start; diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c index 20650f30b606..770c8709e6fa 100644 --- a/drivers/usb/host/hc_crisv10.c +++ b/drivers/usb/host/hc_crisv10.c @@ -30,7 +30,7 @@ #include <../drivers/usb/core/hcd.h> #include <../drivers/usb/core/usb.h> -#include "usb-host.h" +#include "hc_crisv10.h" #define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR diff --git a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c deleted file mode 100644 index ade67124afb8..000000000000 --- a/drivers/usb/host/hc_simple.c +++ /dev/null @@ -1,1039 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) - * for embedded HCs (SL811HS) - * - * USB URB handling, hci_ hcs_ - * URB queueing, qu_ - * Transfer scheduling, sh_ - * - * - *-------------------------------------------------------------------------* - * 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 - * - *-------------------------------------------------------------------------*/ - -/* main lock for urb access */ -static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------*/ -/* URB HCD API function layer - * * * */ - -/*************************************************************************** - * Function Name : hcs_urb_queue - * - * This function initializes the urb status and length before queueing the - * urb. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 - **************************************************************************/ -static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) -{ - int i; - - DBGFUNC ("enter hcs_urb_queue\n"); - if (usb_pipeisoc (urb->pipe)) { - DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EXDEV; - } - - /* urb->next hack : 1 .. resub, 0 .. single shot */ - /* urb->interval = urb->next ? 1 : 0; */ - } - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - - if (usb_pipecontrol (urb->pipe)) - hc_flush_data_cache (hci, urb->setup_packet, 8); - if (usb_pipeout (urb->pipe)) - hc_flush_data_cache (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - - qu_queue_urb (hci, urb); - - return 0; -} - -/*************************************************************************** - * Function Name : hcs_return_urb - * - * This function the return path of URB back to the USB core. It calls the - * the urb complete function if exist, and also handles the resubmition of - * interrupt URBs. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit - * - * Return: 0 - **************************************************************************/ -static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct usb_device *dev = urb->dev; - int resubmit = 0; - - DBGFUNC ("enter hcs_return_urb, urb pointer = 0x%x, " - "transferbuffer point = 0x%x, " - " setup packet pointer = 0x%x, context pointer = 0x%x \n", - (__u32 *) urb, (__u32 *) urb->transfer_buffer, - (__u32 *) urb->setup_packet, (__u32 *) urb->context); - if (urb_debug) - urb_print (urb, "RET", usb_pipeout (urb->pipe)); - - resubmit = urb->interval && resub_ok; - - urb->dev = urb->hcpriv = NULL; - - if (urb->complete) { - urb->complete (urb, NULL); /* call complete */ - } - - if (resubmit) { - /* requeue the URB */ - urb->dev = dev; - hcs_urb_queue (hci, urb); - } else { - usb_put_urb(urb); - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_submit_urb - * - * This function is called by the USB core API when an URB is available to - * process. This function does the following - * - * 1) Check the validity of the URB - * 2) Parse the device number from the URB - * 3) Pass the URB to the root hub routine if its intended for the hub, else - * queue the urb for the attached device. - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_submit_urb (struct urb * urb, int mem_flags) -{ - hci_t *hci; - unsigned int pipe = urb->pipe; - unsigned long flags; - int ret; - - DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); - if (!urb->dev || !urb->dev->bus || urb->hcpriv) - return -EINVAL; - - hci = (hci_t *) urb->dev->bus->hcpriv; - - /* a request to the virtual root hub */ - if (usb_pipedevice (pipe) == hci->rh.devnum) { - if (urb_debug > 1) - urb_print (urb, "SUB-RH", usb_pipein (pipe)); - - return rh_submit_urb (urb); - } - - /* increment urb's reference count, we now control it. */ - urb = usb_get_urb (urb); - - /* queue the URB to its endpoint-queue */ - spin_lock_irqsave (&usb_urb_lock, flags); - ret = hcs_urb_queue (hci, urb); - if (ret != 0) { - /* error on return */ - DBGERR ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", - ret, urb->status); - usb_put_urb (urb); - } - - spin_unlock_irqrestore (&usb_urb_lock, flags); - - return ret; - -} - -/*************************************************************************** - * Function Name : hci_unlink_urb - * - * This function mark the URB to unlink - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_unlink_urb (struct urb * urb, int status) -{ - unsigned long flags; - hci_t *hci; - DECLARE_WAITQUEUE (wait, current); - void *comp = NULL; - - DBGFUNC ("enter hci_unlink_urb\n"); - - if (!urb) /* just to be sure */ - return -EINVAL; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - hci = (hci_t *) urb->dev->bus->hcpriv; - - /* a request to the virtual root hub */ - if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { - return rh_unlink_urb (urb); - } - - if (urb_debug) - urb_print (urb, "UNLINK", 1); - - spin_lock_irqsave (&usb_urb_lock, flags); - - if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { - /* URB active? */ - - /* asynchronous with callback */ - /* relink the urb to the del list */ - list_move (&urb->urb_list, &hci->del_list); - urb->status = status; - spin_unlock_irqrestore (&usb_urb_lock, flags); - } else { - /* hcd does not own URB but we keep the driver happy anyway */ - spin_unlock_irqrestore (&usb_urb_lock, flags); - - if (urb->complete) { - urb->status = status; - urb->actual_length = 0; - urb->complete (urb, NULL); - if (urb->reject) - wake_up (&usb_kill_urb_queue); - } - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_alloc_dev - * - * This function allocates private data space for the usb device and - * initialize the endpoint descriptor heads. - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_alloc_dev (struct usb_device *usb_dev) -{ - struct hci_device *dev; - int i; - - DBGFUNC ("enter hci_alloc_dev\n"); - dev = kmalloc (sizeof (*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - memset (dev, 0, sizeof (*dev)); - - for (i = 0; i < 32; i++) { - INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); - dev->ed[i].pipe_head = NULL; - } - - usb_dev->hcpriv = dev; - - DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); - - return 0; - -} - -/*************************************************************************** - * Function Name : hci_free_dev - * - * This function de-allocates private data space for the usb devic - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 - **************************************************************************/ -static int hci_free_dev (struct usb_device *usb_dev) -{ - DBGFUNC ("enter hci_free_dev\n"); - - if (usb_dev->hcpriv) - kfree (usb_dev->hcpriv); - - usb_dev->hcpriv = NULL; - - return 0; -} - -/*************************************************************************** - * Function Name : hci_get_current_frame_number - * - * This function get the current USB frame number - * - * Input: usb_dev = pointer to the usb device - * - * Return: frame number - **************************************************************************/ -static int hci_get_current_frame_number (struct usb_device *usb_dev) -{ - hci_t *hci = usb_dev->bus->hcpriv; - DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", - hci->frame_number); - - return (hci->frame_number); -} - -/*************************************************************************** - * List of all io-functions - **************************************************************************/ - -static struct usb_operations hci_device_operations = { - .allocate = hci_alloc_dev, - .deallocate = hci_free_dev, - .get_frame_number = hci_get_current_frame_number, - .submit_urb = hci_submit_urb, - .unlink_urb = hci_unlink_urb, -}; - -/*************************************************************************** - * URB queueing: - * - * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of - * active URBs. - * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) - * For every endpoint the head URB of the queued URBs is linked to one of - * those lists. - * - * The rest of the queued URBs of an endpoint are linked into a - * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) - * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is - * in one of the active URB lists. - * - * The index of an endpoint consists of its number and its direction. - * - * The state of an intr and iso URB is 0. - * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK - * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) - * - **************************************************************************/ - -/*************************************************************************** - * Function Name : qu_urb_timeout - * - * This function is called when the URB timeout. The function unlinks the - * URB. - * - * Input: lurb: URB - * - * Return: none - **************************************************************************/ -#ifdef HC_URB_TIMEOUT -static void qu_urb_timeout (unsigned long lurb) -{ - struct urb *urb = (struct urb *) lurb; - - DBGFUNC ("enter qu_urb_timeout\n"); - hci_unlink_urb (urb); -} -#endif - -/*************************************************************************** - * Function Name : qu_pipeindex - * - * This function gets the index of the pipe. - * - * Input: pipe: the urb pipe - * - * Return: index - **************************************************************************/ -static inline int qu_pipeindex (__u32 pipe) -{ - DBGFUNC ("enter qu_pipeindex\n"); - return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); -} - -/*************************************************************************** - * Function Name : qu_seturbstate - * - * This function set the state of the URB. - * - * control pipe: 3 states -- Setup, data, status - * interrupt and bulk pipe: 1 state -- data - * - * Input: urb = USB request block data structure - * state = the urb state - * - * Return: none - **************************************************************************/ -static inline void qu_seturbstate (struct urb * urb, int state) -{ - DBGFUNC ("enter qu_seturbstate\n"); - urb->pipe &= ~0x1f; - urb->pipe |= state & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_urbstate - * - * This function get the current state of the URB. - * - * Input: urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static inline int qu_urbstate (struct urb * urb) -{ - - DBGFUNC ("enter qu_urbstate\n"); - - return urb->pipe & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_queue_active_urb - * - * This function adds the urb to the appropriate active urb list and set - * the urb state. - * - * There are four active lists: isochoronous list, interrupt list, - * control list, and bulk list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * ed = endpoint descriptor - * - * Return: none - **************************************************************************/ -static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) -{ - int urb_state = 0; - DBGFUNC ("enter qu_queue_active_urb\n"); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - list_add (&urb->urb_list, &hci->ctrl_list); - urb_state = US_CTRL_SETUP; - break; - - case PIPE_BULK: - list_add (&urb->urb_list, &hci->bulk_list); - if ((urb->transfer_flags & URB_ZERO_PACKET) - && urb->transfer_buffer_length > 0 - && - ((urb->transfer_buffer_length % - usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe))) == 0)) { - urb_state = US_BULK0; - } - break; - - case PIPE_INTERRUPT: - urb->start_frame = hci->frame_number; - list_add (&urb->urb_list, &hci->intr_list); - break; - - case PIPE_ISOCHRONOUS: - list_add (&urb->urb_list, &hci->iso_list); - break; - } - -#ifdef HC_URB_TIMEOUT - if (urb->timeout) { - ed->timeout.data = (unsigned long) urb; - ed->timeout.expires = urb->timeout + jiffies; - ed->timeout.function = qu_urb_timeout; - add_timer (&ed->timeout); - } -#endif - - qu_seturbstate (urb, urb_state); -} - -/*************************************************************************** - * Function Name : qu_queue_urb - * - * This function adds the urb to the endpoint descriptor list - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static int qu_queue_urb (hci_t * hci, struct urb * urb) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("Enter qu_queue_urb\n"); - - /* for ISOC transfers calculate start frame index */ - - if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & URB_ISO_ASAP) { - urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; - } - - if (ed->pipe_head) { - __list_add (&urb->urb_list, ed->urb_queue.prev, - &(ed->urb_queue)); - } else { - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - if (++hci->active_urbs == 1) - hc_start_int (hci); - } - - return 0; -} - -/*************************************************************************** - * Function Name : qu_next_urb - * - * This function removes the URB from the queue and add the next URB to - * active list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("enter qu_next_urb\n"); - list_del_init(&urb->urb_list); - - if (ed->pipe_head == urb) { -#ifdef HC_URB_TIMEOUT - if (urb->timeout) - del_timer (&ed->timeout); -#endif - - if (!--hci->active_urbs) - hc_stop_int (hci); - - if (!list_empty (&ed->urb_queue)) { - urb = list_entry (ed->urb_queue.next, struct urb, urb_list); - list_del_init (&urb->urb_list); - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - } else { - ed->pipe_head = NULL; - urb = NULL; - } - } - return urb; -} - -/*************************************************************************** - * Function Name : qu_return_urb - * - * This function is part of the return path. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct urb *next_urb; - - DBGFUNC ("enter qu_return_rub\n"); - next_urb = qu_next_urb (hci, urb, resub_ok); - hcs_return_urb (hci, urb, resub_ok); - return next_urb; -} - -/*************************************************************************** - * Function Name : sh_scan_iso_urb_list - * - * This function goes through the isochronous urb list and schedule the - * the transfer. - * - * Note: This function has not tested yet - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * frame_number = the frame number - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, - int frame_number) -{ - struct list_head *lh = list_lh->next; - struct urb *urb; - - DBGFUNC ("enter sh_scan_iso_urb_list\n"); - hci->td_array->len = 0; - - while (lh != list_lh) { - urb = list_entry (lh, struct urb, urb_list); - lh = lh->next; - if (((frame_number - urb->start_frame) & 0x7ff) < - urb->number_of_packets) { - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - if (((frame_number - - urb->start_frame) & 0x7ff) > 0x400) { - if (qu_urbstate (urb) > 0) - urb = qu_return_urb (hci, urb, 1); - else - urb = qu_next_urb (hci, urb, 1); - - if (lh == list_lh && urb) - lh = &urb->urb_list; - } - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_scan_urb_list - * - * This function goes through the urb list and schedule the - * the transaction. - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) -{ - struct list_head *lh = NULL; - struct urb *urb; - - if (list_lh == NULL) { - DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); - } - - DBGFUNC ("enter sh_scan_urb_list: frame# \n"); - - list_for_each (lh, list_lh) { - urb = list_entry (lh, struct urb, urb_list); - if (urb == NULL) - return 1; - if (!usb_pipeint (urb->pipe) - || (((hci->frame_number - urb->start_frame) - & 0x7ff) >= urb->interval)) { - DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, urb->interval, - usb_pipeint (urb->pipe)); - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, - urb->interval, usb_pipeint (urb->pipe)); - urb->start_frame = hci->frame_number; - return 0; - - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_shedule_trans - * - * This function schedule the USB transaction. - * This function will process the endpoint in the following order: - * interrupt, control, and bulk. - * - * Input: hci = data structure for the host controller - * isSOF = flag indicate if Start Of Frame has occurred - * - * Return: 0 - **************************************************************************/ -static int sh_schedule_trans (hci_t * hci, int isSOF) -{ - int units_left = 1; - struct list_head *lh; - - if (hci == NULL) { - DBGERR ("sh_schedule_trans: hci == NULL\n"); - return 0; - } - if (hci->td_array == NULL) { - DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); - return 0; - } - - if (hci->td_array->len != 0) { - DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - } - - /* schedule the next available interrupt transfer or the next - * stage of the interrupt transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { - units_left = sh_scan_urb_list (hci, &hci->intr_list); - } - - /* schedule the next available control transfer or the next - * stage of the control transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { - units_left = sh_scan_urb_list (hci, &hci->ctrl_list); - } - - /* schedule the next available bulk transfer or the next - * stage of the bulk transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { - sh_scan_urb_list (hci, &hci->bulk_list); - - /* be fair to each BULK URB (move list head around) - * only when the new SOF happens */ - - lh = hci->bulk_list.next; - list_move (&hci->bulk_list, lh); - } - return 0; -} - -/*************************************************************************** - * Function Name : sh_add_packet - * - * This function forms the packet and transmit the packet. This function - * will handle all endpoint type: isochoronus, interrupt, control, and - * bulk. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 = unsucessful; 1 = successful - **************************************************************************/ -static int sh_add_packet (hci_t * hci, struct urb * urb) -{ - __u8 *data = NULL; - int len = 0; - int toggle = 0; - int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - int endpoint = usb_pipeendpoint (urb->pipe); - int address = usb_pipedevice (urb->pipe); - int slow = (((urb->pipe) >> 26) & 1); - int out = usb_pipeout (urb->pipe); - int pid = 0; - int ret; - int i = 0; - int iso = 0; - - DBGFUNC ("enter sh_add_packet\n"); - if (maxps == 0) - maxps = 8; - - /* calculate len, toggle bit and add the transaction */ - switch (usb_pipetype (urb->pipe)) { - case PIPE_ISOCHRONOUS: - pid = out ? PID_OUT : PID_IN; - iso = 1; - i = hci->frame_number - urb->start_frame; - data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - len = urb->iso_frame_desc[i].length; - break; - - case PIPE_BULK: /* BULK and BULK0 */ - case PIPE_INTERRUPT: - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, endpoint, out); - break; - - case PIPE_CONTROL: - switch (qu_urbstate (urb)) { - case US_CTRL_SETUP: - len = 8; - pid = PID_SETUP; - data = urb->setup_packet; - toggle = 0; - break; - - case US_CTRL_DATA: - if (!hci->last_packet_nak) { - /* The last packet received is not a nak: - * reset the nak count - */ - - hci->nakCnt = 0; - } - if (urb->transfer_buffer_length != 0) { - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = (urb->actual_length & maxps) ? 0 : 1; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } else { - /* correct state and fall through */ - qu_seturbstate (urb, US_CTRL_ACK); - } - - case US_CTRL_ACK: - len = 0; - - /* reply in opposite direction */ - pid = !out ? PID_OUT : PID_IN; - toggle = 1; - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } - } - - ret = - hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, - address, pid, iso, qu_urbstate (urb)); - - DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " - "max:%d\n len:%d ret:%d data:%p left:%d\n", - address, endpoint, pid, toggle, iso, slow, - maxps, len, ret, data, hci->hp.units_left); - - if (ret >= 0) { - hci->td_array->td[hci->td_array->len].urb = urb; - hci->td_array->td[hci->td_array->len].len = ret; - hci->td_array->td[hci->td_array->len].iso_index = i; - hci->td_array->len++; - hci->active_trans = 1; - return 1; - } - return 0; -} - -/*************************************************************************** - * Function Name : cc_to_error - * - * This function maps the SL811HS hardware error code to the linux USB error - * code. - * - * Input: cc = hardware error code - * - * Return: USB error code - **************************************************************************/ -static int cc_to_error (int cc) -{ - int errCode = 0; - if (cc & SL11H_STATMASK_ERROR) { - errCode |= -EILSEQ; - } else if (cc & SL11H_STATMASK_OVF) { - errCode |= -EOVERFLOW; - } else if (cc & SL11H_STATMASK_STALL) { - errCode |= -EPIPE; - } - return errCode; -} - -/*************************************************************************** - * Function Name : sh_done_list - * - * This function process the packet when it has done finish transfer. - * - * 1) It handles hardware error - * 2) It updates the URB state - * 3) If the USB transaction is complete, it start the return stack path. - * - * Input: hci = data structure for the host controller - * isExcessNak = flag tells if there excess NAK condition occurred - * - * Return: urb_state or -1 if the transaction has complete - **************************************************************************/ -static int sh_done_list (hci_t * hci, int *isExcessNak) -{ - int actbytes = 0; - int active = 0; - void *data = NULL; - int cc; - int maxps; - int toggle; - struct urb *urb; - int urb_state = 0; - int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ - int trans = 0; - int len; - int iso_index = 0; - int out; - int pid = 0; - int debugLen = 0; - - *isExcessNak = 0; - - DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", - hci->td_array->len); - - debugLen = hci->td_array->len; - if (debugLen > 1) - DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", - hci->td_array->len); - - for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; - trans++) { - urb = hci->td_array->td[trans].urb; - len = hci->td_array->td[trans].len; - out = usb_pipeout (urb->pipe); - - if (usb_pipeisoc (urb->pipe)) { - iso_index = hci->td_array->td[trans].iso_index; - data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; - toggle = 0; - } else { - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)); - - } - urb_state = qu_urbstate (urb); - pid = out ? PID_OUT : PID_IN; - ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, - pid, urb_state); - maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - - if (maxps == 0) - maxps = 8; - - active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); - - /* If the transfer is not bulk in, then it is necessary to get all - * data specify by the urb->transfer_len. - */ - - if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) - active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); - - if (urb->transfer_buffer_length == urb->actual_length + actbytes) - active = 0; - - if ((cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | - SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) - && !(cc & SL11H_STATMASK_NAK)) { - if (++urb->error_count > 3) { - DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", - urb->error_count, cc); - urb_state = 0; - active = 0; - } else { - DBGERR ("done_list: packet err, cc = 0x%x, " - " urb->length = 0x%x, actual_len = 0x%x," - " urb_state =0x%x\n", - cc, urb->transfer_buffer_length, - urb->actual_length, urb_state); -// if (cc & SL11H_STATMASK_STALL) { - /* The USB function is STALLED on a control pipe (0), - * then it needs to send the SETUP command again to - * clear the STALL condition - */ - -// if (usb_pipeendpoint (urb->pipe) == 0) { -// urb_state = 2; -// active = 0; -// } -// } else - active = 1; - } - } else { - if (cc & SL11H_STATMASK_NAK) { - if (hci->nakCnt < 0x10000) { - hci->nakCnt++; - hci->last_packet_nak = 1; - active = 1; - *isExcessNak = 0; - } else { - DBGERR ("done_list: nak count exceed limit\n"); - active = 0; - *isExcessNak = 1; - hci->nakCnt = 0; - } - } else { - hci->nakCnt = 0; - hci->last_packet_nak = 0; - } - - if (urb_state != US_CTRL_SETUP) { - /* no error */ - urb->actual_length += actbytes; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - } - if (usb_pipeisoc (urb->pipe)) { - urb->iso_frame_desc[iso_index].actual_length = actbytes; - urb->iso_frame_desc[iso_index].status = cc_to_error (cc); - active = (iso_index < urb->number_of_packets); - } - } - if (!active) { - if (!urb_state) { - urb->status = cc_to_error (cc); - if (urb->status) { - DBGERR ("error on received packet: urb->status = 0x%x\n", - urb->status); - } - hci->td_array->len = 0; - qu_return_urb (hci, urb, 1); - return -1; - } else { - /* We do not want to decrement the urb_state if exceeded nak, - * because we need to finish the data stage of the control - * packet - */ - - if (!(*isExcessNak)) - urb_state--; - qu_seturbstate (urb, urb_state); - } - } - } - - if (urb_state < 0) - DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", - urb_state); - if (debugLen != hci->td_array->len) { - DBGERR ("ERROR: done_list, debugLen!= td_array->len," - "debugLen = 0x%x, hci->td_array->len = 0x%x\n", - debugLen, hci->td_array->len); - } - - hci->td_array->len = 0; - - return urb_state; -} diff --git a/drivers/usb/host/hc_simple.h b/drivers/usb/host/hc_simple.h deleted file mode 100644 index d0289f62f058..000000000000 --- a/drivers/usb/host/hc_simple.h +++ /dev/null @@ -1,231 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/* list of all controllers using this driver - * */ - -static LIST_HEAD (hci_hcd_list); - -/* URB states (urb_state) */ -/* isoc, interrupt single state */ - -/* bulk transfer main state and 0-length packet */ -#define US_BULK 0 -#define US_BULK0 1 -/* three setup states */ -#define US_CTRL_SETUP 2 -#define US_CTRL_DATA 1 -#define US_CTRL_ACK 0 - -/*-------------------------------------------------------------------------*/ -/* HC private part of a device descriptor - * */ - -#define NUM_EDS 32 - -typedef struct epd { - struct urb *pipe_head; - struct list_head urb_queue; -// int urb_state; - struct timer_list timeout; - int last_iso; /* timestamp of last queued ISOC transfer */ - -} epd_t; - -struct hci_device { - epd_t ed[NUM_EDS]; -}; - -/*-------------------------------------------------------------------------*/ -/* Virtual Root HUB - */ - -#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) - -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *urb; /* interrupt URB of root hub */ - int send; /* active flag */ - int interval; /* interval of roothub interrupt transfers */ - struct timer_list rh_int_timer; /* interval timer for rh interrupt EP */ -}; - -#if 1 -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#endif - -/*-------------------------------------------------------------------------*/ -/* struct for each HC - */ - -#define MAX_TRANS 32 - -typedef struct td { - struct urb *urb; - __u16 len; - __u16 iso_index; -} td_t; - -typedef struct td_array { - int len; - td_t td[MAX_TRANS]; -} td_array_t; - -typedef struct hci { - struct virt_root_hub rh; /* roothub */ - wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ - int active; /* HC is operating */ - - struct list_head ctrl_list; /* set of ctrl endpoints */ - struct list_head bulk_list; /* set of bulk endpoints */ - struct list_head iso_list; /* set of isoc endpoints */ - struct list_head intr_list; /* ordered (tree) set of int endpoints */ - struct list_head del_list; /* set of entpoints to be deleted */ - - td_array_t *td_array; - td_array_t a_td_array; - td_array_t i_td_array[2]; - - struct list_head hci_hcd_list; /* list of all hci_hcd */ - struct usb_bus *bus; /* our bus */ - -// int trans; /* number of transactions pending */ - int active_urbs; - int active_trans; - int frame_number; /* frame number */ - hcipriv_t hp; /* individual part of hc type */ - int nakCnt; - int last_packet_nak; - -} hci_t; - -/*-------------------------------------------------------------------------*/ -/* condition (error) CC codes and mapping OHCI like - */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -/* urb interface functions */ -static int hci_get_current_frame_number (struct usb_device *usb_dev); -static int hci_unlink_urb (struct urb * urb); - -static int qu_queue_urb (hci_t * hci, struct urb * urb); - -/* root hub */ -static int rh_init_int_timer (struct urb * urb); -static int rh_submit_urb (struct urb * urb); -static int rh_unlink_urb (struct urb * urb); - -/* schedule functions */ -static int sh_add_packet (hci_t * hci, struct urb * urb); - -/* hc specific functions */ -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state); -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state); - -static void hc_start_int (hci_t * hci); -static void hc_stop_int (hci_t * hci); -static void SL811Write (hci_t * hci, char offset, char data); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void urb_print (struct urb * urb, char *str, int small) -{ - unsigned int pipe = urb->pipe; - int i, len; - - if (!urb->dev || !urb->dev->bus) { - dbg ("%s URB: no dev", str); - return; - } - - printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", - str, hci_get_current_frame_number (urb->dev), - usb_pipedevice (pipe), usb_pipeendpoint (pipe), - usb_pipeout (pipe) ? 'O' : 'I', - usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") - : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, - urb->actual_length, urb->transfer_buffer_length, urb->status, - urb->status); - if (!small) { - if (usb_pipecontrol (pipe)) { - printk (__FILE__ ": cmd(8):"); - for (i = 0; i < 8; i++) - printk (" %02x", ((__u8 *) urb->setup_packet)[i]); - printk ("\n"); - } - if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (__FILE__ ": data(%d/%d):", urb->actual_length, - urb->transfer_buffer_length); - len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; - for (i = 0; i < 2096 && i < len; i++) - printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); - printk ("%s stat:%d\n", i < len ? "..." : "", - urb->status); - } - } -} diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c deleted file mode 100644 index baf0e8086352..000000000000 --- a/drivers/usb/host/hc_sl811.c +++ /dev/null @@ -1,1357 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) - * - * requires (includes) hc_simple.[hc] simple generic HCD frontend - * - * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/smp_lock.h> -#include <linux/list.h> -#include <linux/ioport.h> -#include <asm/io.h> -#include <asm/irq.h> - -#include <linux/usb.h> -#include "../core/hcd.h" - -#undef HC_URB_TIMEOUT -#undef HC_SWITCH_INT -#undef HC_ENABLE_ISOC - -#define SL811_DEBUG_ERR - -#ifdef SL811_DEBUG_ERR -#define DBGERR(fmt, args...) printk(fmt,## args) -#else -#define DBGERR(fmt, args...) -#endif - -#ifdef SL811_DEBUG -#define DBG(fmt, args...) printk(fmt,## args) -#else -#define DBG(fmt, args...) -#endif - -#ifdef SL811_DEBUG_FUNC -#define DBGFUNC(fmt, args...) printk(fmt,## args) -#else -#define DBGFUNC(fmt, args...) -#endif - -#ifdef SL811_DEBUG_DATA -#define DBGDATAR(fmt, args...) printk(fmt,## args) -#define DBGDATAW(fmt, args...) printk(fmt,## args) -#else -#define DBGDATAR(fmt, args...) -#define DBGDATAW(fmt, args...) -#endif - -#ifdef SL811_DEBUG_VERBOSE -#define DBGVERBOSE(fmt, args...) printk(fmt,## args) -#else -#define DBGVERBOSE(fmt, args...) -#endif - -#define TRUE 1 -#define FALSE 0 - -#define HC_SWITCH_INT -#include "hc_sl811.h" -#include "hc_simple.h" - -static int urb_debug = 0; - -#include "hc_simple.c" -#include "hc_sl811_rh.c" - -/* The base_addr, data_reg_addr, and irq number are board specific. - * The current values are design to run on the Accelent SA1110 IDP - * NOTE: values need to modify for different development boards - */ - -static int base_addr = 0xd3800000; -static int data_reg_addr = 0xd3810000; -static int irq = 34; - -/* forware declaration */ - -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state); - -static int sofWaitCnt = 0; - -module_param(urb_debug, int, 0); -MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); - -module_param(base_addr, int, 0); -MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); -module_param(data_reg_addr, int, 0); -MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); -module_param(irq, int, 0); -MODULE_PARM_DESC (irq, "IRQ 34 (default)"); - -static int hc_reset (hci_t * hci); - -/*************************************************************************** - * Function Name : SL811Read - * - * Read a byte of data from the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * - * Return: data - **************************************************************************/ -char SL811Read (hci_t * hci, char offset) -{ - hcipriv_t *hp = &hci->hp; - char data; - writeb (offset, hp->hcport); - wmb (); - data = readb (hp->hcport2); - rmb (); - return (data); -} - -/*************************************************************************** - * Function Name : SL811Write - * - * Write a byte of data to the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * data = the data going to write to SL811H - * - * Return: none - **************************************************************************/ -void SL811Write (hci_t * hci, char offset, char data) -{ - hcipriv_t *hp = &hci->hp; - writeb (offset, hp->hcport); - writeb (data, hp->hcport2); - wmb (); -} - -/*************************************************************************** - * Function Name : SL811BufRead - * - * Read consecutive bytes of data from the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the buffer where the data will store - * size = number of bytes to read - * - * Return: none - **************************************************************************/ -void SL811BufRead (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAR ("SL811BufRead: offset = 0x%x, data = ", offset); - while (size--) { - *buf++ = (char) readb (hp->hcport2); - DBGDATAR ("0x%x ", *(buf - 1)); - rmb (); - } - DBGDATAR ("\n"); -} - -/*************************************************************************** - * Function Name : SL811BufWrite - * - * Write consecutive bytes of data to the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the data buffer - * size = number of bytes to write - * - * Return: none - **************************************************************************/ -void SL811BufWrite (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAW ("SL811BufWrite: offset = 0x%x, data = ", offset); - while (size--) { - DBGDATAW ("0x%x ", *buf); - writeb (*buf, hp->hcport2); - wmb (); - buf++; - } - DBGDATAW ("\n"); -} - -/*************************************************************************** - * Function Name : regTest - * - * This routine test the Read/Write functionality of SL811HS registers - * - * 1) Store original register value into a buffer - * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) - * 3) Read from register - * 4) Compare the written value with the read value and make sure they are - * equivalent - * 5) Restore the original register value - * - * Input: hci = data structure for the host controller - * - * - * Return: TRUE = passed; FALSE = failed - **************************************************************************/ -int regTest (hci_t * hci) -{ - int i, data, result = TRUE; - char buf[256]; - - DBGFUNC ("Enter regTest\n"); - for (i = 0x10; i < 256; i++) { - /* save the original buffer */ - buf[i] = (char) SL811Read (hci, i); - - /* Write the new data to the buffer */ - SL811Write (hci, i, i); - } - - /* compare the written data */ - for (i = 0x10; i < 256; i++) { - data = SL811Read (hci, i); - if (data != i) { - DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", - data, i); - result = FALSE; - } - } - - /* restore the data */ - for (i = 0x10; i < 256; i++) { - SL811Write (hci, i, buf[i]); - } - - return (result); -} - -/*************************************************************************** - * Function Name : regShow - * - * Display all SL811HS register values - * - * Input: hci = data structure for the host controller - * - * Return: none - **************************************************************************/ -void regShow (hci_t * hci) -{ - int i; - for (i = 0; i < 256; i++) { - printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); - } -} - -/************************************************************************ - * Function Name : USBReset - * - * This function resets SL811HS controller and detects the speed of - * the connecting device - * - * Input: hci = data structure for the host controller - * - * Return: 0 = no device attached; 1 = USB device attached - * - ***********************************************************************/ -static int USBReset (hci_t * hci) -{ - int status; - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("enter USBReset\n"); - - SL811Write (hci, SL11H_CTLREG2, 0xae); - - // setup master and full speed - - SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB - mdelay (20); // 20ms - SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 - - for (status = 0; status < 100; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits - - status = SL811Read (hci, SL11H_INTSTATREG); - - if (status & 0x40) // Check if device is removed - { - DBG ("USBReset: Device removed\n"); - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - hp->RHportStatus->portStatus &= - ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); - - return 0; - } - - SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth - SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 - SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 - SL811Write (hci, SL11H_SOFLOWREG, 0xe0); - - if (!(status & 0x80)) { - /* slow speed device connect directly to root-hub */ - - DBG ("USBReset: low speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xee); - SL811Write (hci, SL11H_CTLREG1, 0x21); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= - (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); - - /* clear all interrupt bits */ - - for (status = 0; status < 20; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } else { - /* full speed device connect directly to root hub */ - - DBG ("USBReset: full speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xae); - SL811Write (hci, SL11H_CTLREG1, 0x01); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); - hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; - - /* clear all interrupt bits */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - } - - /* enable all interrupts */ - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - - return 1; -} - -/*-------------------------------------------------------------------------*/ -/* tl functions */ -static inline void hc_mark_last_trans (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u8 *ptd = hp->tl; - - dbg ("enter hc_mark_last_trans\n"); - if (ptd == NULL) { - printk ("hc_mark_last_trans: ptd = null\n"); - return; - } - if (hp->xferPktLen > 0) - *(ptd + hp->tl_last) |= (1 << 3); -} - -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) -{ -} - -/************************************************************************ - * Function Name : hc_add_trans - * - * This function sets up the SL811HS register and transmit the USB packets. - * - * 1) Determine if enough time within the current frame to send the packet - * 2) Load the data into the SL811HS register - * 3) Set the appropriate command to the register and trigger the transmit - * - * Input: hci = data structure for the host controller - * len = data length - * data = transmitting data - * toggle = USB toggle bit, either 0 or 1 - * maxps = maximum packet size for this endpoint - * slow = speed of the device - * endpoint = endpoint number - * address = USB address of the device - * pid = packet ID - * format = - * urb_state = the current stage of USB transaction - * - * Return: 0 = no time left to schedule the transfer - * 1 = success - * - ***********************************************************************/ -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state) -{ - hcipriv_t *hp = &hci->hp; - __u16 speed; - int ii, jj, kk; - - DBGFUNC ("enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x," - " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, - i address, pid, format); - - if (len > maxps) { - len = maxps; - } - - speed = hp->RHportStatus->portStatus; - if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 1024; - } else { - if (slow) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 2048; - } else -// ii = (8*7 + 6*3)*len + 110; - ii = 8 * len + 256; - } - - ii += 2 * 10 * len; - - jj = SL811Read (hci, SL11H_SOFTMRREG); - kk = (jj & 0xFF) * 64 - ii; - - if (kk < 0) { - DBGVERBOSE - ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," - "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, - hci->active_trans); - return (-1); - } - - if (pid != PID_IN) { - /* Load data into hc */ - - SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); - } - - /* transmit */ - - SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, - toggle, slow, urb_state); - - return len; -} - -/************************************************************************ - * Function Name : hc_parse_trans - * - * This function checks the status of the transmitted or received packet - * and copy the data from the SL811HS register into a buffer. - * - * 1) Check the status of the packet - * 2) If successful, and IN packet then copy the data from the SL811HS register - * into a buffer - * - * Input: hci = data structure for the host controller - * actbytes = pointer to actual number of bytes - * data = data buffer - * cc = packet status - * length = the urb transmit length - * pid = packet ID - * urb_state = the current stage of USB transaction - * - * Return: 0 - ***********************************************************************/ -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state) -{ - __u8 addr; - __u8 len; - - DBGFUNC ("enter hc_parse_trans\n"); - - /* get packet status; convert ack rcvd to ack-not-rcvd */ - - *cc = (int) SL811Read (hci, SL11H_PKTSTATREG); - - if (*cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | - SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { - if (*cc & SL11H_STATMASK_OVF) - DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = " - "0x%x, TX_count=0x%x\n", *cc, - SL811Read (hci, SL11H_BUFLNTHREG), - SL811Read (hci, SL11H_XFERCNTREG)); - - } else { - DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n", - *cc, length); - - /* Successful data */ - if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { - - /* Find the base address */ - addr = SL811Read (hci, SL11H_BUFADDRREG); - - /* Find the Transmit Length */ - len = SL811Read (hci, SL11H_BUFLNTHREG); - - /* The actual data length = xmit length reg - xfer count reg */ - *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); - - if ((data != NULL) && (*actbytes > 0)) { - SL811BufRead (hci, addr, data, *actbytes); - - } else if ((data == NULL) && (*actbytes <= 0)) { - DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", - *actbytes); - return 0; - } - } else if (pid == PID_OUT) { - *actbytes = length; - } else { - // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); - } - *toggle = !*toggle; - } - - return 0; -} - -/************************************************************************ - * Function Name : hc_start_int - * - * This function enables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_start_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - int mask = - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; - SL811Write (hci, IntEna, mask); -#endif -} - -/************************************************************************ - * Function Name : hc_stop_int - * - * This function disables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_stop_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - SL811Write (hci, SL11H_INTSTATREG, 0xff); -// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); - -#endif -} - -/************************************************************************ - * Function Name : handleInsRmvIntr - * - * This function handles the insertion or removal of device on SL811HS. - * It resets the controller and updates the port status - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -void handleInsRmvIntr (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - USBReset (hci); - - /* Changes in connection status */ - - hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; - - /* Port Enable or Disable */ - - if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { - /* device is connected to the port: - * 1) Enable port - * 2) Resume ?? - */ -// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - /* Over Current is not supported by the SL811 HW ?? */ - - /* How about the Port Power ?? */ - - } else { - /* Device has disconnect: - * 1) Disable port - */ - - hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); - hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - } -} - -/***************************************************************** - * - * Function Name: SL11StartXaction - * - * This functions load the registers with appropriate value and - * transmit the packet. - * - * Input: hci = data structure for the host controller - * addr = USB address of the device - * epaddr = endpoint number - * pid = packet ID - * len = data length - * toggle = USB toggle bit, either 0 or 1 - * slow = speed of the device - * urb_state = the current stage of USB transaction - * - * Return: 0 = error; 1 = successful - * - *****************************************************************/ -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state) -{ - - hcipriv_t *hp = &hci->hp; - __u8 cmd = 0; - __u8 setup_data[4]; - __u16 speed; - - speed = hp->RHportStatus->portStatus; - if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { - cmd |= SL11H_HCTLMASK_PREAMBLE; - } - switch (pid) { - case PID_SETUP: - cmd &= SL11H_HCTLMASK_PREAMBLE; - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - break; - - case PID_OUT: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - if (toggle) { - cmd |= SL11H_HCTLMASK_SEQ; - } - break; - - case PID_IN: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP); - break; - - default: - DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); - return 0; - } - setup_data[0] = SL11H_DATA_START; - setup_data[1] = len; - setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); - setup_data[3] = addr & 0x7F; - - SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); - - SL811Write (hci, SL11H_HOSTCTLREG, cmd); - -#if 0 - /* The SL811 has a hardware flaw when hub devices sends out - * SE0 between packets. It has been found in a TI chipset and - * cypress hub chipset. It causes the SL811 to hang - * The workaround is to re-issue the preample again. - */ - - if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { - SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable - } -#endif - return 1; -} - -/***************************************************************** - * - * Function Name: hc_interrupt - * - * Interrupt service routine. - * - * 1) determine the causes of interrupt - * 2) clears all interrupts - * 3) calls appropriate function to service the interrupt - * - * Input: irq = interrupt line associated with the controller - * hci = data structure for the host controller - * r = holds the snapshot of the processor's context before - * the processor entered interrupt code. (not used here) - * - * Return value : None. - * - *****************************************************************/ -static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) -{ - char ii; - hci_t *hci = __hci; - int isExcessNak = 0; - int urb_state = 0; - char tmpIrq = 0; - - /* Get value from interrupt status register */ - - ii = SL811Read (hci, SL11H_INTSTATREG); - - if (ii & SL11H_INTMASK_INSRMV) { - /* Device insertion or removal detected for the USB port */ - - SL811Write (hci, SL11H_INTENBLREG, 0); - SL811Write (hci, SL11H_CTLREG1, 0); - mdelay (100); // wait for device stable - handleInsRmvIntr (hci); - return; - } - - /* Clear all interrupts */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - if (ii & SL11H_INTMASK_XFERDONE) { - /* USB Done interrupt occurred */ - - urb_state = sh_done_list (hci, &isExcessNak); -#ifdef WARNING - if (hci->td_array->len > 0) - printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", - hci->td_array->len); -#endif - if (hci->td_array->len == 0 && !isExcessNak - && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { - if (urb_state == 0) { - /* All urb_state has not been finished yet! - * continue with the current urb transaction - */ - - if (hci->last_packet_nak == 0) { - if (!usb_pipecontrol - (hci->td_array->td[0].urb->pipe)) - sh_add_packet (hci, hci->td_array-> td[0].urb); - } - } else { - /* The last transaction has completed: - * schedule the next transaction - */ - - sh_schedule_trans (hci, 0); - } - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; - } - - if (ii & SL11H_INTMASK_SOFINTR) { - hci->frame_number = (hci->frame_number + 1) % 2048; - if (hci->td_array->len == 0) - sh_schedule_trans (hci, 1); - else { - if (sofWaitCnt++ > 100) { - /* The last transaction has not completed. - * Need to retire the current td, and let - * it transmit again later on. - * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER - * GET TO THIS POINT) - */ - - DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - urb_print (hci->td_array->td[hci->td_array->len - 1].urb, - "INTERRUPT", 0); - sh_done_list (hci, &isExcessNak); - SL811Write (hci, SL11H_INTSTATREG, 0xff); - hci->td_array->len = 0; - sofWaitCnt = 0; - } - } - tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); - if (tmpIrq) { - DBG ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - - /* If we receive a DONE IRQ after schedule, need to - * handle DONE IRQ again - */ - - if (tmpIrq & SL11H_INTMASK_XFERDONE) { - DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - urb_state = sh_done_list (hci, &isExcessNak); - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } - } else { - DBG ("SL811 ISR: unknown, int = 0x%x \n", ii); - } - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; -} - -/***************************************************************** - * - * Function Name: hc_reset - * - * This function does register test and resets the SL811HS - * controller. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_reset (hci_t * hci) -{ - int attachFlag = 0; - - DBGFUNC ("Enter hc_reset\n"); - regTest (hci); - attachFlag = USBReset (hci); - if (attachFlag) { - setPortChange (hci, PORT_CONNECT_CHANGE); - } - return (0); -} - -/***************************************************************** - * - * Function Name: hc_alloc_trans_buffer - * - * This function allocates all transfer buffer - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_alloc_trans_buffer (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - int maxlen; - - hp->itl0_len = 0; - hp->itl1_len = 0; - hp->atl_len = 0; - - hp->itl_buffer_len = 1024; - hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ - - maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; - - hp->tl = kmalloc (maxlen, GFP_KERNEL); - - if (!hp->tl) - return -ENOMEM; - - memset (hp->tl, 0, maxlen); - return 0; -} - -/***************************************************************** - * - * Function Name: getPortStatusAndChange - * - * This function gets the ports status from SL811 and format it - * to a USB request format - * - * Input: hci = data structure for the host controller - * - * Return value : port status and change - * - *****************************************************************/ -static __u32 getPortStatusAndChange (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u32 portstatus; - - DBGFUNC ("enter getPorStatusAndChange\n"); - - portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; - - return (portstatus); -} - -/***************************************************************** - * - * Function Name: setPortChange - * - * This function set the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - - switch (bitPos) { - case PORT_CONNECT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_ENABLE_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_OVER_CURRENT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortChange - * - * This function clear the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_CONNECT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_ENABLE_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_RESET_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_SUSPEND_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_OVER_CURRENT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortStatus - * - * This function clear the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: setPortStatus - * - * This function set the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: hc_start - * - * This function starts the root hub functionality. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_start (hci_t * hci) -{ - DBGFUNC ("Enter hc_start\n"); - - rh_connect_rh (hci); - - return 0; -} - -/***************************************************************** - * - * Function Name: hc_alloc_hci - * - * This function allocates all data structure and store in the - * private data structure. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static hci_t *__devinit hc_alloc_hci (void) -{ - hci_t *hci; - hcipriv_t *hp; - portstat_t *ps; - struct usb_bus *bus; - - DBGFUNC ("Enter hc_alloc_hci\n"); - hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); - if (!hci) - return NULL; - - memset (hci, 0, sizeof (hci_t)); - - hp = &hci->hp; - - hp->irq = -1; - hp->hcport = -1; - - /* setup root hub port status */ - - ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); - - if (!ps) - return NULL; - ps->portStatus = PORT_STAT_DEFAULT; - ps->portChange = PORT_CHANGE_DEFAULT; - hp->RHportStatus = ps; - - hci->nakCnt = 0; - hci->last_packet_nak = 0; - - hci->a_td_array.len = 0; - hci->i_td_array[0].len = 0; - hci->i_td_array[1].len = 0; - hci->td_array = &hci->a_td_array; - hci->active_urbs = 0; - hci->active_trans = 0; - INIT_LIST_HEAD (&hci->hci_hcd_list); - list_add (&hci->hci_hcd_list, &hci_hcd_list); - init_waitqueue_head (&hci->waitq); - - INIT_LIST_HEAD (&hci->ctrl_list); - INIT_LIST_HEAD (&hci->bulk_list); - INIT_LIST_HEAD (&hci->iso_list); - INIT_LIST_HEAD (&hci->intr_list); - INIT_LIST_HEAD (&hci->del_list); - - bus = usb_alloc_bus (&hci_device_operations); - if (!bus) { - kfree (hci); - kfree (ps); - return NULL; - } - - hci->bus = bus; - bus->hcpriv = (void *) hci; - - return hci; -} - -/***************************************************************** - * - * Function Name: hc_release_hci - * - * This function De-allocate all resources - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static void hc_release_hci (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("Enter hc_release_hci\n"); - - /* disconnect all devices */ - if (hci->bus->root_hub) - usb_disconnect (&hci->bus->root_hub); - - hc_reset (hci); - - if (hp->tl) - kfree (hp->tl); - - if (hp->hcport > 0) { - release_region (hp->hcport, 2); - hp->hcport = 0; - } - - if (hp->irq >= 0) { - free_irq (hp->irq, hci); - hp->irq = -1; - } - - usb_deregister_bus (hci->bus); - usb_put_bus (hci->bus); - - list_del_init (&hci->hci_hcd_list); - - kfree (hci); -} - -/***************************************************************** - * - * Function Name: init_irq - * - * This function is board specific. It sets up the interrupt to - * be an edge trigger and trigger on the rising edge - * - * Input: none - * - * Return value : none - * - *****************************************************************/ -void init_irq (void) -{ - GPDR &= ~(1 << 13); - set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE); -} - -/***************************************************************** - * - * Function Name: hc_found_hci - * - * This function request IO memory regions, request IRQ, and - * allocate all other resources. - * - * Input: addr = first IO address - * addr2 = second IO address - * irq = interrupt number - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __devinit hc_found_hci (int addr, int addr2, int irq) -{ - hci_t *hci; - hcipriv_t *hp; - - DBGFUNC ("Enter hc_found_hci\n"); - hci = hc_alloc_hci (); - if (!hci) { - return -ENOMEM; - } - - init_irq (); - hp = &hci->hp; - - if (!request_region (addr, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport = addr; - if (!hp->hcport) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport); - } - - if (!request_region (addr2, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr2); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport2 = addr2; - if (!hp->hcport2) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport2); - } - - if (hc_alloc_trans_buffer (hci)) { - hc_release_hci (hci); - return -ENOMEM; - } - - usb_register_bus (hci->bus); - - if (request_irq (irq, hc_interrupt, 0, "SL811", hci) != 0) { - DBGERR ("request interrupt %d failed", irq); - hc_release_hci (hci); - return -EBUSY; - } - hp->irq = irq; - - printk (KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", - addr, addr2, irq); - hc_reset (hci); - - if (hc_start (hci) < 0) { - DBGERR ("can't start usb-%x", addr); - hc_release_hci (hci); - return -EBUSY; - } - - return 0; -} - -/***************************************************************** - * - * Function Name: hci_hcd_init - * - * This is an init function, and it is the first function being called - * - * Input: none - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __init hci_hcd_init (void) -{ - int ret; - - DBGFUNC ("Enter hci_hcd_init\n"); - if (usb_disabled()) - return -ENODEV; - - ret = hc_found_hci (base_addr, data_reg_addr, irq); - - return ret; -} - -/***************************************************************** - * - * Function Name: hci_hcd_cleanup - * - * This is a cleanup function, and it is called when module is - * unloaded. - * - * Input: none - * - * Return: none - * - *****************************************************************/ -static void __exit hci_hcd_cleanup (void) -{ - hci_t *hci, *tmp; - - DBGFUNC ("Enter hci_hcd_cleanup\n"); - list_for_each_entry_safe(hci, tmp, &hci_hcd_list, hci_hcd_list) - hc_release_hci (hci); -} - -module_init (hci_hcd_init); -module_exit (hci_hcd_cleanup); - -MODULE_AUTHOR ("Pei Liu <pbl@cypress.com>"); -MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff --git a/drivers/usb/host/hc_sl811.h b/drivers/usb/host/hc_sl811.h deleted file mode 100644 index 8b9eed235088..000000000000 --- a/drivers/usb/host/hc_sl811.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * SL811HS HCD (Host Controller Driver) for USB. - * - * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC - * - * - */ - -#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) - -/* - * Maximum number of root hub ports - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* control and status registers */ -#define HcRevision 0x00 -#define HcControl 0x01 -#define HcCommandStatus 0x02 -#define HcInterruptStatus 0x03 -#define HcInterruptEnable 0x04 -#define HcInterruptDisable 0x05 -#define HcFmInterval 0x0D -#define HcFmRemaining 0x0E -#define HcFmNumber 0x0F -#define HcLSThreshold 0x11 -#define HcRhDescriptorA 0x12 -#define HcRhDescriptorB 0x13 -#define HcRhStatus 0x14 -#define HcRhPortStatus 0x15 - -#define HcHardwareConfiguration 0x20 -#define HcDMAConfiguration 0x21 -#define HcTransferCounter 0x22 -#define HcuPInterrupt 0x24 -#define HcuPInterruptEnable 0x25 -#define HcChipID 0x27 -#define HcScratch 0x28 -#define HcSoftwareReset 0x29 -#define HcITLBufferLength 0x2A -#define HcATLBufferLength 0x2B -#define HcBufferStatus 0x2C -#define HcReadBackITL0Length 0x2D -#define HcReadBackITL1Length 0x2E -#define HcITLBufferPort 0x40 -#define HcATLBufferPort 0x41 - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -#define OHCI_USB_RESET (0 << 6) -#define OHCI_USB_RESUME (1 << 6) -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_SO (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ - -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ - -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - -/* - * HcHardwareConfiguration - */ -#define InterruptPinEnable (1 << 0) -#define InterruptPinTrigger (1 << 1) -#define InterruptOutputPolarity (1 << 2) -#define DataBusWidth16 (1 << 3) -#define DREQOutputPolarity (1 << 5) -#define DACKInputPolarity (1 << 6) -#define EOTInputPolarity (1 << 7) -#define DACKMode (1 << 8) -#define AnalogOCEnable (1 << 10) -#define SuspendClkNotStop (1 << 11) -#define DownstreamPort15KRSel (1 << 12) - -/* - * HcDMAConfiguration - */ -#define DMAReadWriteSelect (1 << 0) -#define ITL_ATL_DataSelect (1 << 1) -#define DMACounterSelect (1 << 2) -#define DMAEnable (1 << 4) -#define BurstLen_1 0 -#define BurstLen_4 (1 << 5) -#define BurstLen_8 (2 << 5) - -/* - * HcuPInterrupt - */ -#define SOFITLInt (1 << 0) -#define ATLInt (1 << 1) -#define AllEOTInterrupt (1 << 2) -#define OPR_Reg (1 << 4) -#define HCSuspended (1 << 5) -#define ClkReady (1 << 6) - -/* - * HcBufferStatus - */ -#define ITL0BufferFull (1 << 0) -#define ITL1BufferFull (1 << 1) -#define ATLBufferFull (1 << 2) -#define ITL0BufferDone (1 << 3) -#define ITL1BufferDone (1 << 4) -#define ATLBufferDone (1 << 5) - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status */ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#define URB_DEL 1 - -#define PORT_STAT_DEFAULT 0x0100 -#define PORT_CONNECT_STAT 0x1 -#define PORT_ENABLE_STAT 0x2 -#define PORT_SUSPEND_STAT 0x4 -#define PORT_OVER_CURRENT_STAT 0x8 -#define PORT_RESET_STAT 0x10 -#define PORT_POWER_STAT 0x100 -#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 - -#define PORT_CHANGE_DEFAULT 0x0 -#define PORT_CONNECT_CHANGE 0x1 -#define PORT_ENABLE_CHANGE 0x2 -#define PORT_SUSPEND_CHANGE 0x4 -#define PORT_OVER_CURRENT_CHANGE 0x8 -#define PORT_RESET_CHANGE 0x10 - -/* Port Status Request info */ - -typedef struct portstat { - __u16 portChange; - __u16 portStatus; -} portstat_t; - -typedef struct hcipriv { - int irq; - int disabled; /* e.g. got a UE, we're hung */ - atomic_t resume_count; /* defending against multiple resumes */ - struct ohci_regs *regs; /* OHCI controller's memory */ - int hcport; /* I/O base address */ - int hcport2; /* I/O data reg addr */ - - struct portstat *RHportStatus; /* root hub port status */ - - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - - int frame; - - __u8 *tl; - int xferPktLen; - int atl_len; - int atl_buffer_len; - int itl0_len; - int itl1_len; - int itl_buffer_len; - int itl_index; - int tl_last; - int units_left; - -} hcipriv_t; -struct hci; - -#define cClt 0 // Control -#define cISO 1 // ISO -#define cBULK 2 // BULK -#define cInt 3 // Interrupt -#define ISO_BIT 0x10 - -/*------------------------------------------------------------------------- - * EP0 use for configuration and Vendor Specific command interface - *------------------------------------------------------------------------*/ -#define cMemStart 0x10 -#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ -#define EP0Len 0x40 /* Length of config buffer EP0Buf */ -#define EP1Buf 0x60 -#define EP1Len 0x40 - -/*------------------------------------------------------------------------- - * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. - *------------------------------------------------------------------------*/ -#define uBufA 0x80 /* buffer A address for DATA0 */ -#define uBufB 0xc0 /* buffer B address for DATA1 */ -#define uXferLen 0x40 /* xfer length */ -#define sMemSize 0xc0 /* Total SL11 memory size */ -#define cMemEnd 256 - -/*------------------------------------------------------------------------- - * SL811H Register Control memory map - * --Note: - * --SL11H only has one control register set from 0x00-0x04 - * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c - *------------------------------------------------------------------------*/ - -#define EP0Control 0x00 -#define EP0Address 0x01 -#define EP0XferLen 0x02 -#define EP0Status 0x03 -#define EP0Counter 0x04 - -#define EP1Control 0x08 -#define EP1Address 0x09 -#define EP1XferLen 0x0a -#define EP1Status 0x0b -#define EP1Counter 0x0c - -#define CtrlReg 0x05 -#define IntEna 0x06 - // 0x07 is reserved -#define IntStatus 0x0d -#define cDATASet 0x0e -#define cSOFcnt 0x0f -#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ -#define HostMask 0x47 /* Host request command for IntStatus */ -#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ - -/*------------------------------------------------------------------------- - * Standard Chapter 9 definition - *------------------------------------------------------------------------- - */ -#define GET_STATUS 0x00 -#define CLEAR_FEATURE 0x01 -#define SET_FEATURE 0x03 -#define SET_ADDRESS 0x05 -#define GET_DESCRIPTOR 0x06 -#define SET_DESCRIPTOR 0x07 -#define GET_CONFIG 0x08 -#define SET_CONFIG 0x09 -#define GET_INTERFACE 0x0a -#define SET_INTERFACE 0x0b -#define SYNCH_FRAME 0x0c - -#define DEVICE 0x01 -#define CONFIGURATION 0x02 -#define STRING 0x03 -#define INTERFACE 0x04 -#define ENDPOINT 0x05 - -/*------------------------------------------------------------------------- - * SL11H/SL811H definition - *------------------------------------------------------------------------- - */ -#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) -#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) -#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) -#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) -#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) -#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) - -#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition -#define PID_SOF 0xA5 -#define PID_IN 0x69 -#define PID_OUT 0xe1 - -#define MAX_RETRY 0xffff -#define TIMEOUT 5 /* 2 mseconds */ - -#define SL11H_HOSTCTLREG 0 -#define SL11H_BUFADDRREG 1 -#define SL11H_BUFLNTHREG 2 -#define SL11H_PKTSTATREG 3 /* read */ -#define SL11H_PIDEPREG 3 /* write */ -#define SL11H_XFERCNTREG 4 /* read */ -#define SL11H_DEVADDRREG 4 /* write */ -#define SL11H_CTLREG1 5 -#define SL11H_INTENBLREG 6 - -#define SL11H_HOSTCTLREG_B 8 -#define SL11H_BUFADDRREG_B 9 -#define SL11H_BUFLNTHREG_B 0x0A -#define SL11H_PKTSTATREG_B 0x0B /* read */ -#define SL11H_PIDEPREG_B 0x0B /* write */ -#define SL11H_XFERCNTREG_B 0x0C /* read */ -#define SL11H_DEVADDRREG_B 0x0C /* write */ - -#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ -#define SL11H_HWREVREG 0x0E /* read */ -#define SL11H_SOFLOWREG 0x0E /* write */ -#define SL11H_SOFTMRREG 0x0F /* read */ -#define SL11H_CTLREG2 0x0F /* write */ -#define SL11H_DATA_START 0x10 - -/* Host control register bits (addr 0) */ -#define SL11H_HCTLMASK_ARM 1 -#define SL11H_HCTLMASK_ENBLEP 2 -#define SL11H_HCTLMASK_WRITE 4 -#define SL11H_HCTLMASK_ISOCH 0x10 -#define SL11H_HCTLMASK_AFTERSOF 0x20 -#define SL11H_HCTLMASK_SEQ 0x40 -#define SL11H_HCTLMASK_PREAMBLE 0x80 - -/* Packet status register bits (addr 3) */ -#define SL11H_STATMASK_ACK 1 -#define SL11H_STATMASK_ERROR 2 -#define SL11H_STATMASK_TMOUT 4 -#define SL11H_STATMASK_SEQ 8 -#define SL11H_STATMASK_SETUP 0x10 -#define SL11H_STATMASK_OVF 0x20 -#define SL11H_STATMASK_NAK 0x40 -#define SL11H_STATMASK_STALL 0x80 - -/* Control register 1 bits (addr 5) */ -#define SL11H_CTL1MASK_DSBLSOF 1 -#define SL11H_CTL1MASK_NOTXEOF2 4 -#define SL11H_CTL1MASK_DSTATE 0x18 -#define SL11H_CTL1MASK_NSPD 0x20 -#define SL11H_CTL1MASK_SUSPEND 0x40 -#define SL11H_CTL1MASK_CLK12 0x80 - -#define SL11H_CTL1VAL_RESET 8 - -/* Interrupt enable (addr 6) and interrupt status register bits (addr 0xD) */ -#define SL11H_INTMASK_XFERDONE 1 -#define SL11H_INTMASK_SOFINTR 0x10 -#define SL11H_INTMASK_INSRMV 0x20 -#define SL11H_INTMASK_USBRESET 0x40 -#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ - -/* HW rev and SOF lo register bits (addr 0xE) */ -#define SL11H_HWRMASK_HWREV 0xF0 - -/* SOF counter and control reg 2 (addr 0xF) */ -#define SL11H_CTL2MASK_SOFHI 0x3F -#define SL11H_CTL2MASK_DSWAP 0x40 -#define SL11H_CTL2MASK_HOSTMODE 0xae - diff --git a/drivers/usb/host/hc_sl811_rh.c b/drivers/usb/host/hc_sl811_rh.c deleted file mode 100644 index aaaa705bccdf..000000000000 --- a/drivers/usb/host/hc_sl811_rh.c +++ /dev/null @@ -1,583 +0,0 @@ - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS virtual root hub - * - * based on usb-ohci.c by R. Weissgaerber et al. - *-------------------------------------------------------------------------* - * 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 - * - *-------------------------------------------------------------------------*/ - -/* FIXME: reuse the root hub framework in usbcore, shrinking this code. */ - -#ifdef DEBUG -#undef DEBUG -#endif -static __u32 getPortStatusAndChange (hci_t * hci); -static void setPortStatus (hci_t * hci, __u16 bitPos); -static void setPortChange (hci_t * hci, __u16 bitPos); -static void clrPortStatus (hci_t * hci, __u16 bitPos); -static void clrPortChange (hci_t * hci, __u16 bitPos); -static int USBReset (hci_t * hci); -static int cc_to_error (int cc); - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = { - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = { - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - -/*************************************************************************** - * Function Name : rh_send_irq - * - * This function examine the port change in the virtual root hub. - * - * Note: This function assumes only one port exist in the root hub. - * - * Input: hci = data structure for the host controller - * rh_data = The pointer to port change data - * rh_len = length of the data in bytes - * - * Return: length of data - **************************************************************************/ -static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) -{ - int num_ports; - int i; - int ret; - int len; - __u8 data[8]; - - DBGFUNC ("enter rh_send_irq: \n"); - - /* Assuming the root hub has one port. This value need to change if - * there are more than one port for the root hub - */ - - num_ports = 1; - - /* The root hub status is not implemented, it basically has two fields: - * -- Local Power Status - * -- Over Current Indicator - * -- Local Power Change - * -- Over Current Indicator - * - * Right now, It is assume the power is good and no changes - */ - - *(__u8 *) data = 0; - - ret = *(__u8 *) data; - - /* Has the port status change within the root hub: It checks for - * -- Port Connect Status change - * -- Port Enable Change - */ - - for (i = 0; i < num_ports; i++) { - *(__u8 *) (data + (i + 1) / 8) |= - (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); - ret += *(__u8 *) (data + (i + 1) / 8); - - /* After the port change is read, it should be reset so the next time - * is it doesn't trigger a change again */ - - } - len = i / 8 + 1; - - if (ret > 0) { - memcpy (rh_data, data, min (len, min (rh_len, (int)sizeof (data)))); - return len; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_int_timer_do - * - * This function is called when the timer expires. It gets the the port - * change data and pass along to the upper protocol. - * - * Note: The virtual root hub interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: ptr = ptr to the urb - * - * Return: none - **************************************************************************/ -static void rh_int_timer_do (unsigned long ptr) -{ - int len; - struct urb *urb = (struct urb *) ptr; - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_int_timer_do\n"); - - if (hci->rh.send) { - len = rh_send_irq (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - if (len > 0) { - urb->actual_length = len; - if (urb_debug == 2) - urb_print (urb, "RET-t(rh)", - usb_pipeout (urb->pipe)); - - if (urb->complete) { - urb->complete (urb, NULL); - } - } - } - - /* re-activate the timer */ - rh_init_int_timer (urb); -} - -/*************************************************************************** - * Function Name : rh_init_int_timer - * - * This function creates a timer that act as interrupt pipe in the - * virtual hub. - * - * Note: The virtual root hub's interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_init_int_timer (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - hci->rh.interval = urb->interval; - - init_timer (&hci->rh.rh_int_timer); - hci->rh.rh_int_timer.function = rh_int_timer_do; - hci->rh.rh_int_timer.data = (unsigned long) urb; - hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; - add_timer (&hci->rh.rh_int_timer); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* for returning string descriptors in UTF-16LE */ -static int ascii2utf (char *ascii, __u8 *utf, int utfmax) -{ - int retval; - - for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *ascii++ & 0x7f; - *utf++ = 0; - } - return retval; -} - -static int root_hub_string (int id, int serial, char *type, __u8 *data, int len) -{ - char buf [30]; - - // assert (len > (2 * (sizeof (buf) + 1))); - // assert (strlen (type) <= 8); - - // language ids - if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes data */ - *data++ = 0; *data++ = 0; /* some language id */ - return 4; - - // serial number - } else if (id == 1) { - sprintf (buf, "%x", serial); - - // product description - } else if (id == 2) { - sprintf (buf, "USB %s Root Hub", type); - - // id 3 == vendor description - - // unsupported IDs --> "stall" - } else - return 0; - - data [0] = 2 + ascii2utf (buf, data + 2, len - 2); - data [1] = 3; - return data [0]; -} - -/*-------------------------------------------------------------------------*/ - -/* helper macro */ -#define OK(x) len = (x); break - -/*************************************************************************** - * Function Name : rh_submit_urb - * - * This function handles all USB request to the the virtual root hub - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_submit_urb (struct urb * urb) -{ - struct usb_device *usb_dev = urb->dev; - hci_t *hci = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = TD_CC_NOERROR; - __u32 datab[4]; - __u8 *data_buf = (__u8 *) datab; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBGFUNC ("enter rh_submit_urb\n"); - if (usb_pipeint (pipe)) { - hci->rh.urb = urb; - hci->rh.send = 1; - hci->rh.interval = urb->interval; - rh_init_int_timer (urb); - urb->status = cc_to_error (TD_CC_NOERROR); - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu (cmd->wValue); - wIndex = le16_to_cpu (cmd->wIndex); - wLength = le16_to_cpu (cmd->wLength); - - DBG ("rh_submit_urb, req = %d(%x) len=%d", - bmRType_bReq, bmRType_bReq, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32 (0); - OK (4); - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = - cpu_to_le32 (getPortStatusAndChange (hci)); - OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK (0); - - case (RH_C_HUB_OVER_CURRENT): - /* Over Current Not Implemented */ - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - clrPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_PORT_SUSPEND): - clrPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_POWER): - clrPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_C_PORT_CONNECTION): - clrPortChange (hci, PORT_CONNECT_STAT); - OK (0); - - case (RH_C_PORT_ENABLE): - clrPortChange (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_C_PORT_SUSPEND): - clrPortChange (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_C_PORT_OVER_CURRENT): - clrPortChange (hci, PORT_OVER_CURRENT_STAT); - OK (0); - - case (RH_C_PORT_RESET): - clrPortChange (hci, PORT_RESET_STAT); - OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - setPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_RESET): - setPortStatus (hci, PORT_RESET_STAT); - // USBReset(hci); - clrPortChange (hci, - PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE - | PORT_SUSPEND_CHANGE | - PORT_OVER_CURRENT_CHANGE); - setPortChange (hci, PORT_RESET_CHANGE); - clrPortStatus (hci, PORT_RESET_STAT); - setPortStatus (hci, PORT_ENABLE_STAT); - - OK (0); - - case (RH_PORT_POWER): - setPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_PORT_ENABLE): - setPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - } - break; - - case RH_SET_ADDRESS: - hci->rh.devnum = wValue; - OK (0); - - case RH_GET_DESCRIPTOR: - DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); - data_buf = root_hub_dev_des; - OK (len); - - case (0x02): /* configuration descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); - data_buf = root_hub_config_des; - OK (len); - - case (0x03): /* string descriptors */ - len = root_hub_string (wValue & 0xff, (int) (long) 0, - "SL811HS", data, wLength); - if (len > 0) { - data_buf = data; - OK (min (leni, len)); - } - - default: - status = SL11H_STATMASK_STALL; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - data_buf[0] = 9; // min length; - data_buf[1] = 0x29; - data_buf[2] = 1; // # of downstream port - data_buf[3] = 0; - datab[1] = 0; - data_buf[5] = 50; // 100 ms for port reset - data_buf[7] = 0xfc; // which port is attachable - if (data_buf[2] < 7) { - data_buf[8] = 0xff; - } else { - } - - len = min (leni, min ((__u16)data_buf[0], wLength)); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data_buf = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - DBGERR ("unsupported root hub command"); - status = SL11H_STATMASK_STALL; - } - - len = min (len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - urb->actual_length = len; - urb->status = cc_to_error (status); - - urb->hcpriv = NULL; - urb->dev = NULL; - if (urb->complete) { - urb->complete (urb, NULL); - } - - return 0; -} - -/*************************************************************************** - * Function Name : rh_unlink_urb - * - * This function unlinks the URB - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_unlink_urb (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_unlink_urb\n"); - if (hci->rh.urb == urb) { - hci->rh.send = 0; - del_timer (&hci->rh.rh_int_timer); - hci->rh.urb = NULL; - - urb->hcpriv = NULL; - usb_put_dev (urb->dev); - urb->dev = NULL; - if (urb->transfer_flags & URB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) { - urb->complete (urb, NULL); - } - } else - urb->status = -ENOENT; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_connect_rh - * - * This function connect the virtual root hub to the USB stack - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_connect_rh (hci_t * hci) -{ - struct usb_device *usb_dev; - int retval; - - hci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, hci->bus, 0); - if (!usb_dev) - return -ENOMEM; - - usb_dev->devnum = 1; - usb_dev->bus->devnum_next = usb_dev->devnum + 1; - set_bit (usb_dev->devnum, usb_dev->bus->devmap.devicemap); - - down (&usb_bus_list_lock); - hci->bus->root_hub = usb_dev; - retval = usb_new_device (usb_dev); - if (retval != 0) - hci->bus->root_hub = NULL; - up (&usb_bus_list_lock); - if (retval != 0) { - usb_put_dev (usb_dev); - return -ENODEV; - } - - return 0; -} diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index d5747e974cfb..8f40bfa42d76 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -305,18 +305,26 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ports, i, changed = 0, length = 1; - int can_suspend = 1; + int can_suspend = hcd->can_wakeup; + unsigned long flags; - /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (!HCD_IS_RUNNING(ohci->hcd.state)) - return 0; + spin_lock_irqsave (&ohci->lock, flags); + + /* handle autosuspended root: finish resuming before + * letting khubd or root hub timer see state changes. + */ + if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER + || !HCD_IS_RUNNING(ohci->hcd.state)) { + can_suspend = 0; + goto done; + } ports = roothub_a (ohci) & RH_A_NDP; if (ports > MAX_ROOT_PORTS) { ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports, ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ - return 0; + goto done; } /* init status */ @@ -352,6 +360,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) continue; can_suspend = 0; } +done: + spin_unlock_irqrestore (&ohci->lock, flags); #ifdef CONFIG_PM /* save power by suspending idle root hubs; diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index debe1034fd49..89624afecd1a 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -121,10 +121,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) msleep (100); #ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - disable_irq ((to_pci_dev(hcd->self.controller))->irq); - - { + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Disable USB PAD & cell clock */ @@ -132,7 +129,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); } -#endif +#endif /* CONFIG_PMAC_PBOOK */ return 0; } @@ -143,7 +140,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) int retval = 0; #ifdef CONFIG_PMAC_PBOOK - { + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Re-enable USB PAD & cell clock */ @@ -151,7 +148,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) if (of_node) pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); } -#endif +#endif /* CONFIG_PMAC_PBOOK */ /* resume root hub */ if (time_before (jiffies, ohci->next_statechange)) @@ -165,12 +162,6 @@ static int ohci_pci_resume (struct usb_hcd *hcd) usb_unlock_device (hcd->self.root_hub); #endif - if (retval == 0) { -#ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - enable_irq (to_pci_dev(hcd->self.controller)->irq); -#endif - } return retval; } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c new file mode 100644 index 000000000000..ac8f8990b42a --- /dev/null +++ b/drivers/usb/host/sl811-hcd.c @@ -0,0 +1,1905 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + * + * Periodic scheduling is based on Roman's OHCI code + * Copyright (C) 1999 Roman Weissgaerber + * + * The SL811HS controller handles host side USB (like the SL11H, but with + * another register set and SOF generation) as well as peripheral side USB + * (like the SL811S). This driver version doesn't implement the Gadget API + * for the peripheral role; or OTG (that'd need much external circuitry). + * + * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" + * document (providing significant pieces missing from that spec); plus + * the SL811S spec if you want peripheral side info. + */ + +/* + * Status: Passed basic stress testing, works with hubs, mice, keyboards, + * and usb-storage. + * + * TODO: + * - usb suspend/resume triggered by sl811 (with USB_SUSPEND) + * - various issues noted in the code + * - performance work; use both register banks; ... + * - use urb->iso_frame_desc[] with ISO transfers + */ + +#undef VERBOSE +#undef PACKET_TRACE + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG +# define DEBUG +#else +# undef DEBUG +#endif + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/usb_sl811.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/byteorder.h> + +#include "../core/hcd.h" +#include "sl811.h" + + +MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); +MODULE_LICENSE("GPL"); + +#define DRIVER_VERSION "06 Dec 2004" + + +#ifndef DEBUG +# define STUB_DEBUG_FILE +#endif + +/* for now, use only one transfer register bank */ +#undef USE_B + +/* this doesn't understand urb->iso_frame_desc[], but if you had a driver + * that just queued one ISO frame per URB then iso transfers "should" work + * using the normal urb status fields. + */ +#define DISABLE_ISO + +// #define QUIRK2 +#define QUIRK3 + +static const char hcd_name[] = "sl811-hcd"; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs); + +static void port_power(struct sl811 *sl811, int is_on) +{ + /* hub is inactive unless the port is powered */ + if (is_on) { + if (sl811->port1 & (1 << USB_PORT_FEAT_POWER)) + return; + + sl811->port1 = (1 << USB_PORT_FEAT_POWER); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_ON; + } else { + sl811->port1 = 0; + sl811->irq_enable = 0; + sl811->hcd.state = USB_STATE_HALT; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_DISK; + } + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_IRQ_STATUS, ~0); + + if (sl811->board && sl811->board->port_power) { + /* switch VBUS, at 500mA unless hub power budget gets set */ + DBG("power %s\n", is_on ? "on" : "off"); + sl811->board->port_power(sl811->hcd.self.controller, is_on); + } + + /* reset as thoroughly as we can */ + if (sl811->board && sl811->board->reset) + sl811->board->reset(sl811->hcd.self.controller); + + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + // if !is_on, put into lowpower mode now +} + +/*-------------------------------------------------------------------------*/ + +/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, + * and may start I/O. Endpoint queues are scanned during completion irq + * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation. + * + * Using an external DMA engine to copy a packet at a time could work, + * though setup/teardown costs may be too big to make it worthwhile. + */ + +/* SETUP starts a new control request. Devices are not allowed to + * STALL or NAK these; they must cancel any pending control requests. + */ +static void setup_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + void __iomem *data_reg; + + addr = SL811HS_PACKET_BUF(bank == 0); + len = sizeof(struct usb_ctrlrequest); + data_reg = sl811->data_reg; + sl811_write_buf(sl811, addr, urb->setup_packet, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_SETUP /* | ep->epnum */, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + /* always OUT/data0 */ ; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = 0; + PACKET("SETUP qh%p\n", ep); +} + +/* STATUS finishes control requests, often after IN or OUT data packets */ +static void status_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + int do_out; + void __iomem *data_reg; + + do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe); + data_reg = sl811->data_reg; + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, 0); + writeb(0, data_reg); + writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + /* always data1; sometimes IN */ + control |= SL11H_HCTLMASK_TOGGLE; + if (do_out) + control |= SL11H_HCTLMASK_OUT; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = 0; + PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "", + do_out ? "out" : "in", ep); +} + +/* IN packets can be used with any type of endpoint. here we just + * start the transfer, data from the peripheral may arrive later. + * urb->iso_frame_desc is currently ignored here... + */ +static void in_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + void __iomem *data_reg; + + /* avoid losing data on overflow */ + len = ep->maxpacket; + addr = SL811HS_PACKET_BUF(bank == 0); + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 0)) + control |= SL11H_HCTLMASK_TOGGLE; + data_reg = sl811->data_reg; + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_IN | ep->epnum, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = min((int)len, + urb->transfer_buffer_length - urb->actual_length); + PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len); +} + +/* OUT packets can be used with any type of endpoint. + * urb->iso_frame_desc is currently ignored here... + */ +static void out_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + void *buf; + u8 addr; + u8 len; + void __iomem *data_reg; + + buf = urb->transfer_buffer + urb->actual_length; + prefetch(buf); + + len = min((int)ep->maxpacket, + urb->transfer_buffer_length - urb->actual_length); + + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 1)) + control |= SL11H_HCTLMASK_TOGGLE; + addr = SL811HS_PACKET_BUF(bank == 0); + data_reg = sl811->data_reg; + + sl811_write_buf(sl811, addr, buf, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, data_reg); + writeb(SL_OUT | ep->epnum, data_reg); + writeb(usb_pipedevice(urb->pipe), data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = len; + PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len); +} + +/*-------------------------------------------------------------------------*/ + +/* caller updates on-chip enables later */ + +static inline void sofirq_on(struct sl811 *sl811) +{ + if (sl811->irq_enable & SL11H_INTMASK_SOFINTR) + return; + VDBG("sof irq on\n"); + sl811->irq_enable |= SL11H_INTMASK_SOFINTR; +} + +static inline void sofirq_off(struct sl811 *sl811) +{ + if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR)) + return; + VDBG("sof irq off\n"); + sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR; +} + +/*-------------------------------------------------------------------------*/ + +/* pick the next endpoint for a transaction, and issue it. + * frames start with periodic transfers (after whatever is pending + * from the previous frame), and the rest of the time is async + * transfers, scheduled round-robin. + */ +static struct sl811h_ep *start(struct sl811 *sl811, u8 bank) +{ + struct sl811h_ep *ep; + struct sl811h_req *req; + struct urb *urb; + int fclock; + u8 control; + + /* use endpoint at schedule head */ + if (sl811->next_periodic) { + ep = sl811->next_periodic; + sl811->next_periodic = ep->next; + } else { + if (sl811->next_async) + ep = sl811->next_async; + else if (!list_empty(&sl811->async)) + ep = container_of(sl811->async.next, + struct sl811h_ep, schedule); + else { + /* could set up the first fullspeed periodic + * transfer for the next frame ... + */ + return NULL; + } + +#ifdef USE_B + if ((bank && sl811->active_b == ep) || sl811->active_a == ep) + return NULL; +#endif + + if (ep->schedule.next == &sl811->async) + sl811->next_async = NULL; + else + sl811->next_async = container_of(ep->schedule.next, + struct sl811h_ep, schedule); + } + + if (unlikely(list_empty(&ep->queue))) { + DBG("empty %p queue?\n", ep); + return NULL; + } + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + control = ep->defctrl; + + /* if this frame doesn't have enough time left to transfer this + * packet, wait till the next frame. too-simple algorithm... + */ + fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6; + fclock -= 100; /* setup takes not much time */ + if (urb->dev->speed == USB_SPEED_LOW) { + if (control & SL11H_HCTLMASK_PREAMBLE) { + /* also note erratum 1: some hubs won't work */ + fclock -= 800; + } + fclock -= ep->maxpacket << 8; + + /* erratum 2: AFTERSOF only works for fullspeed */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + sofirq_on(sl811); + return NULL; + } + } else { + fclock -= 12000 / 19; /* 19 64byte packets/msec */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + control |= SL11H_HCTLMASK_AFTERSOF; + + /* throttle bulk/control irq noise */ + } else if (ep->nak_count) + control |= SL11H_HCTLMASK_AFTERSOF; + } + + + switch (ep->nextpid) { + case USB_PID_IN: + in_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_OUT: + out_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_SETUP: + setup_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_ACK: /* for control status */ + status_packet(sl811, ep, urb, bank, control); + break; + default: + DBG("bad ep%p pid %02x\n", ep, ep->nextpid); + ep = NULL; + } + return ep; +} + +#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) + +static inline void start_transfer(struct sl811 *sl811) +{ + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + return; + if (sl811->active_a == NULL) { + sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF)); + if (sl811->active_a != NULL) + sl811->jiffies_a = jiffies + MIN_JIFFIES; + } +#ifdef USE_B + if (sl811->active_b == NULL) { + sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF)); + if (sl811->active_b != NULL) + sl811->jiffies_b = jiffies + MIN_JIFFIES; + } +#endif +} + +static void finish_request( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct sl811h_req *req, + struct pt_regs *regs, + int status +) __releases(sl811->lock) __acquires(sl811->lock) +{ + unsigned i; + struct urb *urb = req->urb; + + list_del(&req->queue); + kfree(req); + urb->hcpriv = NULL; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock(&urb->lock); + + spin_unlock(&sl811->lock); + usb_hcd_giveback_urb(&sl811->hcd, urb, regs); + spin_lock(&sl811->lock); + + /* leave active endpoints in the schedule */ + if (!list_empty(&ep->queue)) + return; + + /* async deschedule? */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + if (ep == sl811->next_async) + sl811->next_async = NULL; + return; + } + + /* periodic deschedule */ + DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep *temp; + struct sl811h_ep **prev = &sl811->periodic[i]; + + while (*prev && ((temp = *prev) != ep)) + prev = &temp->next; + if (*prev) + *prev = ep->next; + sl811->load[i] -= ep->load; + } + ep->branch = PERIODIC_SIZE; + sl811->periodic_count--; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + -= ep->load / ep->period; + if (ep == sl811->next_periodic) + sl811->next_periodic = ep->next; + + /* we might turn SOFs back on again for the async schedule */ + if (sl811->periodic_count == 0) + sofirq_off(sl811); +} + +static void +done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs) +{ + u8 status; + struct sl811h_req *req; + struct urb *urb; + int urbstat = -EINPROGRESS; + + if (unlikely(!ep)) + return; + + status = sl811_read(sl811, bank + SL11H_PKTSTATREG); + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + + /* we can safely ignore NAKs */ + if (status & SL11H_STATMASK_NAK) { + // PACKET("...NAK_%02x qh%p\n", bank, ep); + if (!ep->period) + ep->nak_count++; + ep->error_count = 0; + + /* ACK advances transfer, toggle, and maybe queue */ + } else if (status & SL11H_STATMASK_ACK) { + struct usb_device *udev = urb->dev; + int len; + unsigned char *buf; + + /* urb->iso_frame_desc is currently ignored here... */ + + ep->nak_count = ep->error_count = 0; + switch (ep->nextpid) { + case USB_PID_OUT: + // PACKET("...ACK/out_%02x qh%p\n", bank, ep); + urb->actual_length += ep->length; + usb_dotoggle(udev, ep->epnum, 1); + if (urb->actual_length + == urb->transfer_buffer_length) { + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + + /* some bulk protocols terminate OUT transfers + * by a short packet, using ZLPs not padding. + */ + else if (ep->length < ep->maxpacket + || !(urb->transfer_flags + & URB_ZERO_PACKET)) + urbstat = 0; + } + break; + case USB_PID_IN: + // PACKET("...ACK/in_%02x qh%p\n", bank, ep); + buf = urb->transfer_buffer + urb->actual_length; + prefetchw(buf); + len = ep->maxpacket - sl811_read(sl811, + bank + SL11H_XFERCNTREG); + if (len > ep->length) { + len = ep->length; + urb->status = -EOVERFLOW; + } + urb->actual_length += len; + sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), + buf, len); + usb_dotoggle(udev, ep->epnum, 0); + if (urb->actual_length == urb->transfer_buffer_length) + urbstat = 0; + else if (len < ep->maxpacket) { + if (urb->transfer_flags & URB_SHORT_NOT_OK) + urbstat = -EREMOTEIO; + else + urbstat = 0; + } + if (usb_pipecontrol(urb->pipe) + && (urbstat == -EREMOTEIO + || urbstat == 0)) { + + /* NOTE if the status stage STALLs (why?), + * this reports the wrong urb status. + */ + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = urbstat; + spin_unlock(&urb->lock); + + req = NULL; + ep->nextpid = USB_PID_ACK; + } + break; + case USB_PID_SETUP: + // PACKET("...ACK/setup_%02x qh%p\n", bank, ep); + if (urb->transfer_buffer_length == urb->actual_length) + ep->nextpid = USB_PID_ACK; + else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + // PACKET("...ACK/status_%02x qh%p\n", bank, ep); + urbstat = 0; + break; + } + + /* STALL stops all transfers */ + } else if (status & SL11H_STATMASK_STALL) { + PACKET("...STALL_%02x qh%p\n", bank, ep); + ep->nak_count = ep->error_count = 0; + urbstat = -EPIPE; + + /* error? retry, until "3 strikes" */ + } else if (++ep->error_count >= 3) { + if (status & SL11H_STATMASK_TMOUT) + urbstat = -ETIMEDOUT; + else if (status & SL11H_STATMASK_OVF) + urbstat = -EOVERFLOW; + else + urbstat = -EPROTO; + ep->error_count = 0; + PACKET("...3STRIKES_%02x %02x qh%p stat %d\n", + bank, status, ep, urbstat); + } + + if ((urbstat != -EINPROGRESS || urb->status != -EINPROGRESS) + && req) + finish_request(sl811, ep, req, regs, urbstat); +} + +static inline u8 checkdone(struct sl811 *sl811) +{ + u8 ctl; + u8 irqstat = 0; + + if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_A: ctrl %02x sts %02x\n", + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#ifdef USE_B + if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_B: ctrl %02x sts %02x\n", ctl, + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#endif + return irqstat; +} + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs) +{ + struct sl811 *sl811 = _sl811; + u8 irqstat; + irqreturn_t ret = IRQ_NONE; + unsigned retries = 5; + + spin_lock(&sl811->lock); + +retry: + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP; + if (irqstat) { + sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + irqstat &= sl811->irq_enable; + } + +#ifdef QUIRK2 + /* this may no longer be necessary ... */ + if (irqstat == 0 && ret == IRQ_NONE) { + irqstat = checkdone(sl811); + if (irqstat && irq != ~0) + sl811->stat_lost++; + } +#endif + + /* USB packets, not necessarily handled in the order they're + * issued ... that's fine if they're different endpoints. + */ + if (irqstat & SL11H_INTMASK_DONE_A) { + done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs); + sl811->active_a = NULL; + sl811->stat_a++; + } +#ifdef USE_B + if (irqstat & SL11H_INTMASK_DONE_B) { + done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs); + sl811->active_b = NULL; + sl811->stat_b++; + } +#endif + if (irqstat & SL11H_INTMASK_SOFINTR) { + unsigned index; + + index = sl811->frame++ % (PERIODIC_SIZE - 1); + sl811->stat_sof++; + + /* be graceful about almost-inevitable periodic schedule + * overruns: continue the previous frame's transfers iff + * this one has nothing scheduled. + */ + if (sl811->next_periodic) { + // ERR("overrun to slot %d\n", index); + sl811->stat_overrun++; + } + if (sl811->periodic[index]) + sl811->next_periodic = sl811->periodic[index]; + } + + /* khubd manages debouncing and wakeup */ + if (irqstat & SL11H_INTMASK_INSRMV) { + sl811->stat_insrmv++; + + /* most stats are reset for each VBUS session */ + sl811->stat_wake = 0; + sl811->stat_sof = 0; + sl811->stat_a = 0; + sl811->stat_b = 0; + sl811->stat_lost = 0; + + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + /* usbcore nukes other pending transactions on disconnect */ + if (sl811->active_a) { + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_a, + container_of(sl811->active_a->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_a = NULL; + } +#ifdef USE_B + if (sl811->active_b) { + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_b, + container_of(sl811->active_b->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_b = NULL; + } +#endif + + /* port status seems wierd until after reset, so + * force the reset and make khubd clean up later. + */ + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_CONNECTION); + + } else if (irqstat & SL11H_INTMASK_RD) { + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) { + DBG("wakeup\n"); + sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND; + sl811->stat_wake++; + } else + irqstat &= ~SL11H_INTMASK_RD; + } + + if (irqstat) { + if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + start_transfer(sl811); + ret = IRQ_HANDLED; + sl811->hcd.saw_irq = 1; + if (retries--) + goto retry; + } + + if (sl811->periodic_count == 0 && list_empty(&sl811->async)) + sofirq_off(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + spin_unlock(&sl811->lock); + + return ret; +} + +/*-------------------------------------------------------------------------*/ + +/* usb 1.1 says max 90% of a frame is available for periodic transfers. + * this driver doesn't promise that much since it's got to handle an + * IRQ per packet; irq handling latencies also use up that time. + */ +#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ + +static int balance(struct sl811 *sl811, u16 period, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that period + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < period ; i++) { + if (branch < 0 || sl811->load[branch] > sl811->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += period) { + if ((sl811->load[j] + load) + > MAX_PERIODIC_LOAD) + break; + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/*-------------------------------------------------------------------------*/ + +static int sl811h_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + int mem_flags +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct sl811h_ep *ep = NULL; + struct sl811h_req *req; + unsigned long flags; + int i; + int retval = 0; + +#ifdef DISABLE_ISO + if (type == PIPE_ISOCHRONOUS) + return -ENOSPC; +#endif + + /* avoid all allocations within spinlocks: request or endpoint */ + urb->hcpriv = req = kmalloc(sizeof *req, mem_flags); + if (!req) + return -ENOMEM; + req->urb = urb; + + i = epnum << 1; + if (i && is_out) + i |= 1; + if (!hdev->ep[i]) + ep = kcalloc(1, sizeof *ep, mem_flags); + + spin_lock_irqsave(&sl811->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + || !HCD_IS_RUNNING(sl811->hcd.state)) { + retval = -ENODEV; + goto fail; + } + + if (hdev->ep[i]) { + kfree(ep); + ep = hdev->ep[i]; + } else if (!ep) { + retval = -ENOMEM; + goto fail; + + } else { + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->schedule); + ep->udev = usb_get_dev(udev); + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + if (ep->maxpacket > H_MAXPACKET) { + /* iso packets up to 240 bytes could work... */ + DBG("dev %d ep%d maxpacket %d\n", + udev->devnum, epnum, ep->maxpacket); + retval = -EINVAL; + goto fail; + } + + if (udev->speed == USB_SPEED_LOW) { + /* send preamble for external hub? */ + if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD)) + ep->defctrl |= SL11H_HCTLMASK_PREAMBLE; + } + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->period = urb->interval; + ep->branch = PERIODIC_SIZE; + if (type == PIPE_ISOCHRONOUS) + ep->defctrl |= SL11H_HCTLMASK_ISOCH; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, is_out)) + / 1000; + break; + } + + hdev->ep[i] = ep; + } + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) + list_add_tail(&ep->schedule, &sl811->async); + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->period; + if (ep->branch < PERIODIC_SIZE) + break; + + retval = balance(sl811, ep->period, ep->load); + if (retval < 0) + goto fail; + ep->branch = retval; + retval = 0; + urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + + ep->branch; + + /* sort each schedule branch by period (slow before fast) + * to share the faster parts of the tree without needing + * dummy/placeholder nodes + */ + DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep **prev = &sl811->periodic[i]; + struct sl811h_ep *here = *prev; + + while (here && ep != here) { + if (ep->period > here->period) + break; + prev = &here->next; + here = *prev; + } + if (ep != here) { + ep->next = here; + *prev = ep; + } + sl811->load[i] += ep->load; + } + sl811->periodic_count++; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + += ep->load / ep->period; + sofirq_on(sl811); + } + + /* in case of unlink-during-submit */ + spin_lock(&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + finish_request(sl811, ep, req, NULL, 0); + req = NULL; + retval = 0; + goto fail; + } + list_add_tail(&req->queue, &ep->queue); + spin_unlock(&urb->lock); + + start_transfer(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); +fail: + spin_unlock_irqrestore(&sl811->lock, flags); + if (retval) + kfree(req); + return retval; +} + +static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + unsigned long flags; + unsigned i; + struct sl811h_ep *ep; + struct sl811h_req *req = urb->hcpriv; + int retval = 0; + + i = usb_pipeendpoint(pipe) << 1; + if (i && is_out) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + if (ep) { + /* finish right away if this urb can't be active ... + * note that some drivers wrongly expect delays + */ + if (ep->queue.next != &req->queue) { + /* not front of queue? never active */ + + /* for active transfers, we expect an IRQ */ + } else if (sl811->active_a == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_A: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_A(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + 0); + sl811->active_a = NULL; + } else + req = NULL; +#ifdef USE_B + } else if (sl811->active_b == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_B: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_B(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), + 0); + sl811->active_b = NULL; + } else + req = NULL; +#endif + } else { + /* front of queue for inactive endpoint */ + } + + if (req) + finish_request(sl811, ep, req, NULL, 0); + else + VDBG("dequeue, urb %p active %s; wait4irq\n", urb, + (sl811->active_a == ep) ? "A" : "B"); + } else + retval = -EINVAL; + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +static void +sl811h_endpoint_disable(struct usb_hcd *hcd, struct hcd_dev *hdev, int epnum) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct sl811h_ep *ep; + unsigned long flags; + int i; + + i = (epnum & 0xf) << 1; + if (epnum && !(epnum & USB_DIR_IN)) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + hdev->ep[i] = NULL; + spin_unlock_irqrestore(&sl811->lock, flags); + + if (ep) { + /* assume we'd just wait for the irq */ + if (!list_empty(&ep->queue)) + msleep(3); + if (!list_empty(&ep->queue)) + WARN("ep %p not empty?\n", ep); + + usb_put_dev(ep->udev); + kfree(ep); + } + return; +} + +static int +sl811h_get_frame(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + + /* wrong except while periodic transfers are scheduled; + * never matches the on-the-wire frame; + * subject to overruns. + */ + return sl811->frame; +} + + +/*-------------------------------------------------------------------------*/ + +/* the virtual root hub timer IRQ checks for hub status */ +static int +sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); +#ifdef QUIRK3 + unsigned long flags; + + /* non-SMP HACK: use root hub timer as i/o watchdog + * this seems essential when SOF IRQs aren't in use... + */ + local_irq_save(flags); + if (!timer_pending(&sl811->timer)) { + if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE) + sl811->stat_lost++; + } + local_irq_restore(flags); +#endif + + if (!(sl811->port1 & (0xffff << 16))) + return 0; + + /* tell khubd port 1 changed */ + *buf = (1 << 1); + return 1; +} + +static void +sl811h_hub_descriptor ( + struct sl811 *sl811, + struct usb_hub_descriptor *desc +) { + u16 temp = 0; + + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = 1; + desc->bDescLength = 9; + + /* per-port power switching (gang of one!), or none */ + desc->bPwrOn2PwrGood = 0; + if (sl811->board && sl811->board->port_power) { + desc->bPwrOn2PwrGood = sl811->board->potpg; + if (!desc->bPwrOn2PwrGood) + desc->bPwrOn2PwrGood = 10; + temp = 0x0001; + } else + temp = 0x0002; + + /* no overcurrent errors detection/handling */ + temp |= 0x0010; + + desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); + + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = 1 << 1; + desc->bitmap[1] = ~0; +} + +static void +sl811h_timer(unsigned long _sl811) +{ + struct sl811 *sl811 = (void *) _sl811; + unsigned long flags; + u8 irqstat; + u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; + const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) + | (1 << USB_PORT_FEAT_ENABLE) + | (1 << USB_PORT_FEAT_LOWSPEED); + + spin_lock_irqsave(&sl811->lock, flags); + + /* stop special signaling */ + sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + udelay(3); + + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS); + + switch (signaling) { + case SL11H_CTL1MASK_SE0: + DBG("end reset\n"); + sl811->port1 = (1 << USB_PORT_FEAT_C_RESET) + | (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + /* don't wrongly ack RD */ + if (irqstat & SL11H_INTMASK_INSRMV) + irqstat &= ~SL11H_INTMASK_RD; + break; + case SL11H_CTL1MASK_K: + DBG("end resume\n"); + sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND); + break; + default: + DBG("odd timer signaling: %02x\n", signaling); + break; + } + sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + + if (irqstat & SL11H_INTMASK_RD) { + /* usbcore nukes all pending transactions on disconnect */ + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_C_ENABLE); + sl811->port1 &= ~mask; + sl811->irq_enable = SL11H_INTMASK_INSRMV; + } else { + sl811->port1 |= mask; + if (irqstat & SL11H_INTMASK_DP) + sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED); + sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD; + } + + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) { + u8 ctrl2 = SL811HS_CTL2_INIT; + + sl811->irq_enable |= SL11H_INTMASK_DONE_A; +#ifdef USE_B + sl811->irq_enable |= SL11H_INTMASK_DONE_B; +#endif + if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) { + sl811->ctrl1 |= SL11H_CTL1MASK_LSPD; + ctrl2 |= SL811HS_CTL2MASK_DSWAP; + } + + /* start SOFs flowing, kickstarting with A registers */ + sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_SOFLOWREG, 0xe0); + sl811_write(sl811, SL811HS_CTLREG2, ctrl2); + + /* autoincrementing */ + sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0); + writeb(SL_SOF, sl811->data_reg); + writeb(0, sl811->data_reg); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + SL11H_HCTLMASK_ARM); + + /* khubd provides debounce delay */ + } else { + sl811->ctrl1 = 0; + } + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + /* reenable irqs */ + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&sl811->lock, flags); + + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + sl811->port1 &= (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + break; + case USB_PORT_FEAT_SUSPEND: + if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))) + break; + + /* 20 msec of resume/K signaling, other irqs blocked */ + DBG("start resume...\n"); + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 |= SL11H_CTL1MASK_K; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(20)); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 0); + break; + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + break; + default: + goto error; + } + sl811->port1 &= ~(1 << wValue); + break; + case GetHubDescriptor: + sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (wIndex != 1) + goto error; + *(__le32 *) buf = cpu_to_le32(sl811->port1); + +#ifndef VERBOSE + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ +#endif + DBG("GetPortStatus %08x\n", sl811->port1); + break; + case SetPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (sl811->port1 & (1 << USB_PORT_FEAT_RESET)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))) + goto error; + + DBG("suspend...\n"); + sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 1); + break; + case USB_PORT_FEAT_RESET: + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER))) + break; + + /* 50 msec of reset/SE0 signaling, irqs blocked */ + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 = SL11H_CTL1MASK_SE0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->port1 |= (1 << USB_PORT_FEAT_RESET); + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(50)); + break; + default: + goto error; + } + sl811->port1 |= 1 << wValue; + break; + + default: +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +#ifdef CONFIG_PM + +static int +sl811h_hub_suspend(struct usb_hcd *hcd) +{ + // SOFs off + DBG("%s\n", __FUNCTION__); + return 0; +} + +static int +sl811h_hub_resume(struct usb_hcd *hcd) +{ + // SOFs on + DBG("%s\n", __FUNCTION__); + return 0; +} + +#else + +#define sl811h_hub_suspend NULL +#define sl811h_hub_resume NULL + +#endif + + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct sl811 *sl811) { } +static inline void remove_debug_file(struct sl811 *sl811) { } + +#else + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +static void dump_irq(struct seq_file *s, char *label, u8 mask) +{ + seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask, + (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "", + (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "", + (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "", + (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "", + (mask & SL11H_INTMASK_RD) ? " rd" : "", + (mask & SL11H_INTMASK_DP) ? " dp" : ""); +} + +static int proc_sl811h_show(struct seq_file *s, void *unused) +{ + struct sl811 *sl811 = s->private; + struct sl811h_ep *ep; + unsigned i; + + seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n", + sl811->hcd.product_desc, + hcd_name, DRIVER_VERSION, + sl811->port1); + + seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv); + seq_printf(s, "current session: done_a %ld done_b %ld " + "wake %ld sof %ld overrun %ld lost %ld\n\n", + sl811->stat_a, sl811->stat_b, + sl811->stat_wake, sl811->stat_sof, + sl811->stat_overrun, sl811->stat_lost); + + spin_lock_irq(&sl811->lock); + + if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND) + seq_printf(s, "(suspended)\n\n"); + else { + u8 t = sl811_read(sl811, SL11H_CTLREG1); + + seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t, + (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "", + ({char *s; switch (t & SL11H_CTL1MASK_FORCE) { + case SL11H_CTL1MASK_NORMAL: s = ""; break; + case SL11H_CTL1MASK_SE0: s = " se0/reset"; break; + case SL11H_CTL1MASK_K: s = " k/resume"; break; + default: s = "j"; break; + }; s; }), + (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "", + (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : ""); + + dump_irq(s, "irq_enable", + sl811_read(sl811, SL11H_IRQ_ENABLE)); + dump_irq(s, "irq_status", + sl811_read(sl811, SL11H_IRQ_STATUS)); + seq_printf(s, "frame clocks remaining: %d\n", + sl811_read(sl811, SL11H_SOFTMRREG) << 6); + } + + seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a, + sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b, + sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + seq_printf(s, "\n"); + list_for_each_entry (ep, &sl811->async, schedule) { + struct sl811h_req *req; + + seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d" + " nak %d err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep, ep->epnum, + ({ char *s; switch (ep->nextpid) { + case USB_PID_IN: s = "in"; break; + case USB_PID_OUT: s = "out"; break; + case USB_PID_SETUP: s = "setup"; break; + case USB_PID_ACK: s = "status"; break; + default: s = "?"; break; + }; s;}), + ep->maxpacket, + ep->nak_count, ep->error_count); + list_for_each_entry (req, &ep->queue, queue) { + seq_printf(s, " urb%p, %d/%d\n", req->urb, + req->urb->actual_length, + req->urb->transfer_buffer_length); + } + } + if (!list_empty(&sl811->async)) + seq_printf(s, "\n"); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + for (i = 0; i < PERIODIC_SIZE; i++) { + ep = sl811->periodic[i]; + if (!ep) + continue; + seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]); + + /* DUMB: prints shared entries multiple times */ + do { + seq_printf(s, + " %s%sqh%d/%p (%sdev%d ep%d%s max %d) " + "err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep->period, ep, + (ep->udev->speed == USB_SPEED_FULL) + ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" + : ((ep->nextpid == USB_PID_IN) + ? "in" + : "out"), + ep->maxpacket, ep->error_count); + ep = ep->next; + } while (ep); + } + + spin_unlock_irq(&sl811->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_sl811h_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_sl811h_show, PDE(inode)->data); +} + +static struct file_operations proc_ops = { + .open = proc_sl811h_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one sl811 per system */ +static const char proc_filename[] = "driver/sl811h"; + +static void create_debug_file(struct sl811 *sl811) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) + return; + + pde->proc_fops = &proc_ops; + pde->data = sl811; + sl811->pde = pde; +} + +static void remove_debug_file(struct sl811 *sl811) +{ + if (sl811->pde) + remove_proc_entry(proc_filename, NULL); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void +sl811h_stop(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + unsigned long flags; + + del_timer_sync(&sl811->hcd.rh_timer); + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_start(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev; + + /* chip has been reset, VBUS power is off */ + + udev = usb_alloc_dev(NULL, &sl811->hcd.self, 0); + if (!udev) + return -ENOMEM; + + udev->speed = USB_SPEED_FULL; + hcd->state = USB_STATE_RUNNING; + + if (sl811->board) + sl811->hcd.can_wakeup = sl811->board->can_wakeup; + + if (hcd_register_root(udev, &sl811->hcd) != 0) { + usb_put_dev(udev); + sl811h_stop(hcd); + return -ENODEV; + } + + if (sl811->board && sl811->board->power) + hub_set_power_budget(udev, sl811->board->power * 2); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct hc_driver sl811h_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .flags = HCD_USB11, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = sl811h_urb_enqueue, + .urb_dequeue = sl811h_urb_dequeue, + .endpoint_disable = sl811h_endpoint_disable, + + /* + * periodic schedule support + */ + .get_frame_number = sl811h_get_frame, + + /* + * root hub support + */ + .hub_status_data = sl811h_hub_status_data, + .hub_control = sl811h_hub_control, + .hub_suspend = sl811h_hub_suspend, + .hub_resume = sl811h_hub_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init_or_module +sl811h_remove(struct device *dev) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + struct platform_device *pdev; + struct resource *res; + + pdev = container_of(dev, struct platform_device, dev); + + if (HCD_IS_RUNNING(sl811->hcd.state)) + sl811->hcd.state = USB_STATE_QUIESCING; + + usb_disconnect(&sl811->hcd.self.root_hub); + remove_debug_file(sl811); + sl811h_stop(&sl811->hcd); + + if (!list_empty(&sl811->hcd.self.bus_list)) + usb_deregister_bus(&sl811->hcd.self); + + if (sl811->hcd.irq >= 0) + free_irq(sl811->hcd.irq, sl811); + + if (sl811->data_reg) + iounmap(sl811->data_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(res->start, 1); + + if (sl811->addr_reg) + iounmap(sl811->addr_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, 1); + + kfree(sl811); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init +sl811h_probe(struct device *dev) +{ + struct sl811 *sl811; + struct platform_device *pdev; + struct resource *addr, *data; + int irq; + int status; + u8 tmp; + unsigned long flags; + + /* basic sanity checks first. board-specific init logic should + * have initialized these three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + pdev = container_of(dev, struct platform_device, dev); + if (pdev->num_resources < 3) + return -ENODEV; + + addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) + return -ENODEV; + + /* refuse to confuse usbcore */ + if (dev->dma_mask) { + DBG("no we won't dma\n"); + return -EINVAL; + } + + if (!request_mem_region(addr->start, 1, hcd_name)) + return -EBUSY; + if (!request_mem_region(data->start, 1, hcd_name)) { + release_mem_region(addr->start, 1); + return -EBUSY; + } + + /* allocate and initialize hcd */ + sl811 = kcalloc(1, sizeof *sl811, GFP_KERNEL); + if (!sl811) + return 0; + dev_set_drvdata(dev, sl811); + + usb_bus_init(&sl811->hcd.self); + sl811->hcd.self.controller = dev; + sl811->hcd.self.bus_name = dev->bus_id; + sl811->hcd.self.op = &usb_hcd_operations; + sl811->hcd.self.hcpriv = sl811; + + // NOTE: 2.6.11 starts to change the hcd glue layer some more, + // eventually letting us eliminate struct sl811h_req and a + // lot of the boilerplate code here + + INIT_LIST_HEAD(&sl811->hcd.dev_list); + sl811->hcd.self.release = &usb_hcd_release; + + sl811->hcd.description = sl811h_hc_driver.description; + init_timer(&sl811->hcd.rh_timer); + sl811->hcd.driver = &sl811h_hc_driver; + sl811->hcd.irq = -1; + sl811->hcd.state = USB_STATE_HALT; + + spin_lock_init(&sl811->lock); + INIT_LIST_HEAD(&sl811->async); + sl811->board = dev->platform_data; + init_timer(&sl811->timer); + sl811->timer.function = sl811h_timer; + sl811->timer.data = (unsigned long) sl811; + + sl811->addr_reg = ioremap(addr->start, resource_len(addr)); + if (sl811->addr_reg == NULL) { + status = -ENOMEM; + goto fail; + } + sl811->data_reg = ioremap(data->start, resource_len(addr)); + if (sl811->data_reg == NULL) { + status = -ENOMEM; + goto fail; + } + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); + msleep(200); + + tmp = sl811_read(sl811, SL11H_HWREVREG); + switch (tmp >> 4) { + case 1: + sl811->hcd.product_desc = "SL811HS v1.2"; + break; + case 2: + sl811->hcd.product_desc = "SL811HS v1.5"; + break; + default: + /* reject case 0, SL11S is less functional */ + DBG("chiprev %02x\n", tmp); + status = -ENXIO; + goto fail; + } + + /* sl811s would need a different handler for this irq */ +#ifdef CONFIG_ARM + /* Cypress docs say the IRQ is IRQT_HIGH ... */ + set_irq_type(irq, IRQT_RISING); +#endif + status = request_irq(irq, sl811h_irq, SA_INTERRUPT, hcd_name, sl811); + if (status < 0) + goto fail; + sl811->hcd.irq = irq; + + INFO("%s, irq %d\n", sl811->hcd.product_desc, irq); + + status = usb_register_bus(&sl811->hcd.self); + if (status < 0) + goto fail; + status = sl811h_start(&sl811->hcd); + if (status == 0) { + create_debug_file(sl811); + return 0; + } +fail: + sl811h_remove(dev); + DBG("init error, %d\n", status); + return status; +} + +#ifdef CONFIG_PM + +/* for this device there's no useful distinction between the controller + * and its root hub, except that the root hub only gets direct PM calls + * when CONFIG_USB_SUSPEND is enabled. + */ + +static int +sl811h_suspend(struct device *dev, u32 state, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + int retval = 0; + + if (phase != SUSPEND_POWER_DOWN) + return retval; + + if (state <= PM_SUSPEND_MEM) + retval = sl811h_hub_suspend(&sl811->hcd); + else + port_power(sl811, 0); + if (retval == 0) + dev->power.power_state = state; + return retval; +} + +static int +sl811h_resume(struct device *dev, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + + if (phase != RESUME_POWER_ON) + return 0; + + /* with no "check to see if VBUS is still powered" board hook, + * let's assume it'd only be powered to enable remote wakeup. + */ + if (dev->power.power_state > PM_SUSPEND_MEM + || !sl811->hcd.can_wakeup) { + sl811->port1 = 0; + port_power(sl811, 1); + return 0; + } + + dev->power.power_state = PM_SUSPEND_ON; + return sl811h_hub_resume(&sl811->hcd); +} + +#else + +#define sl811h_suspend NULL +#define sl811h_resume NULL + +#endif + + +static struct device_driver sl811h_driver = { + .name = (char *) hcd_name, + .bus = &platform_bus_type, + + .probe = sl811h_probe, + .remove = sl811h_remove, + + .suspend = sl811h_suspend, + .resume = sl811h_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init sl811h_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return driver_register(&sl811h_driver); +} +module_init(sl811h_init); + +static void __exit sl811h_cleanup(void) +{ + driver_unregister(&sl811h_driver); +} +module_exit(sl811h_cleanup); diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h new file mode 100644 index 000000000000..1f5f0d48de85 --- /dev/null +++ b/drivers/usb/host/sl811.h @@ -0,0 +1,270 @@ +/* + * SL811HS register declarations and HCD data structures + * + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * Copyright (C) 2001 Cypress Semiconductor Inc. + */ + +/* + * SL811HS has transfer registers, and control registers. In host/master + * mode one set of registers is used; in peripheral/slave mode, another. + * - SL11H only has some "A" transfer registers from 0x00-0x04 + * - SL811HS also has "B" registers from 0x08-0x0c + * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30 + */ + +#define SL811_EP_A(base) ((base) + 0) +#define SL811_EP_B(base) ((base) + 8) + +#define SL811_HOST_BUF 0x00 +#define SL811_PERIPH_EP0 0x00 +#define SL811_PERIPH_EP1 0x10 +#define SL811_PERIPH_EP2 0x20 +#define SL811_PERIPH_EP3 0x30 + + +/* TRANSFER REGISTERS: host and peripheral sides are similar + * except for the control models (master vs slave). + */ +#define SL11H_HOSTCTLREG 0 +# define SL11H_HCTLMASK_ARM 0x01 +# define SL11H_HCTLMASK_ENABLE 0x02 +# define SL11H_HCTLMASK_IN 0x00 +# define SL11H_HCTLMASK_OUT 0x04 +# define SL11H_HCTLMASK_ISOCH 0x10 +# define SL11H_HCTLMASK_AFTERSOF 0x20 +# define SL11H_HCTLMASK_TOGGLE 0x40 +# define SL11H_HCTLMASK_PREAMBLE 0x80 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +# define SL11H_STATMASK_ACK 0x01 +# define SL11H_STATMASK_ERROR 0x02 +# define SL11H_STATMASK_TMOUT 0x04 +# define SL11H_STATMASK_SEQ 0x08 +# define SL11H_STATMASK_SETUP 0x10 +# define SL11H_STATMASK_OVF 0x20 +# define SL11H_STATMASK_NAK 0x40 +# define SL11H_STATMASK_STALL 0x80 +#define SL11H_PIDEPREG 3 /* write */ +# define SL_SETUP 0xd0 +# define SL_IN 0x90 +# define SL_OUT 0x10 +# define SL_SOF 0x50 +# define SL_PREAMBLE 0xc0 +# define SL_NAK 0xa0 +# define SL_STALL 0xe0 +# define SL_DATA0 0x30 +# define SL_DATA1 0xb0 +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ + + +/* CONTROL REGISTERS: host and peripheral are very different. + */ +#define SL11H_CTLREG1 5 +# define SL11H_CTL1MASK_SOF_ENA 0x01 +# define SL11H_CTL1MASK_FORCE 0x18 +# define SL11H_CTL1MASK_NORMAL 0x00 +# define SL11H_CTL1MASK_SE0 0x08 /* reset */ +# define SL11H_CTL1MASK_J 0x10 +# define SL11H_CTL1MASK_K 0x18 /* resume */ +# define SL11H_CTL1MASK_LSPD 0x20 +# define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_IRQ_ENABLE 6 +# define SL11H_INTMASK_DONE_A 0x01 +# define SL11H_INTMASK_DONE_B 0x02 +# define SL11H_INTMASK_SOFINTR 0x10 +# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */ +# define SL11H_INTMASK_RD 0x40 +# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */ +#define SL11S_ADDRESS 7 + +/* 0x08-0x0c are for the B buffer (not in SL11) */ + +#define SL11H_IRQ_STATUS 0x0D /* write to ack */ +#define SL11H_HWREVREG 0x0E /* read */ +# define SL11H_HWRMASK_HWREV 0xF0 +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ + +/* a write to this register enables SL811HS features. + * HOST flag presumably overrides the chip input signal? + */ +#define SL811HS_CTLREG2 0x0F +# define SL811HS_CTL2MASK_SOF_MASK 0x3F +# define SL811HS_CTL2MASK_DSWAP 0x40 +# define SL811HS_CTL2MASK_HOST 0x80 + +#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e) + + +/* DATA BUFFERS: registers from 0x10..0xff are for data buffers; + * that's 240 bytes, which we'll split evenly between A and B sides. + * Only ISO can use more than 64 bytes per packet. + * (The SL11S has 0x40..0xff for buffers.) + */ +#define H_MAXPACKET 120 /* bytes in A or B fifos */ + +#define SL11H_DATA_START 0x10 +#define SL811HS_PACKET_BUF(is_a) ((is_a) \ + ? SL11H_DATA_START \ + : (SL11H_DATA_START + H_MAXPACKET)) + +/*-------------------------------------------------------------------------*/ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct sl811 { + struct usb_hcd hcd; + spinlock_t lock; + void __iomem *addr_reg; + void __iomem *data_reg; + struct sl811_platform_data *board; + struct proc_dir_entry *pde; + + unsigned long stat_insrmv; + unsigned long stat_wake; + unsigned long stat_sof; + unsigned long stat_a; + unsigned long stat_b; + unsigned long stat_lost; + unsigned long stat_overrun; + + /* sw model */ + struct timer_list timer; + struct sl811h_ep *next_periodic; + struct sl811h_ep *next_async; + + struct sl811h_ep *active_a; + unsigned long jiffies_a; + struct sl811h_ep *active_b; + unsigned long jiffies_b; + + u32 port1; + u8 ctrl1, ctrl2, irq_enable; + u16 frame; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: interrupt, iso */ + u16 load[PERIODIC_SIZE]; + struct sl811h_ep *periodic[PERIODIC_SIZE]; + unsigned periodic_count; +}; + +static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd) +{ + return container_of(hcd, struct sl811, hcd); +} + +struct sl811h_ep { + struct list_head queue; + struct usb_device *udev; + + u8 defctrl; + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 error_count; + u16 nak_count; + u16 length; /* of current packet */ + + /* periodic schedule */ + u16 period; + u16 branch; + u16 load; + struct sl811h_ep *next; + + /* async schedule */ + struct list_head schedule; +}; + +struct sl811h_req { + /* FIXME usbcore should maintain endpoints' urb queues + * directly in 'struct usb_host_endpoint' + */ + struct urb *urb; + struct list_head queue; +}; + +/*-------------------------------------------------------------------------*/ + +/* These register utilities should work for the SL811S register API too + * NOTE: caller must hold sl811->lock. + */ + +static inline u8 sl811_read(struct sl811 *sl811, int reg) +{ + writeb(reg, sl811->addr_reg); + return readb(sl811->data_reg); +} + +static inline void sl811_write(struct sl811 *sl811, int reg, u8 val) +{ + writeb(reg, sl811->addr_reg); + writeb(val, sl811->data_reg); +} + +static inline void +sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count) +{ + const u8 *data; + void __iomem *data_reg; + + if (!count) + return; + writeb(addr, sl811->addr_reg); + + data = buf; + data_reg = sl811->data_reg; + do { + writeb(*data++, data_reg); + } while (--count); +} + +static inline void +sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) +{ + u8 *data; + void __iomem *data_reg; + + if (!count) + return; + writeb(addr, sl811->addr_reg); + + data = buf; + data_reg = sl811->data_reg; + do { + *data++ = readb(data_reg); + } while (--count); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#ifdef PACKET_TRACE +# define PACKET VDBG +#else +# define PACKET(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) + diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index a0721d06059e..baf7444411ea 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -34,17 +34,6 @@ static inline void lprintk(char *buf) } } -static inline int uhci_is_skeleton_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) -{ - int i; - - for (i = 0; i < UHCI_NUM_SKELQH; i++) - if (qh == uhci->skelqh[i]) - return 1; - - return 0; -} - static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) { char *out = buf; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index e3e0396d2a70..1feccf2f03b5 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -69,7 +69,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) * FIXME: Synchronize access to these fields by a spinlock. */ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, - unsigned int port_addr) + unsigned long port_addr) { int status; @@ -78,13 +78,19 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, clear_bit(port, &uhci->suspended_ports); clear_bit(port, &uhci->resuming_ports); set_bit(port, &uhci->port_c_suspend); + + /* The controller won't actually turn off the RD bit until + * it has had a chance to send a low-speed EOP sequence, + * which takes 3 bit times (= 2 microseconds). We'll delay + * slightly longer for good luck. */ + udelay(4); } } static void uhci_check_resume(struct uhci_hcd *uhci) { unsigned int port; - unsigned int port_addr; + unsigned long port_addr; for (port = 0; port < uhci->rh_numports; ++port) { port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 1324e7272a2b..a215c2f9f816 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -2016,8 +2016,6 @@ static void __devexit aty128_remove(struct pci_dev *pdev) release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); - release_mem_region(pci_resource_start(pdev, 1), - pci_resource_len(pdev, 1)); release_mem_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2)); framebuffer_release(info); diff --git a/fs/Kconfig b/fs/Kconfig index cd6ca6da9119..3a0bb962c67e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1718,12 +1718,22 @@ config CIFS_XATTR config CIFS_POSIX bool "CIFS POSIX Extensions (EXPERIMENTAL)" - depends on CIFS + depends on CIFS_XATTR help Enabling this option will cause the cifs client to attempt to negotiate a newer dialect with servers, such as Samba 3.0.5 or later, that optionally can handle more POSIX like (rather - than Windows like) file behavior. If unsure, say N. + than Windows like) file behavior. It also enables + support for POSIX ACLs (getfacl and setfacl) to servers + (such as Samba 3.10 and later) which can negotiate + CIFS POSIX ACL support. If unsure, say N. + +config CIFS_EXPERIMENTAL + bool "CIFS Experimental Features (EXPERIMENTAL)" + depends on CIFS + help + Enables cifs features under testing. These features + are highly experimental. If unsure, say N. config NCP_FS tristate "NCP file system support (to mount NetWare volumes)" diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 2a1924078059..4aaceb3733ed 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,53 @@ +Version 1.28 +------------ +Add module init parm for large SMB buffer size (to allow it to be changed +from its default of 16K) which is especially useful for large file copy +when mounting with the directio mount option. Fix oops after +returning from mount when experimental ExtendedSecurity enabled and +SpnegoNegotiated returning invalid error. Fix case to retry better when +peek returns from 1 to 3 bytes on socket which should have more data. +Fixed path based calls (such as cifs lookup) to handle path names +longer than 530 (now can handle PATH_MAX). + +Version 1.27 +------------ +Turn off DNOTIFY (directory change notification support) by default +(unless built with the experimental flag) to fix hang with KDE +file browser. Fix DNOTIFY flag mappings. Fix hang (in wait_event +waiting on an SMB response) in SendReceive when session dies but +reconnects quickly from another task. Add module init parms for +minimum number of large and small network buffers in the buffer pools, +and for the maximum number of simultaneous requests. + +Version 1.26 +------------ +Add setfacl support to allow setting of ACLs remotely to Samba 3.10 and later +and other POSIX CIFS compliant servers. Fix error mapping for getfacl +to EOPNOTSUPP when server does not support posix acls on the wire. Fix +improperly zeroed buffer in CIFS Unix extensions set times call. + +Version 1.25 +------------ +Fix internationlization problem in cifs readdir with filenames that map to +longer UTF8 strings than the string on the wire was in Unicode. Add workaround +for readdir to netapp servers. Fix search rewind (seek into readdir to return +non-consecutive entries). Do not do readdir when server negotiates +buffer size to small to fit filename. Add support for reading POSIX ACLs from +the server (add also acl and noacl mount options). + +Version 1.24 +------------ +Optionally allow using server side inode numbers, rather than client generated +ones by specifying mount option "serverino" - this is required for some apps +to work which double check hardlinked files and have persistent inode numbers. + +Version 1.23 +------------ +Multiple bigendian fixes. On little endian systems (for reconnect after +network failure) fix tcp session reconnect code so we do not try first +to reconnect on reverse of port 445. Treat reparse points (NTFS junctions) +as directories rather than symlinks because we can do follow link on them. + Version 1.22 ------------ Add config option to enable XATTR (extended attribute) support, mapping diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 955bf5138fa5..ec52a34fa023 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -3,4 +3,4 @@ # obj-$(CONFIG_CIFS) += cifs.o -cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o +cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o diff --git a/fs/cifs/README b/fs/cifs/README index acf1448e6096..1643ef5426cc 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -64,6 +64,13 @@ trivially built from Samba 3.0 or later source e.g. by executing: gcc samba/source/client/mount.cifs.c -o mount.cifs +If cifs is built as a module, then the size and number of network buffers +and maximum number of simultaneous requests to one server can be configured. +Changing these from their defaults is not recommended. By executing modinfo + modinfo kernel/fs/cifs/cifs.ko +on kernel/fs/cifs/cifs.ko the list of configuration changes that can be made +at module initialization time (by running insmod cifs.ko) can be seen. + Allowing User Mounts ==================== To permit users to mount and unmount over directories they own is possible @@ -115,11 +122,20 @@ cifs client, and that EA support is present in later versions of Samba (e.g. 3.0.6 and later (also EA support works in all versions of Windows, at least to shares on NTFS filesystems). Extended Attribute (xattr) support is an optional feature of most Linux filesystems which may require enabling via -make menuconfig +make menuconfig. + +The CIFS client can get and set POSIX ACLs (getfacl, setfacl) to Samba servers +version 3.10 and later. Setting POSIX ACLs requires enabling both XATTR and +then POSIX support in the CIFS configuration options when building the cifs +module. Some administrators may want to change Samba's smb.conf "map archive" and -"create mask" parameters from the default. Creating special devices (mknod) -remotely may require specifying a mkdev function to Samba if you are not using +"create mask" parameters from the default. Unless the create mask is changed +newly created files can end up with an unnecessarily restrictive default mode, +which may not be what you want, although if the CIFS Unix extensions are +enabled on the server and client, subsequent setattr calls (e.g. chmod) can +fix the mode. Note that creating special devices (mknod) remotely +may require specifying a mkdev function to Samba if you are not using Samba 3.0.6 or later. For more information on these see the manual pages ("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the smbfs vfs, does not read the smb.conf on the client system @@ -191,7 +207,20 @@ Servers must support the NTLM SMB dialect (which is the most recent, supported by Samba and Windows NT version 4, 2000 and XP and many other SMB/CIFS servers) Servers must support either "pure-TCP" (port 445 TCP/IP CIFS connections) or RFC 1001/1002 support for "Netbios-Over-TCP/IP." Neither of these is likely to be a -problem as most servers support this. IPv6 support is planned for the future. +problem as most servers support this. IPv6 support is planned for the future, +and is almost complete. + +Valid filenames differ between Windows and Linux. Windows typically restricts +filenames which contain certain reserved characters (e.g.the character : +which is used to delimit the beginning of a stream name by Windows), while +Linux allows a slightly wider set of valid characters in filenames. Windows +servers can remap such characters when an explicit mapping is specified in +the Server's registry. Samba starting with version 3.10 will allow such +filenames (ie those which contain valid Linux characters, which normally +would be forbidden for Windows/CIFS semantics) as long as the server is +configured for Unix Extensions (and the client has not disabled +/proc/fs/cifs/LinuxExtensionsEnabled). + CIFS VFS Mount Options ====================== @@ -266,6 +295,10 @@ A partial list of the supported mount options follows: If you do not trust the servers in your network (your mount targets) it is recommended that you specify this option for greater security. + exec Permit execution of binaries on the mount. + noexec Do not permit execution of binaries on the mount. + dev Recognize block devices on the remote mount. + nodev Do not recognize devices on the remote mount. suid Allow remote files on this mountpoint with suid enabled to be executed (default for mounts when executed as root, nosuid is default for user mounts). @@ -292,6 +325,22 @@ A partial list of the supported mount options follows: Note that this does not affect the normal ACL check on the target machine done by the server software (of the server ACL against the user name provided at mount time). + serverino Use servers inode numbers instead of generating automatically + incrementing inode numbers on the client. Although this will + make it easier to spot hardlinked files (as they will have + the same inode numbers) and inode numbers may be persistent, + note that the server does not guarantee that the inode numbers + are unique if multiple server side mounts are exported under a + single share (since inode numbers on the servers might not + be unique if multiple filesystems are mounted under the same + shared higher level directory). Note that this requires that + the server support the CIFS Unix Extensions as other servers + do not return a unique IndexNumber on SMB FindFirst (most + servers return zero as the IndexNumber). Parameter has no + effect to Windows servers and others which do not support the + CIFS Unix Extensions. + noserverino Client generates inode numbers (rather than using the actual one + from the server) by default. setuids If the CIFS Unix extensions are negotiated with the server the client will attempt to set the effective uid and gid of the local process on newly created files, directories, and @@ -304,6 +353,23 @@ A partial list of the supported mount options follows: the client) set the uid and gid is the default. This parameter has no effect if the CIFS Unix Extensions are not negotiated. + netbiosname When mounting to servers via port 139, specifies the RFC1001 + source name to use to represent the client netbios machine + name when doing the RFC1001 netbios session initialize. + direct Do not do inode data caching on files opened on this mount. + This precludes mmaping files on this mount. In some cases + with fast networks and little or no caching benefits on the + client (e.g. when the application is doing large sequential + reads bigger than page size without rereading the same data) + this can provide better performance than the default + behavior which caches reads (reaadahead) and writes + (writebehind) through the local Linux client pagecache + if oplock (caching token) is granted and held. Note that + direct allows write operations larger than page size + to be sent to the server. + acl Allow setfacl and getfacl to manage posix ACLs if server + supports them. (default) + noacl Do not allow setfacl and getfacl calls on this mount The mount.cifs mount helper also accepts a few mount options before -o including: @@ -375,14 +441,14 @@ and for more extensive tracing including the start of smb requests and responses echo 1 > /proc/fs/cifs/traceSMB Two other experimental features are under development and to test -require enabling an ifdef (e.g. by adding "#define CIFS_FCNTL" in cifsglob.h) +require enabling CONFIG_CIFS_EXPERIMENTAL - CONFIG_CIFS_QUOTA + More efficient write operations and SMB buffer handling - CONFIG_CIFS_FCNTL (fcntl needed for support of directory change + DNOTIFY fcntl: needed for support of directory change notification and perhaps later for file leases) -Per share (per client mount) statistics are available in /proc/fs/cifs/DebugData +Per share (per client mount) statistics are available in /proc/fs/cifs/Stats if the kernel was configured with cifs statistics enabled. The statistics represent the number of successful (ie non-zero return code from the server) SMB responses to some of the more common commands (open, delete, mkdir etc.). diff --git a/fs/cifs/TODO b/fs/cifs/TODO index 05464a35b81c..f4e3e1f67ee4 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -14,7 +14,7 @@ b) Better pam/winbind integration (e.g. to handle uid mapping better) c) multi-user mounts - multiplexed sessionsetups over single vc -(ie tcp session) - prettying up needed +(ie tcp session) - prettying up needed, and more testing needed d) Kerberos/SPNEGO session setup support - (started) @@ -40,8 +40,8 @@ k) hook lower into the sockets api (as NFS/SunRPC does) to avoid the extra copy in/out of the socket buffers in some cases. l) finish support for IPv6. This is mostly complete but -needs a simple inet_pton like function to convert ipv6 -addresses in string representation. +needs a simple conversion of ipv6 to sin6_addr from the +address in string representation. m) Better optimize open (and pathbased setfilesize) to reduce the oplock breaks coming from windows srv. Piggyback identical file @@ -50,23 +50,24 @@ than resending (helps reduce server resource utilization and avoid spurious oplock breaks). o) Improve performance of readpages by sending more than one read -at a time when 8 pages or more are requested. Evaluate whether -reads larger than 16K would be helpful. +at a time when 8 pages or more are requested. In conjuntion +add support for async_cifs_readpages. -p) For support of Windows9x/98 we need to retry failed mounts -to *SMBSERVER (default server name) with the uppercase hostname -in the RFC1001 session_init request. - -q) Add support for storing symlink and fifo info to Windows servers +p) Add support for storing symlink and fifo info to Windows servers in the Extended Attribute format their SFU clients would recognize. -r) Finish fcntl D_NOTIFY support so kde and gnome file list windows -will autorefresh +q) Finish fcntl D_NOTIFY support so kde and gnome file list windows +will autorefresh (started) + +r) Add GUI tool to configure /proc/fs/cifs settings and for display of +the CIFS statistics (started) + +q) implement support for security and trusted categories of xattrs +(requires minor protocol extension) to enable better support for SELINUX -s) Add GUI tool to configure /proc/fs/cifs settings and for display of -the CIFS statistics +r) Implement O_DIRECT flag on open (already supported on mount) -KNOWN BUGS (updated May 27, 2004) +KNOWN BUGS (updated December 10, 2004) ==================================== 1) existing symbolic links (Windows reparse points) are recognized but can not be created remotely. They are implemented for Samba and those that @@ -83,9 +84,6 @@ Samba (may be unmappable due to POSIX to Windows lock model differences but worth investigating). Also debug Samba to see why lock test case 7 takes longer to complete to Samba than to Windows. -5) implement search rewind (seeking backward in a readdir), which is -necessary for one of the "special" subsection of posix file API -tests in the Connectathon nfs test suite. Misc testing to do ================== diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 0cf2b223e490..716dd019bcd1 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -201,7 +201,12 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, sprintf(buf,"SMB Request/Response Buffer: %d\n", bufAllocCount.counter); length += item_length; - buf += item_length; + buf += item_length; + item_length = + sprintf(buf,"SMB Small Req/Resp Buffer: %d\n", + smBufAllocCount.counter); + length += item_length; + buf += item_length; item_length = sprintf(buf,"Operations (MIDs): %d\n", midCount.counter); @@ -337,7 +342,7 @@ cifs_proc_init(void) if (pde) pde->write_proc = oplockEnabled_write; - pde = create_proc_read_entry("QuotaEnabled", 0, proc_fs_cifs, + pde = create_proc_read_entry("ReenableOldCifsReaddirCode", 0, proc_fs_cifs, quotaEnabled_read, NULL); if (pde) pde->write_proc = quotaEnabled_write; @@ -396,7 +401,7 @@ cifs_proc_clean(void) remove_proc_entry("ExtendedSecurity",proc_fs_cifs); remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs); - remove_proc_entry("QuotaEnabled",proc_fs_cifs); + remove_proc_entry("ReenableOldCifsReaddirCode",proc_fs_cifs); remove_proc_entry("LookupCacheEnabled",proc_fs_cifs); remove_proc_entry("cifs", proc_root_fs); } @@ -485,7 +490,7 @@ quotaEnabled_read(char *page, char **start, off_t off, { int len; - len = sprintf(page, "%d\n", quotaEnabled); + len = sprintf(page, "%d\n", experimEnabled); /* could also check if quotas are enabled in kernel as a whole first */ len -= off; @@ -512,9 +517,9 @@ quotaEnabled_write(struct file *file, const char __user *buffer, if (rc) return rc; if (c == '0' || c == 'n' || c == 'N') - quotaEnabled = 0; + experimEnabled = 0; else if (c == '1' || c == 'y' || c == 'Y') - quotaEnabled = 1; + experimEnabled = 1; return count; } diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 592af83bcb9c..b6c62f591568 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifs_fs_sb.h * - * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) International Business Machines Corp., 2002,2004 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -18,8 +18,10 @@ #ifndef _CIFS_FS_SB_H #define _CIFS_FS_SB_H -#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ +#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ #define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */ +#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ +#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 996fc298a306..8de4890ca1ae 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -50,15 +50,27 @@ int cifsFYI = 0; int cifsERROR = 1; int traceSMB = 0; unsigned int oplockEnabled = 1; -unsigned int quotaEnabled = 0; +unsigned int experimEnabled = 0; unsigned int linuxExtEnabled = 1; unsigned int lookupCacheEnabled = 1; unsigned int multiuser_mount = 0; unsigned int extended_security = 0; unsigned int ntlmv2_support = 0; unsigned int sign_CIFS_PDUs = 1; -unsigned int CIFSMaximumBufferSize = CIFS_MAX_MSGSIZE; struct task_struct * oplockThread = NULL; +unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; +module_param(CIFSMaxBufSize, int, CIFS_MAX_MSGSIZE); +MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048"); +unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; +module_param(cifs_min_rcv, int, CIFS_MIN_RCV_POOL); +MODULE_PARM_DESC(cifs_min_rcv,"Network buffers in pool. Default: 4 Range: 1 to 64"); +unsigned int cifs_min_small = 30; +module_param(cifs_min_small, int, 30); +MODULE_PARM_DESC(cifs_small_rcv,"Small network buffers in pool. Default: 30 Range: 2 to 256"); +unsigned int cifs_max_pending = CIFS_MAX_REQ; +module_param(cifs_max_pending, int, CIFS_MAX_REQ); +MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256"); + extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); @@ -207,6 +219,8 @@ static kmem_cache_t *cifs_inode_cachep; static kmem_cache_t *cifs_req_cachep; static kmem_cache_t *cifs_mid_cachep; kmem_cache_t *cifs_oplock_cachep; +static kmem_cache_t *cifs_sm_req_cachep; +mempool_t *cifs_sm_req_poolp; mempool_t *cifs_req_poolp; mempool_t *cifs_mid_poolp; @@ -431,6 +445,20 @@ cifs_read_wrapper(struct file * file, char __user *read_data, size_t read_size, return -EIO; cFYI(1,("In read_wrapper size %zd at %lld",read_size,*poffset)); + +#ifdef CONFIG_CIFS_EXPERIMENTAL + /* check whether we can cache writes locally */ + if(file->f_dentry->d_sb) { + struct cifs_sb_info *cifs_sb; + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + if(cifs_sb != NULL) { + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) + return cifs_user_read(file,read_data, + read_size,poffset); + } + } +#endif /* CIFS_EXPERIMENTAL */ + if(CIFS_I(file->f_dentry->d_inode)->clientCanCacheRead) { return generic_file_read(file,read_data,read_size,poffset); } else { @@ -463,7 +491,19 @@ cifs_write_wrapper(struct file * file, const char __user *write_data, cFYI(1,("In write_wrapper size %zd at %lld",write_size,*poffset)); +#ifdef CONFIG_CIFS_EXPERIMENTAL /* BB fixme - fix user char * to kernel char * mapping here BB */ /* check whether we can cache writes locally */ + if(file->f_dentry->d_sb) { + struct cifs_sb_info *cifs_sb; + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + if(cifs_sb != NULL) { + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + return cifs_user_write(file,write_data, + write_size,poffset); + } + } + } +#endif /* CIFS_EXPERIMENTAL */ written = generic_file_write(file,write_data,write_size,poffset); if(!CIFS_I(file->f_dentry->d_inode)->clientCanCacheAll) { if(file->f_dentry->d_inode->i_mapping) { @@ -495,6 +535,12 @@ struct inode_operations cifs_dir_inode_ops = { .setattr = cifs_setattr, .symlink = cifs_symlink, .mknod = cifs_mknod, +#ifdef CONFIG_CIFS_XATTR + .setxattr = cifs_setxattr, + .getxattr = cifs_getxattr, + .listxattr = cifs_listxattr, + .removexattr = cifs_removexattr, +#endif }; struct inode_operations cifs_file_inode_ops = { @@ -537,14 +583,18 @@ struct file_operations cifs_file_ops = { .flush = cifs_flush, .mmap = cifs_file_mmap, .sendfile = generic_file_sendfile, +#ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, +#endif /* CONFIG_CIFS_EXPERIMENTAL */ }; struct file_operations cifs_dir_ops = { .readdir = cifs_readdir, .release = cifs_closedir, .read = generic_read_dir, +#ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, +#endif /* CONFIG_CIFS_EXPERIMENTAL */ }; static void @@ -582,14 +632,31 @@ cifs_destroy_inodecache(void) static int cifs_init_request_bufs(void) { + if(CIFSMaxBufSize < 8192) { + /* Buffer size can not be smaller than 2 * PATH_MAX since maximum + Unicode path name has to fit in any SMB/CIFS path based frames */ + CIFSMaxBufSize = 8192; + } else if (CIFSMaxBufSize > 1024*127) { + CIFSMaxBufSize = 1024 * 127; + } else { + CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/ + } +/* cERROR(1,("CIFSMaxBufSize %d 0x%x",CIFSMaxBufSize,CIFSMaxBufSize)); */ cifs_req_cachep = kmem_cache_create("cifs_request", - CIFS_MAX_MSGSIZE + + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (cifs_req_cachep == NULL) return -ENOMEM; - cifs_req_poolp = mempool_create(CIFS_MIN_RCV_POOL, + if(cifs_min_rcv < 1) + cifs_min_rcv = 1; + else if (cifs_min_rcv > 64) { + cifs_min_rcv = 64; + cFYI(1,("cifs_min_rcv set to maximum (64)")); + } + + cifs_req_poolp = mempool_create(cifs_min_rcv, mempool_alloc_slab, mempool_free_slab, cifs_req_cachep); @@ -598,6 +665,40 @@ cifs_init_request_bufs(void) kmem_cache_destroy(cifs_req_cachep); return -ENOMEM; } + /* 256 (MAX_CIFS_HDR_SIZE bytes is enough for most SMB responses and + almost all handle based requests (but not write response, nor is it + sufficient for path based requests). A smaller size would have + been more efficient (compacting multiple slab items on one 4k page) + for the case in which debug was on, but this larger size allows + more SMBs to use small buffer alloc and is still much more + efficient to alloc 1 per page off the slab compared to 17K (5page) + alloc of large cifs buffers even when page debugging is on */ + cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq", + MAX_CIFS_HDR_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (cifs_sm_req_cachep == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + return -ENOMEM; + } + + if(cifs_min_small < 2) + cifs_min_small = 2; + else if (cifs_min_small > 256) { + cifs_min_small = 256; + cFYI(1,("cifs_min_small set to maximum (256)")); + } + + cifs_sm_req_poolp = mempool_create(cifs_min_small, + mempool_alloc_slab, + mempool_free_slab, + cifs_sm_req_cachep); + + if(cifs_sm_req_poolp == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + kmem_cache_destroy(cifs_sm_req_cachep); + return -ENOMEM; + } return 0; } @@ -609,6 +710,10 @@ cifs_destroy_request_bufs(void) if (kmem_cache_destroy(cifs_req_cachep)) printk(KERN_WARNING "cifs_destroy_request_cache: error not all structures were freed\n"); + mempool_destroy(cifs_sm_req_poolp); + if (kmem_cache_destroy(cifs_sm_req_cachep)) + printk(KERN_WARNING + "cifs_destroy_request_cache: cifs_small_rq free error\n"); } static int @@ -751,6 +856,14 @@ init_cifs(void) GlobalSMBSeslock = RW_LOCK_UNLOCKED; GlobalMid_Lock = SPIN_LOCK_UNLOCKED; + if(cifs_max_pending < 2) { + cifs_max_pending = 2; + cFYI(1,("cifs_max_pending set to min of 2")); + } else if(cifs_max_pending > 256) { + cifs_max_pending = 256; + cFYI(1,("cifs_max_pending set to max of 256")); + } + rc = cifs_init_inodecache(); if (!rc) { rc = cifs_init_mids(); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index b1cbc68d09ad..9175b3849cb5 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -63,10 +63,10 @@ extern struct file_operations cifs_file_ops; extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_read(struct file *file, char *read_data, +extern ssize_t cifs_user_read(struct file *file, char __user *read_data, size_t read_size, loff_t * poffset); -extern ssize_t cifs_write(struct file *file, const char *write_data, - size_t write_size, loff_t * poffset); +extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, + size_t write_size, loff_t * poffset); extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, struct dentry *, int); extern int cifs_flush(struct file *); @@ -90,5 +90,5 @@ extern int cifs_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); -#define CIFS_VERSION "1.20" +#define CIFS_VERSION "1.28" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 150210c578a5..87e8b3d760bd 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -240,6 +240,20 @@ struct cifsLockInfo { /* * One of these for each open instance of a file */ +struct cifs_search_info { + loff_t index_of_last_entry; + __u16 entries_in_buffer; + __u16 info_level; + __u32 resume_key; + char * ntwrk_buf_start; + char * srch_entries_start; + char * presume_name; + unsigned int resume_name_len; + unsigned endOfSearch:1; + unsigned emptyDir:1; + unsigned unicode:1; +}; + struct cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ @@ -250,14 +264,12 @@ struct cifsFileInfo { /* lock scope id (0 if none) */ struct file * pfile; /* needed for writepage */ struct inode * pInode; /* needed for oplock break */ - unsigned endOfSearch:1; /* we have reached end of search */ unsigned closePend:1; /* file is marked to close */ - unsigned emptyDir:1; unsigned invalidHandle:1; /* file closed via session abend */ struct semaphore fh_sem; /* prevents reopen race after dead ses*/ - char * search_resume_name; - unsigned int resume_name_length; - __u32 resume_key; + char * search_resume_name; /* BB removeme BB */ + unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */ + struct cifs_search_info srch_inf; }; /* @@ -317,6 +329,8 @@ struct oplock_q_entry { #define MID_REQUEST_SUBMITTED 2 #define MID_RESPONSE_RECEIVED 4 #define MID_RETRY_NEEDED 8 /* session closed while this request out */ +#define MID_NO_RESP_NEEDED 0x10 +#define MID_SMALL_BUFFER 0x20 /* 112 byte response buffer instead of 4K */ /* ***************************************************************** @@ -399,6 +413,7 @@ GLOBAL_EXTERN atomic_t tconInfoReconnectCount; /* Various Debug counters to remove someday (BB) */ GLOBAL_EXTERN atomic_t bufAllocCount; +GLOBAL_EXTERN atomic_t smBufAllocCount; GLOBAL_EXTERN atomic_t midCount; /* Misc globals */ @@ -407,11 +422,15 @@ GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions have the uid/password or Kerberos credential or equivalent for current user */ GLOBAL_EXTERN unsigned int oplockEnabled; -GLOBAL_EXTERN unsigned int quotaEnabled; +GLOBAL_EXTERN unsigned int experimEnabled; GLOBAL_EXTERN unsigned int lookupCacheEnabled; GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent with more secure ntlmssp2 challenge/resp */ GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */ GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ -GLOBAL_EXTERN unsigned int linuxExtEnabled; /* enable Linux/Unix CIFS extensions */ +GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ +GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */ +GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ +GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ +GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 20430a2d2016..ae31f666ec53 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -28,27 +28,30 @@ #define BAD_PROT CIFS_PROT+1 /* SMB command codes */ -#define SMB_COM_CREATE_DIRECTORY 0x00 -#define SMB_COM_DELETE_DIRECTORY 0x01 -#define SMB_COM_CLOSE 0x04 -#define SMB_COM_DELETE 0x06 -#define SMB_COM_RENAME 0x07 -#define SMB_COM_LOCKING_ANDX 0x24 -#define SMB_COM_COPY 0x29 +/* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + (ie which include no useful data other than the SMB error code itself). + Knowing this helps avoid response buffer allocations and copy in some cases */ +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_DELETE 0x06 /* trivial response */ +#define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ +#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_TRANSACTION2 0x32 #define SMB_COM_TRANSACTION2_SECONDARY 0x33 -#define SMB_COM_FIND_CLOSE2 0x34 -#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ #define SMB_COM_NEGOTIATE 0x72 #define SMB_COM_SESSION_SETUP_ANDX 0x73 -#define SMB_COM_LOGOFF_ANDX 0x74 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ #define SMB_COM_TREE_CONNECT_ANDX 0x75 #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 -#define SMB_COM_NT_RENAME 0xA5 +#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ /* Transact2 subcommand codes */ #define TRANS2_OPEN 0x00 @@ -141,7 +144,7 @@ #define SMBFLG2_KNOWS_EAS cpu_to_le16(2) #define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) #define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x80) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) #define SMBFLG2_DFS cpu_to_le16(0x1000) #define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) #define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) @@ -682,7 +685,7 @@ typedef struct smb_com_write_req { __le32 OffsetHigh; __le16 ByteCount; __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ - char Data[1]; + char Data[0]; } WRITE_REQ; typedef struct smb_com_write_rsp { @@ -972,10 +975,10 @@ typedef struct smb_com_transaction_change_notify_rsp { /* response contains array of the following structures */ struct file_notify_information { - __u32 NextEntryOffset; - __u32 Action; - __u32 FileNameLength; - __u8 FileName[1]; + __le32 NextEntryOffset; + __le32 Action; + __le32 FileNameLength; + __u8 FileName[0]; }; struct reparse_data { @@ -1004,45 +1007,55 @@ struct cifs_quota_data { #define QUOTA_LIST_START 0x100 #define QUOTA_FOR_SID 0x101 -typedef union smb_com_transaction2 { - struct { - struct smb_hdr hdr; /* wct = 14+ */ - __u16 TotalParameterCount; - __u16 TotalDataCount; - __u16 MaxParameterCount; - __u16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __u16 Flags; - __u32 Timeout; - __u16 Reserved2; - __u16 ParameterCount; - __u16 ParameterOffset; - __u16 DataCount; - __u16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __u16 SubCommand; /* 1st setup word - can be followed by SetupCount words */ - __u16 ByteCount; /* careful - setupcount is not always one */ - } req; - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 TotalParameterCount; - __u16 TotalDataCount; - __u16 Reserved; - __u16 ParameterCount; - __u16 ParamterOffset; - __u16 ParameterDisplacement; - __u16 DataCount; - __u16 DataOffset; - __u16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ - /* data area follows */ - } resp; -} TRANSACTION2; +struct trans2_req { + /* struct smb_hdr hdr precedes. Set wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* 1st setup word - SetupCount words follow */ + __le16 ByteCount; +}; + +struct smb_t2_req { + struct smb_hdr hdr; + struct trans2_req t2_req; +}; + +struct trans2_resp { + /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* SetupWords[SetupCount]; + __u16 ByteCount; + __u16 Reserved2;*/ + /* data area follows */ +}; + +struct smb_t2_rsp { + struct smb_hdr hdr; + struct trans2_resp t2_rsp; +}; /* PathInfo/FileInfo infolevels */ #define SMB_INFO_STANDARD 1 @@ -1063,6 +1076,15 @@ typedef union smb_com_transaction2 { #define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B #define SMB_QUERY_FILE_UNIX_BASIC 0x200 #define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + #define SMB_SET_FILE_BASIC_INFO 0x101 #define SMB_SET_FILE_DISPOSITION_INFO 0x102 @@ -1071,8 +1093,10 @@ typedef union smb_com_transaction2 { #define SMB_SET_FILE_UNIX_BASIC 0x200 #define SMB_SET_FILE_UNIX_LINK 0x201 #define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 #define SMB_SET_FILE_BASIC_INFO2 0x3ec -#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo level too */ #define SMB_FILE_ALL_INFO2 0x3fa #define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb #define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc @@ -1086,6 +1110,8 @@ typedef union smb_com_transaction2 { #define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 #define SMB_FIND_FILE_NAMES_INFO 0x103 #define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 #define SMB_FIND_FILE_UNIX 0x202 typedef struct smb_com_transaction2_qpi_req { @@ -1115,17 +1141,7 @@ typedef struct smb_com_transaction2_qpi_req { typedef struct smb_com_transaction2_qpi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ } TRANSACTION2_QPI_RSP; @@ -1158,17 +1174,7 @@ typedef struct smb_com_transaction2_spi_req { typedef struct smb_com_transaction2_spi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ } TRANSACTION2_SPI_RSP; @@ -1208,17 +1214,7 @@ struct smb_com_transaction2_sfi_req { struct smb_com_transaction2_sfi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ }; @@ -1268,17 +1264,7 @@ typedef struct smb_com_transaction2_ffirst_req { typedef struct smb_com_transaction2_ffirst_rsp { struct smb_hdr hdr; /* wct = 10 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; } TRANSACTION2_FFIRST_RSP; @@ -1320,17 +1306,7 @@ typedef struct smb_com_transaction2_fnext_req { typedef struct smb_com_transaction2_fnext_rsp { struct smb_hdr hdr; /* wct = 10 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; } TRANSACTION2_FNEXT_RSP; @@ -1351,6 +1327,8 @@ typedef struct smb_com_transaction2_fnext_rsp_parms { #define SMB_QUERY_CIFS_UNIX_INFO 0x200 #define SMB_QUERY_LABEL_INFO 0x3ea #define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 typedef struct smb_com_transaction2_qfsi_req { struct smb_hdr hdr; /* wct = 14+ */ @@ -1377,17 +1355,7 @@ typedef struct smb_com_transaction2_qfsi_req { typedef struct smb_com_transaction_qfsi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* should be zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; __u8 Pad; /* may be three bytes *//* followed by data area */ } TRANSACTION2_QFSI_RSP; @@ -1430,17 +1398,7 @@ typedef struct dfs_referral_level_3 { typedef struct smb_com_transaction_get_dfs_refer_rsp { struct smb_hdr hdr; /* wct = 10 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; /* zero setup words following */ + struct trans2_resp t2; __u16 ByteCount; __u8 Pad; __le16 PathConsumed; @@ -1545,6 +1503,7 @@ typedef struct { /* Linux/Unix extensions capability flags */ #define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ #define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 +#define CIFS_UNIX_XATTR_CAP 0x00000004 /*support for new namespace*/ /* DeviceType Flags */ #define FILE_DEVICE_CD_ROM 0x00000002 @@ -1575,10 +1534,13 @@ typedef struct { __le32 Attributes; __le32 MaxPathNameComponentLength; __le32 FileSystemNameLen; - char FileSystemName[52]; /* do not really need to save this - so potentially get only subset of name */ + char FileSystemName[52]; /* do not really need to save this - so potentially get only subset of name */ } FILE_SYSTEM_ATTRIBUTE_INFO; -typedef struct { /* data block encoding of response to level 263 QPathInfo */ +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +typedef struct { /* data block encoding of response to level 263 QPathInfo */ __le64 CreationTime; __le64 LastAccessTime; __le64 LastWriteTime; @@ -1600,12 +1562,20 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */ __le32 AlignmentRequirement; __le32 FileNameLength; char FileName[1]; -} FILE_ALL_INFO; /* level 263 QPathInfo */ +} FILE_ALL_INFO; /* level 0x107 QPathInfo */ +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 typedef struct { __le64 EndOfFile; __le64 NumOfBytes; - __le64 LastStatusChange; /*SNIA spec says DCE time for the three time fields */ + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ __le64 LastAccessTime; __le64 LastModificationTime; __le64 Uid; @@ -1616,11 +1586,11 @@ typedef struct { __u64 UniqueId; __le64 Permissions; __le64 Nlinks; -} FILE_UNIX_BASIC_INFO; /* level 512 QPathInfo */ +} FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ typedef struct { char LinkDest[1]; -} FILE_UNIX_LINK_INFO; /* level 513 QPathInfo */ +} FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ typedef struct { __u16 CreationDate; @@ -1635,21 +1605,99 @@ typedef struct { __u32 EASize; } FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ -/* defines for enumerating possible values of the Unix type field below */ -#define UNIX_FILE 0 -#define UNIX_DIR 1 -#define UNIX_SYMLINK 2 -#define UNIX_CHARDEV 3 -#define UNIX_BLOCKDEV 4 -#define UNIX_FIFO 5 -#define UNIX_SOCKET 6 +typedef struct { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} FILE_BASIC_INFO; /* size info, level 0x101 */ + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +}; /* size used on disk, level 0x103 for set, 0x105 for query */ + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +}; /* size info, level 0x104 for set, 0x106 for query */ + +struct file_alt_name_info { + __u8 alt_name[1]; +}; /* level 0x0108 */ + +struct file_stream_info { + __le32 number_of_streams; /* BB check sizes and verify location */ + /* followed by info on streams themselves + u64 size; + u64 allocation_size + stream info */ +}; /* level 0x109 */ + +struct file_compression_info { + __le64 compressed_size; + __le16 format; + __u8 unit_shift; + __u8 ch_shift; + __u8 cl_shift; + __u8 pad[3]; +}; /* level 0x10b */ + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +struct cifs_posix_ace { /* access control entry (ACE) */ + __u8 cifs_e_tag; + __u8 cifs_e_perm; + __le64 cifs_uid; /* or gid */ +}; + +struct cifs_posix_acl { /* access conrol list (ACL) */ + __le16 version; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[0]; + /* followed by + struct cifs_posix_ace default_ace_arraay[] */ +}; /* level 0x204 */ + +/* types of access control entries already defined in posix_acl.h */ +/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 +#define CIFS_POSIX_ACL_USER 0x02 +#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 +#define CIFS_POSIX_ACL_GROUP 0x08 +#define CIFS_POSIX_ACL_MASK 0x10 +#define CIFS_POSIX_ACL_OTHER 0x20 */ + +/* types of perms */ +/* #define CIFS_POSIX_ACL_EXECUTE 0x01 +#define CIFS_POSIX_ACL_WRITE 0x02 +#define CIFS_POSIX_ACL_READ 0x04 */ + +/* end of POSIX ACL definitions */ + +struct file_internal_info { + __u64 UniqueId; /* inode number */ +}; /* level 0x3ee */ +struct file_mode_info { + __le32 Mode; +}; /* level 0x3f8 */ + +struct file_attrib_tag { + __le32 Attribute; + __le32 ReparseTag; +}; /* level 0x40b */ + + +/********************************************************/ +/* FindFirst/FindNext transact2 data buffer formats */ +/********************************************************/ typedef struct { __le32 NextEntryOffset; - __le32 ResumeKey; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ __le64 EndOfFile; __le64 NumOfBytes; - __le64 LastStatusChange; /*SNIA spec says DCE time for the three time fields */ + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ __le64 LastAccessTime; __le64 LastModificationTime; __le64 Uid; @@ -1657,28 +1705,40 @@ typedef struct { __le32 Type; __le64 DevMajor; __le64 DevMinor; - __le64 UniqueId; + __u64 UniqueId; __le64 Permissions; __le64 Nlinks; char FileName[1]; -} FILE_UNIX_INFO; +} FILE_UNIX_INFO; /* level 0x202 */ typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; __le64 CreationTime; __le64 LastAccessTime; __le64 LastWriteTime; __le64 ChangeTime; - __le32 Attributes; - __u32 Pad; -} FILE_BASIC_INFO; /* size info, level 0x101 */ - -struct file_allocation_info { + __le64 EndOfFile; __le64 AllocationSize; -}; /* size info, level 0x103 */ + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} FILE_DIRECTORY_INFO; /* level 0x101 FF response data area */ -struct file_end_of_file_info { - __le64 FileSize; /* offset to end of file */ -}; /* size info, level 0x104 */ +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + char FileName[1]; +} FILE_FULL_DIRECTORY_INFO; /* level 0x102 FF response data area */ typedef struct { __le32 NextEntryOffset; @@ -1691,8 +1751,30 @@ typedef struct { __le64 AllocationSize; __le32 ExtFileAttributes; __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ char FileName[1]; -} FILE_DIRECTORY_INFO; /* level 257 FF response data area */ +} SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF response data area */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[12]; + char FileName[1]; +} FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FF response data area */ + struct gea { unsigned char name_len; @@ -1830,6 +1912,15 @@ struct xsymlink { char path[1024]; }; +typedef struct { + /* BB do we need another field for flags? BB */ + __u32 xattr_name_len; + __u32 xattr_value_len; + char xattr_name[0]; + /* followed by xattr_value[xattr_value_len], no pad */ +} FILE_XATTR_INFO; /* extended attribute, info level 205 */ + + #endif #pragma pack() /* resume default structure packing */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 572b6a40496d..308ed2a4a4e0 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifsproto.h * - * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) International Business Machines Corp., 2002,2004 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -32,6 +32,8 @@ struct statfs; extern struct smb_hdr *cifs_buf_get(void); extern void cifs_buf_release(void *); +extern struct smb_hdr *cifs_small_buf_get(void); +extern void cifs_small_buf_release(void *); extern int smb_send(struct socket *, struct smb_hdr *, unsigned int /* length */ , struct sockaddr *); extern unsigned int _GetXid(void); @@ -190,7 +192,8 @@ extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 lseek, unsigned int *nbytes, - const char *buf, const int long_op); + const char *buf, const char __user *ubuf, + const int long_op); extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 netfid, const __u64 len, const __u64 offset, const __u32 numUnlock, @@ -233,4 +236,12 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName, const char * ea_name, const void * ea_value, const __u16 ea_value_len, const struct nls_table *nls_codepage); +extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen,const int acl_type, + const struct nls_table *nls_codepage); +extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, const int acl_type, + const struct nls_table *nls_codepage); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 8e17185383f0..81a6c9ebda6a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -30,6 +30,7 @@ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/vfs.h> +#include <linux/posix_acl_xattr.h> #include <asm/uaccess.h> #include "cifspdu.h" #include "cifsglob.h" @@ -78,6 +79,97 @@ static void mark_open_files_invalid(struct cifsTconInfo * pTcon) } static int +small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, + void **request_buf /* returned */) +{ + int rc = 0; + + /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so + check for tcp and smb session status done differently + for those three - in the calling routine */ + if(tcon) { + if((tcon->ses) && (tcon->ses->server)){ + struct nls_table *nls_codepage; + /* Give Demultiplex thread up to 10 seconds to + reconnect, should be greater than cifs socket + timeout which is 7 seconds */ + while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { + wait_event_interruptible_timeout(tcon->ses->server->response_q, + (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); + if(tcon->ses->server->tcpStatus == CifsNeedReconnect) { + /* on "soft" mounts we wait once */ + if((tcon->retry == FALSE) || + (tcon->ses->status == CifsExiting)) { + cFYI(1,("gave up waiting on reconnect in smb_init")); + return -EHOSTDOWN; + } /* else "hard" mount - keep retrying until + process is killed or server comes back up */ + } else /* TCP session is reestablished now */ + break; + + } + + nls_codepage = load_nls_default(); + /* need to prevent multiple threads trying to + simultaneously reconnect the same SMB session */ + down(&tcon->ses->sesSem); + if(tcon->ses->status == CifsNeedReconnect) + rc = cifs_setup_session(0, tcon->ses, nls_codepage); + if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { + mark_open_files_invalid(tcon); + rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, + nls_codepage); + up(&tcon->ses->sesSem); + if(rc == 0) + atomic_inc(&tconInfoReconnectCount); + + cFYI(1, ("reconnect tcon rc = %d", rc)); + /* Removed call to reopen open files here - + it is safer (and faster) to reopen files + one at a time as needed in read and write */ + + /* Check if handle based operation so we + know whether we can continue or not without + returning to caller to reset file handle */ + switch(smb_command) { + case SMB_COM_READ_ANDX: + case SMB_COM_WRITE_ANDX: + case SMB_COM_CLOSE: + case SMB_COM_FIND_CLOSE2: + case SMB_COM_LOCKING_ANDX: { + unload_nls(nls_codepage); + return -EAGAIN; + } + } + } else { + up(&tcon->ses->sesSem); + } + unload_nls(nls_codepage); + + } else { + return -EIO; + } + } + if(rc) + return rc; + + *request_buf = cifs_small_buf_get(); + if (*request_buf == 0) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct); + +#ifdef CONFIG_CIFS_STATS + if(tcon != NULL) { + atomic_inc(&tcon->num_smbs_sent); + } +#endif /* CONFIG_CIFS_STATS */ + return rc; +} + +static int smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf /* returned */ , void **response_buf /* returned */ ) @@ -171,10 +263,42 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, if(tcon != NULL) { atomic_inc(&tcon->num_smbs_sent); } -#endif +#endif /* CONFIG_CIFS_STATS */ return rc; } +static int validate_t2(struct smb_t2_rsp * pSMB) +{ + int rc = -EINVAL; + int total_size; + char * pBCC; + + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if(pSMB->hdr.WordCount >= 10) { + if((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && + (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { + /* check that bcc is at least as big as parms + data */ + /* check that bcc is less than negotiated smb buffer */ + total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); + if(total_size < 512) { + total_size+=le16_to_cpu(pSMB->t2_rsp.DataCount); + /* BCC le converted in SendReceive */ + pBCC = (pSMB->hdr.WordCount * 2) + sizeof(struct smb_hdr) + + (char *)pSMB; + if((total_size <= (*(u16 *)pBCC)) && + (total_size < + CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { + return 0; + } + + } + } + } + cifs_dump_mem("Invalid transact2 SMB: ",(char *)pSMB, + sizeof(struct smb_t2_rsp) + 16); + return rc; +} int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) { @@ -217,7 +341,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) /* probably no need to store and check maxvcs */ server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), - (__u32) CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE); + (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); cFYI(0, ("Max buf = %d ", ses->server->maxBuf)); GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); @@ -265,6 +389,12 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) SecurityBlob, count - 16, &server->secType); + if(rc == 1) { + /* BB Need to fill struct for sessetup here */ + rc = -EOPNOTSUPP; + } else { + rc = -EINVAL; + } } } else server->capabilities &= ~CAP_EXTENDED_SECURITY; @@ -288,7 +418,7 @@ int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) { struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; + struct smb_hdr *smb_buffer_response; /* BB removeme BB */ int rc = 0; int length; @@ -322,20 +452,20 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) up(&tcon->tconSem); return -EIO; } - - rc = smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, - (void **) &smb_buffer, (void **) &smb_buffer_response); + rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **)&smb_buffer); if (rc) { up(&tcon->tconSem); return rc; - } + } else { + smb_buffer_response = smb_buffer; /* BB removeme BB */ + } rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response, &length, 0); if (rc) cFYI(1, (" Tree disconnect failed %d", rc)); if (smb_buffer) - cifs_buf_release(smb_buffer); + cifs_small_buf_release(smb_buffer); up(&tcon->tconSem); /* No need to return error on this operation if tid invalidated and @@ -365,9 +495,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) up(&ses->sesSem); return -EBUSY; } - - rc = smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL /* no tcon anymore */, - (void **) &pSMB, (void **) &smb_buffer_response); + rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); + smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ if(ses->server) { if(ses->server->secMode & @@ -395,7 +524,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) } } if (pSMB) - cifs_buf_release(pSMB); + cifs_small_buf_release(pSMB); up(&ses->sesSem); /* if session dead then we do not need to do ulogoff, @@ -424,13 +553,13 @@ DelFileRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->fileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->fileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->fileName, fileName, name_len); } @@ -476,13 +605,13 @@ RmDirRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, dirName, 530 + name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, dirName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(dirName, 530); + name_len = strnlen(dirName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->DirName, dirName, name_len); } @@ -526,13 +655,13 @@ MkDirRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, name, 530 + name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, name, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(name, 530); + name_len = strnlen(name, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->DirName, name, name_len); } @@ -583,7 +712,7 @@ openRetry: count = 1; /* account for one byte pad to word boundary */ name_len = cifs_strtoUCS((wchar_t *) (pSMB->fileName + 1), - fileName, 530 + fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ @@ -591,7 +720,7 @@ openRetry: pSMB->NameLength = cpu_to_le16(name_len); } else { /* BB improve the check for buffer overruns BB */ count = 0; /* no pad */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ pSMB->NameLength = cpu_to_le16(name_len); strncpy(pSMB->fileName, fileName, name_len); @@ -700,7 +829,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, __u16 data_length = le16_to_cpu(pSMBr->DataLength); *nbytes = data_length; /*check that DataLength would not go beyond end of SMB */ - if ((data_length > CIFS_MAX_MSGSIZE) + if ((data_length > CIFSMaxBufSize) || (data_length > count)) { cFYI(1,("bad length %d for count %d",data_length,count)); rc = -EIO; @@ -733,7 +862,7 @@ int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, const char *buf, - const int long_op) + const char __user * ubuf, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; @@ -754,15 +883,29 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; pSMB->Remaining = 0; + /* BB can relax this if buffer is big enough in some cases - ie we can + send more if LARGE_WRITE_X capability returned by the server and if + our buffer is big enough or if we convert to iovecs on socket writes + and eliminate the copy to the CIFS buffer */ bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF; if (bytes_sent > count) bytes_sent = count; pSMB->DataLengthHigh = 0; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - - memcpy(pSMB->Data,buf,bytes_sent); + if(buf) + memcpy(pSMB->Data,buf,bytes_sent); + else if(ubuf) + copy_from_user(pSMB->Data,ubuf,bytes_sent); + else { + /* No buffer */ + if(pSMB) + cifs_buf_release(pSMB); + return -EINVAL; + } byte_count = bytes_sent + 1 /* pad */ ; pSMB->DataLengthLow = cpu_to_le16(bytes_sent); @@ -787,6 +930,66 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, return rc; } +#ifdef CONFIG_CIFS_EXPERIMENTAL +int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, + const int netfid, const unsigned int count, + const __u64 offset, unsigned int *nbytes, const char __user *buf, + const int long_op) +{ + int rc = -EACCES; + WRITE_REQ *pSMB = NULL; + WRITE_RSP *pSMBr = NULL; + /*int bytes_returned;*/ + unsigned bytes_sent; + __u16 byte_count; + + rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); + pSMBr = (WRITE_RSP *)pSMB; /* BB removeme BB */ + + if (rc) + return rc; + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; + pSMB->Remaining = 0; + bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF; + if (bytes_sent > count) + bytes_sent = count; + pSMB->DataLengthHigh = 0; + pSMB->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); + + byte_count = bytes_sent + 1 /* pad */ ; + pSMB->DataLengthLow = cpu_to_le16(bytes_sent); + pSMB->DataLengthHigh = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + +/* rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, buf, buflen, &bytes_returned, long_op); */ /* BB fixme BB */ + if (rc) { + cFYI(1, ("Send error in write2 (large write) = %d", rc)); + *nbytes = 0; + } else + *nbytes = le16_to_cpu(pSMBr->Count); + + if (pSMB) + cifs_small_buf_release(pSMB); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} +#endif /* CIFS_EXPERIMENTAL */ + int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const __u64 len, @@ -861,8 +1064,8 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) cFYI(1, ("In CIFSSMBClose")); /* do not retry on dead session on close */ - rc = smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); + pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ if(rc == -EAGAIN) return 0; if (rc) @@ -879,8 +1082,9 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) cERROR(1, ("Send error in Close = %d", rc)); } } + if (pSMB) - cifs_buf_release(pSMB); + cifs_small_buf_release(pSMB); /* Since session is dead, file will be closed on server already */ if(rc == -EAGAIN) @@ -915,7 +1119,7 @@ renameRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530 + cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ @@ -925,15 +1129,15 @@ renameRetry: pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = cifs_strtoUCS((wchar_t *) & pSMB-> - OldFileName[name_len + 2], toName, 530, + OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fromName, 530); + name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); - name_len2 = strnlen(toName, 530); + name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); @@ -1014,7 +1218,7 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, sprintf(dummy_string,"cifs%x",pSMB->hdr.Mid); len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, dummy_string, 24, nls_codepage); } else { - len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, target_name, 530, nls_codepage); + len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, target_name, PATH_MAX, nls_codepage); } rename_info->target_name_len = cpu_to_le32(2 * len_of_str); count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2; @@ -1073,7 +1277,7 @@ copyRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, - 530 /* find define for this maxpathcomponent */, + PATH_MAX /* find define for this maxpathcomponent */, nls_codepage); name_len++; /* trailing null */ name_len *= 2; @@ -1081,15 +1285,15 @@ copyRetry: /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = cifs_strtoUCS((wchar_t *) & pSMB-> - OldFileName[name_len + 2], toName, 530, + OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fromName, 530); + name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); - name_len2 = strnlen(toName, 530); + name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); @@ -1139,14 +1343,14 @@ createSymLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fromName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fromName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fromName, 530); + name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fromName, name_len); } @@ -1163,13 +1367,13 @@ createSymLinkRetry: data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = - cifs_strtoUCS((wchar_t *) data_offset, toName, 530 + cifs_strtoUCS((wchar_t *) data_offset, toName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len_target = strnlen(toName, 530); + name_len_target = strnlen(toName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, toName, name_len_target); } @@ -1230,14 +1434,14 @@ createHardLinkRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifs_strtoUCS((wchar_t *) pSMB->FileName, toName, 530 + name_len = cifs_strtoUCS((wchar_t *) pSMB->FileName, toName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(toName, 530); + name_len = strnlen(toName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, toName, name_len); } @@ -1254,13 +1458,13 @@ createHardLinkRetry: data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = - cifs_strtoUCS((wchar_t *) data_offset, fromName, 530 + cifs_strtoUCS((wchar_t *) data_offset, fromName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len_target = strnlen(fromName, 530); + name_len_target = strnlen(fromName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, fromName, name_len_target); } @@ -1326,7 +1530,7 @@ winCreateHardLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530 + cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ @@ -1335,15 +1539,15 @@ winCreateHardLinkRetry: pSMB->OldFileName[name_len + 1] = 0x04; name_len2 = cifs_strtoUCS((wchar_t *) & pSMB-> - OldFileName[name_len + 2], toName, 530, + OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fromName, 530); + name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); - name_len2 = strnlen(toName, 530); + name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); @@ -1392,13 +1596,13 @@ querySymLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -1432,13 +1636,17 @@ querySymLinkRetry: (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc)); - } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - __u16 count = le16_to_cpu(pSMBr->DataCount); - if ((pSMBr->ByteCount < 2) || (data_offset > 512)) + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = UniStrnlen((wchar_t *) ((char *) &pSMBr->hdr.Protocol +data_offset), @@ -1550,6 +1758,306 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, return rc; } +#ifdef CONFIG_CIFS_POSIX + +/*Convert an Access Control Entry from wire format to local POSIX xattr format*/ +static void cifs_convert_ace(posix_acl_xattr_entry * ace, struct cifs_posix_ace * cifs_ace) +{ + /* u8 cifs fields do not need le conversion */ + ace->e_perm = (__u16)cifs_ace->cifs_e_perm; + ace->e_tag = (__u16)cifs_ace->cifs_e_tag; + ace->e_id = (__u32)le64_to_cpu(cifs_ace->cifs_uid); + /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */ + + return; +} + +/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ +static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen,const int acl_type,const int size_of_data_area) +{ + int size = 0; + int i; + __u16 count; + struct cifs_posix_ace * pACE; + struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)src; + posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)trgt; + + if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) + return -EOPNOTSUPP; + + if(acl_type & ACL_TYPE_ACCESS) { + count = le16_to_cpu(cifs_acl->access_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if(size_of_data_area < size) { + cFYI(1,("bad CIFS POSIX ACL size %d vs. %d",size_of_data_area,size)); + return -EINVAL; + } + } else if(acl_type & ACL_TYPE_DEFAULT) { + count = le16_to_cpu(cifs_acl->access_entry_count); + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; +/* skip past access ACEs to get to default ACEs */ + pACE = &cifs_acl->ace_array[count]; + count = le16_to_cpu(cifs_acl->default_entry_count); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if(size_of_data_area < size) + return -EINVAL; + } else { + /* illegal type */ + return -EINVAL; + } + + size = posix_acl_xattr_size(count); + if((buflen == 0) || (local_acl == NULL)) { + /* used to query ACL EA size */ + } else if(size > buflen) { + return -ERANGE; + } else /* buffer big enough */ { + local_acl->a_version = POSIX_ACL_XATTR_VERSION; + for(i = 0;i < count ;i++) { + cifs_convert_ace(&local_acl->a_entries[i],pACE); + pACE ++; + } + } + return size; +} + +__u16 convert_ace_to_cifs_ace(struct cifs_posix_ace * cifs_ace, + const posix_acl_xattr_entry * local_ace) +{ + __u16 rc = 0; /* 0 = ACL converted ok */ + + cifs_ace->cifs_e_perm = (__u8)cpu_to_le16(local_ace->e_perm); + cifs_ace->cifs_e_tag = (__u8)cpu_to_le16(local_ace->e_tag); + /* BB is there a better way to handle the large uid? */ + if(local_ace->e_id == -1) { + /* Probably no need to le convert -1 on any arch but can not hurt */ + cifs_ace->cifs_uid = cpu_to_le64(-1); + } else + cifs_ace->cifs_uid = (__u64)cpu_to_le32(local_ace->e_id); + /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/ + return rc; +} + +/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ +__u16 ACL_to_cifs_posix(char * parm_data,const char * pACL,const int buflen, + const int acl_type) +{ + __u16 rc = 0; + struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)parm_data; + posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)pACL; + int count; + int i; + + if((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) + return 0; + + count = posix_acl_xattr_count((size_t)buflen); + cFYI(1,("setting acl with %d entries from buf of length %d and version of %d", + count,buflen,local_acl->a_version)); + if(local_acl->a_version != 2) { + cFYI(1,("unknown POSIX ACL version %d",local_acl->a_version)); + return 0; + } + cifs_acl->version = cpu_to_le16(1); + if(acl_type == ACL_TYPE_ACCESS) + cifs_acl->access_entry_count = count; + else if(acl_type == ACL_TYPE_DEFAULT) + cifs_acl->default_entry_count = count; + else { + cFYI(1,("unknown ACL type %d",acl_type)); + return 0; + } + for(i=0;i<count;i++) { + rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], + &local_acl->a_entries[i]); + if(rc != 0) { + /* ACE not converted */ + break; + } + } + if(rc == 0) { + rc = (__u16)(count * sizeof(struct cifs_posix_ace)); + rc += sizeof(struct cifs_posix_acl); + /* BB add check to make sure ACL does not overflow SMB */ + } + return rc; +} + +int +CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage) +{ +/* SMB_QUERY_POSIX_ACL */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + + cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName)); + +queryAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX + /* BB fixme find define for this maxpathcomponent */ + , nls_codepage); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->FileName[name_len] = 0; + pSMB->FileName[name_len+1] = 0; + } else { /* BB improve the check for buffer overruns BB */ + name_len = strnlen(searchName, PATH_MAX /* BB fixme */); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, searchName, name_len); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max data count below from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); + pSMB->Reserved4 = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc || (pSMBr->ByteCount < 2)) + /* BB also check enough total bytes returned */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + rc = cifs_copy_posix_acl(acl_inf, + (char *)&pSMBr->hdr.Protocol+data_offset, + buflen,acl_type,count); + } + } + if (pSMB) + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto queryAclRetry; + return rc; +} + +int +CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, const int acl_type, + const struct nls_table *nls_codepage) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + char *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count, data_count, param_offset, offset; + + cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName)); +setAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX + /* BB fixme find define for this maxpathcomponent */ + , nls_codepage); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve the check for buffer overruns BB */ + name_len = strnlen(fileName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, fileName, name_len); + } + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + parm_data = ((char *) &pSMB->hdr.Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + + /* convert to on the wire format for POSIX ACL */ + data_count = ACL_to_cifs_posix(parm_data,local_acl,buflen,acl_type); + + if(data_count == 0) { + rc = -EOPNOTSUPP; + goto setACLerrorExit; + } + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); + byte_count = 3 /* pad */ + params + data_count; + pSMB->DataCount = cpu_to_le16(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Set POSIX ACL returned %d", rc)); + } + +setACLerrorExit: + if (pSMB) + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto setAclRetry; + return rc; +} + +#endif + int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, @@ -1564,7 +2072,7 @@ CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, int name_len; __u16 params, byte_count; - cFYI(1, ("In QPathInfo path %s", searchName)); +/* cFYI(1, ("In QPathInfo path %s", searchName)); */ /* BB fixme BB */ QPathInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); @@ -1573,13 +2081,13 @@ QPathInfoRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -1613,13 +2121,12 @@ QPathInfoRetry: if (rc) { cFYI(1, ("Send error in QPathInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - /* BB also check enough total bytes returned */ - /* BB we need to improve the validity checking - of these trans2 responses */ - if ((pSMBr->ByteCount < 40) || (data_offset > 512)) + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 40)) rc = -EIO; /* bad smb */ else if (pFindData){ + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, sizeof (FILE_ALL_INFO)); @@ -1657,13 +2164,13 @@ UnixQPathInfoRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -1698,15 +2205,12 @@ UnixQPathInfoRetry: if (rc) { cFYI(1, ("Send error in QPathInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - /* BB also check if enough total bytes returned */ - if ((pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO)) || - (data_offset > 512) || - (data_offset < sizeof(struct smb_hdr))) { - cFYI(1,("UnixQPathinfo invalid data offset %d bytes returned %d", - (int)data_offset,bytes_returned)); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) { rc = -EIO; /* bad smb */ } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, @@ -1721,6 +2225,7 @@ UnixQPathInfoRetry: return rc; } +#ifdef CONFIG_CIFS_EXPERIMENTAL /* function unused at present */ int CIFSFindSingle(const int xid, struct cifsTconInfo *tcon, const char *searchName, FILE_ALL_INFO * findData, @@ -1743,13 +2248,13 @@ findUniqueRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -1764,7 +2269,7 @@ findUniqueRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_ffirst_req,InformationLevel) - 4); + offsetof(struct smb_com_transaction2_ffirst_req,InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; /* one byte, no need to le convert */ @@ -1799,6 +2304,7 @@ findUniqueRetry: return rc; } +#endif /* CIFS_EXPERIMENTAL */ int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, @@ -1825,13 +2331,13 @@ findFirstRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -1849,8 +2355,8 @@ findFirstRetry: byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_ffirst_req, SearchAttributes) - 4); + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; /* one byte no need to make endian neutral */ @@ -1859,7 +2365,7 @@ findFirstRetry: pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); - pSMB->SearchCount = cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO)); /* should this be shrunk even more ? */ + pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_DIRECTORY_INFO)); /* should this be shrunk even more ? */ pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); /* test for Unix extensions */ @@ -1880,20 +2386,25 @@ findFirstRetry: if (rc) { /* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ cFYI(1, ("Error in FindFirst = %d", rc)); - } else { /* decode response */ + } else { + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if(!rc) { + /* decode response */ /* BB add safety checks for these memcpys */ - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - *pUnicodeFlag = TRUE; - else - *pUnicodeFlag = FALSE; - memcpy(findParms, - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->ParameterOffset), - sizeof (T2_FFIRST_RSP_PARMS)); - response_data = - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->DataOffset); - memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount)); + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + *pUnicodeFlag = TRUE; + else + *pUnicodeFlag = FALSE; + memcpy(findParms, + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset), + sizeof (T2_FFIRST_RSP_PARMS)); + response_data = + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + memcpy(findData, response_data, + le16_to_cpu(pSMBr->t2.DataCount)); + } } if (pSMB) cifs_buf_release(pSMB); @@ -1904,6 +2415,256 @@ findFirstRetry: return rc; } +/* xid, tcon, searchName and codepage are input parms, rest are returned */ +int +CIFSFindFirst2(const int xid, struct cifsTconInfo *tcon, + const char *searchName, + const struct nls_table *nls_codepage, + __u16 * pnetfid, + struct cifs_search_info * psrch_inf) +{ +/* level 257 SMB_ */ + TRANSACTION2_FFIRST_REQ *pSMB = NULL; + TRANSACTION2_FFIRST_RSP *pSMBr = NULL; + T2_FFIRST_RSP_PARMS * parms; + int rc = 0; + int bytes_returned = 0; + int name_len; + __u16 params, byte_count; + + cFYI(1, ("In FindFirst2")); + +findFirst2Retry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifs_strtoUCS((wchar_t *) pSMB->FileName,searchName, + PATH_MAX, nls_codepage); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->FileName[name_len] = 0; /* null terminate just in case */ + pSMB->FileName[name_len+1] = 0; + } else { /* BB add check for overrun of SMB buf BB */ + name_len = strnlen(searchName, PATH_MAX); + name_len++; /* trailing null */ +/* BB fix here and in unicode clause above ie + if(name_len > buffersize-header) + free buffer exit; BB */ + strncpy(pSMB->FileName, searchName, name_len); + pSMB->FileName[name_len] = 0; /* just in case */ + } + + params = 12 + name_len /* includes null */ ; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(10); + pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + pSMB->SearchCount= cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); + pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | + CIFS_SEARCH_RETURN_RESUME); + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + + /* BB what should we set StorageType to? Does it matter? BB */ + pSMB->SearchStorageType = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + + if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ + /* BB Add code to handle unsupported level rc */ + cFYI(1, ("Error in FindFirst = %d", rc)); + + if (pSMB) + cifs_buf_release(pSMB); + + /* BB eventually could optimize out free and realloc of buf */ + /* for this case */ + if (rc == -EAGAIN) + goto findFirst2Retry; + } else { /* decode response */ + /* BB remember to free buffer if error BB */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if(rc == 0) { + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = TRUE; + else + psrch_inf->unicode = FALSE; + + psrch_inf->ntwrk_buf_start = (char *)pSMBr; + psrch_inf->srch_entries_start = + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + + parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset)); + + if(parms->EndofSearch) + psrch_inf->endOfSearch = TRUE; + else + psrch_inf->endOfSearch = FALSE; + + psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry = + psrch_inf->entries_in_buffer; +/*cFYI(1,("entries in buf %d index_of_last %d",psrch_inf->entries_in_buffer,psrch_inf->index_of_last_entry)); */ + *pnetfid = parms->SearchHandle; + } else { + if(pSMB) + cifs_buf_release(pSMB); + } + } + + return rc; +} + +int CIFSFindNext2(const int xid, struct cifsTconInfo *tcon, + __u16 searchHandle, struct cifs_search_info * psrch_inf) +{ + TRANSACTION2_FNEXT_REQ *pSMB = NULL; + TRANSACTION2_FNEXT_RSP *pSMBr = NULL; + T2_FNEXT_RSP_PARMS * parms; + char *response_data; + int rc = 0; + int bytes_returned, name_len; + __u16 params, byte_count; + + cFYI(1, ("In FindNext2")); + + if(psrch_inf->endOfSearch == TRUE) + return -ENOENT; + + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 14; /* includes 2 bytes of null string, converted to LE below */ + byte_count = 0; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(8); + pSMB->MaxDataCount = + cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); + pSMB->SearchHandle = searchHandle; /* always kept as le */ + pSMB->SearchCount = + cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO)); + /* test for Unix extensions */ +/* if (tcon->ses->capabilities & CAP_UNIX) { + pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX); + psrch_inf->info_level = SMB_FIND_FILE_UNIX; + } else { + pSMB->InformationLevel = + cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO); + psrch_inf->info_level = SMB_FIND_FILE_DIRECTORY_INFO; + } */ + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + pSMB->ResumeKey = psrch_inf->resume_key; + pSMB->SearchFlags = + cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); + + name_len = psrch_inf->resume_name_len; + params += name_len; + if(name_len < PATH_MAX) { + memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); + byte_count += name_len; + } else { + rc = -EINVAL; + goto FNext2_err_exit; + } + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + + if (rc) { + if (rc == -EBADF) { + psrch_inf->endOfSearch = TRUE; + rc = 0; /* search probably was closed at end of search above */ + } else + cFYI(1, ("FindNext returned = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if(rc == 0) { + /* BB fixme add lock for file (srch_info) struct here */ + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = TRUE; + else + psrch_inf->unicode = FALSE; + response_data = (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset); + parms = (T2_FNEXT_RSP_PARMS *)response_data; + response_data = (char *)&pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + cifs_buf_release(psrch_inf->ntwrk_buf_start); + psrch_inf->srch_entries_start = response_data; + psrch_inf->ntwrk_buf_start = (char *)pSMB; + if(parms->EndofSearch) + psrch_inf->endOfSearch = TRUE; + else + psrch_inf->endOfSearch = FALSE; + + psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry += + psrch_inf->entries_in_buffer; +/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",psrch_inf->entries_in_buffer,psrch_inf->index_of_last_entry)); */ + + /* BB fixme add unlock here */ + } + + } + + /* BB On error, should we leave previous search buf (and count and + last entry fields) intact or free the previous one? */ + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ +FNext2_err_exit: + if ((rc != 0) && pSMB) + cifs_buf_release(pSMB); + + return rc; +} + int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms, @@ -1939,8 +2700,8 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_fnext_req,SearchHandle) - 4); + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -1949,7 +2710,7 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->SearchHandle = searchHandle; /* always kept as le */ findParms->SearchCount = 0; /* set to zero in case of error */ pSMB->SearchCount = - cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO)); + cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO)); /* test for Unix extensions */ if (tcon->ses->capabilities & CAP_UNIX) { pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX); @@ -1963,7 +2724,7 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); /* BB add check to make sure we do not cross end of smb */ - if(name_len < CIFS_MAX_MSGSIZE) { + if(name_len < PATH_MAX) { memcpy(pSMB->ResumeFileName, resume_file_name, name_len); byte_count += name_len; } @@ -1984,19 +2745,23 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon, else cFYI(1, ("FindNext returned = %d", rc)); } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB add safety checks for these memcpys */ - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - *pUnicodeFlag = TRUE; - else - *pUnicodeFlag = FALSE; - memcpy(findParms, - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->ParameterOffset), - sizeof (T2_FNEXT_RSP_PARMS)); - response_data = - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->DataOffset); - memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount)); + if(rc == 0) { + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + *pUnicodeFlag = TRUE; + else + *pUnicodeFlag = FALSE; + memcpy(findParms, + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset), + sizeof (T2_FNEXT_RSP_PARMS)); + response_data = + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + memcpy(findData,response_data,le16_to_cpu(pSMBr->t2.DataCount)); + } } if (pSMB) cifs_buf_release(pSMB); @@ -2012,12 +2777,12 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle { int rc = 0; FINDCLOSE_REQ *pSMB = NULL; - CLOSE_RSP *pSMBr = NULL; + CLOSE_RSP *pSMBr = NULL; /* BB removeme BB */ int bytes_returned; cFYI(1, ("In CIFSSMBFindClose")); - rc = smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); + pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ /* no sense returning error if session restarted file handle has been closed */ if(rc == -EAGAIN) @@ -2033,7 +2798,7 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle cERROR(1, ("Send error in FindClose = %d", rc)); } if (pSMB) - cifs_buf_release(pSMB); + cifs_small_buf_release(pSMB); /* Since session is dead, search handle closed on server already */ if (rc == -EAGAIN) @@ -2042,6 +2807,39 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle return rc; } +#ifdef CONFIG_CIFS_EXPERIMENTAL +int +CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + __u64 * inode_number, + const struct nls_table *nls_codepage) +{ + int rc = 0; + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + + cFYI(1,("In GetSrvInodeNumber for %s",searchName)); + if(tcon == NULL) + return -ENODEV; + + cFYI(1, ("In QPathInfo path %s", searchName)); +GetInodeNumberRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + +/* BB add missing code here */ + + if (pSMB) + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto GetInodeNumberRetry; + return rc; +} +#endif /* CIFS_EXPERIMENTAL */ + int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, @@ -2084,13 +2882,13 @@ getDFSRetry: pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; name_len = cifs_strtoUCS((wchar_t *) pSMB->RequestFileName, - searchName, 530 + searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->RequestFileName, searchName, name_len); } @@ -2124,14 +2922,17 @@ getDFSRetry: cFYI(1, ("Send error in GetDFSRefer = %d", rc)); } else { /* decode response */ /* BB Add logic to parse referrals here */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - __u16 data_count = le16_to_cpu(pSMBr->DataCount); - cFYI(1, - ("Decoding GetDFSRefer response. BCC: %d Offset %d", - pSMBr->ByteCount, data_offset)); - if ((pSMBr->ByteCount < 17) || (data_offset > 512)) /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 17)) /* BB also check enough total bytes returned */ + rc = -EIO; /* bad smb */ else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount); + + cFYI(1, + ("Decoding GetDFSRefer response. BCC: %d Offset %d", + pSMBr->ByteCount, data_offset)); referrals = (struct dfs_referral_level_3 *) (8 /* sizeof start of data block */ + @@ -2257,13 +3058,16 @@ QFSInfoRetry: if (rc) { cERROR(1, ("Send error in QFSInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - cFYI(1, - ("Decoding qfsinfo response. BCC: %d Offset %d", - pSMBr->ByteCount, data_offset)); - if ((pSMBr->ByteCount < 24) || (data_offset > 512)) /* BB also check enough total bytes returned */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 24)) /* BB alsO CHEck enough total bytes returned */ rc = -EIO; /* bad smb */ else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + cFYI(1, + ("Decoding qfsinfo response. BCC: %d Offset %d", + pSMBr->ByteCount, data_offset)); + response_data = (FILE_SYSTEM_INFO *) (((char *) &pSMBr->hdr.Protocol) + @@ -2339,10 +3143,12 @@ QFSAttributeRetry: if (rc) { cERROR(1, ("Send error in QFSAttributeInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - if ((pSMBr->ByteCount < 13) || (data_offset > 512)) { /* BB also check enough bytes returned */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 13)) { /* BB also check enough bytes returned */ rc = -EIO; /* bad smb */ } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_ATTRIBUTE_INFO *) (((char *) &pSMBr->hdr.Protocol) + @@ -2408,11 +3214,12 @@ QFSDeviceRetry: if (rc) { cFYI(1, ("Send error in QFSDeviceInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - if ((pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO)) - || (data_offset > 512)) + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO))) rc = -EIO; /* bad smb */ else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_DEVICE_INFO *) (((char *) &pSMBr->hdr.Protocol) + @@ -2477,10 +3284,12 @@ QFSUnixRetry: if (rc) { cERROR(1, ("Send error in QFSUnixInfo = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); - if ((pSMBr->ByteCount < 13) || (data_offset > 512)) { + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 13)) { rc = -EIO; /* bad smb */ } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_UNIX_INFO *) (((char *) &pSMBr->hdr.Protocol) + @@ -2526,13 +3335,13 @@ SetEOFRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } @@ -2705,13 +3514,13 @@ SetTimesRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } @@ -2785,13 +3594,13 @@ SetTimesRetryLegacy: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } @@ -2821,12 +3630,7 @@ SetTimesRetryLegacy: pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; - /* I doubt that passthrough levels apply to this old - preNT info level */ -/* if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); - else*/ - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; memcpy(data_offset, data, sizeof (FILE_INFO_STANDARD)); @@ -2868,13 +3672,13 @@ setPermsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } @@ -2894,6 +3698,7 @@ setPermsRetry: data_offset = (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol + offset); + memset(data_offset, 0, count); pSMB->DataOffset = cpu_to_le16(offset); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->SetupCount = 1; @@ -2980,7 +3785,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, -1); if (rc) { cFYI(1, ("Error in Notify = %d", rc)); } @@ -3016,13 +3821,13 @@ QAllEAsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -3056,11 +3861,12 @@ QAllEAsRetry: if (rc) { cFYI(1, ("Send error in QueryAllEAs = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ - if ((pSMBr->ByteCount < 4) || (data_offset > 512)) + if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, @@ -3072,6 +3878,7 @@ QAllEAsRetry: of list */ /* check that each element of each entry does not go beyond end of list */ + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); struct fealist * ea_response_data; rc = 0; /* validate_trans2_offsets() */ @@ -3158,13 +3965,13 @@ QEARetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(searchName, 530); + name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } @@ -3198,11 +4005,12 @@ QEARetry: if (rc) { cFYI(1, ("Send error in Query EA = %d", rc)); } else { /* decode response */ - __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ - if ((pSMBr->ByteCount < 4) || (data_offset > 512)) + if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, @@ -3214,8 +4022,9 @@ QEARetry: of list */ /* check that each element of each entry does not go beyond end of list */ + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); struct fealist * ea_response_data; - rc = -ENOENT; + rc = -ENODATA; /* validate_trans2_offsets() */ /* BB to check if(start of smb + data_offset > &bcc+ bcc)*/ ea_response_data = (struct fealist *) @@ -3303,13 +4112,13 @@ SetEARetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530 + cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ - name_len = strnlen(fileName, 530); + name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e548bb3cf2e3..24bd15572009 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -58,6 +58,7 @@ struct smb_vol { char *domainname; char *UNC; char *UNCip; + char *in6_addr; /* ipv6 address as human readable form of in6_addr */ char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[16]; /* netbios name of client */ uid_t linux_uid; @@ -69,6 +70,9 @@ struct smb_vol { unsigned intr:1; unsigned setuids:1; unsigned noperm:1; + unsigned no_psx_acl:1; /* set if posix acl support should be disabled */ + unsigned server_ino:1; /* use inode numbers from server ie UniqueId */ + unsigned direct_io:1; unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -213,7 +217,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) write_unlock(&GlobalSMBSeslock); if(length > 1) { mempool_resize(cifs_req_poolp, - length + CIFS_MIN_RCV_POOL, + length + cifs_min_rcv, GFP_KERNEL); } @@ -250,7 +254,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) cFYI(1,("call to reconnect done")); csocket = server->ssocket; continue; - } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) { + } else if ((length == -ERESTARTSYS) || (length == -EAGAIN) + || ((length > 0) && (length <= 3)) ) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); /* minimum sleep to prevent looping allowing socket to clear and app threads to set @@ -276,7 +281,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } pdu_length = 4 + ntohl(smb_buffer->smb_buf_length); - /* Ony read pdu_length after below checks for too short (due + /* Only read pdu_length after below checks for too short (due to e.g. int overflow) and too long ie beyond end of buf */ cFYI(1, ("Peek length rcvd: 0x%x beginning 0x%x)", length, pdu_length)); @@ -326,18 +331,24 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) csocket = server->ssocket; continue; } else { - if ((length != sizeof (struct smb_hdr) - 1) - || (pdu_length > - CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) + if (length < 16) { + /* We can not validate the SMB unless + at least this much of SMB available + so give the socket time to copy + a few more bytes and retry */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(10); + continue; + } else if( (pdu_length > + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) || (pdu_length < sizeof (struct smb_hdr) - 1) - || - (checkSMBhdr + || (checkSMBhdr (smb_buffer, smb_buffer->Mid))) { cERROR(1, - ("Invalid size or format for SMB found with length %d and pdu_lenght %d", + ("Invalid size or format for SMB found with length %d and pdu_length %d", length, pdu_length)); - cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr)); + cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr)+3); /* could we fix this network corruption by finding next smb header (instead of killing the session) and restart reading from next valid SMB found? */ @@ -485,7 +496,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) write_unlock(&GlobalSMBSeslock); if(length > 0) { mempool_resize(cifs_req_poolp, - length + CIFS_MIN_RCV_POOL, + length + cifs_min_rcv, GFP_KERNEL); } @@ -505,7 +516,7 @@ cifs_kcalloc(size_t size, int type) } static int -cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol) +cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) { char *value; char *data; @@ -692,7 +703,12 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol vol->file_mode = simple_strtoul(value, &value, 0); } - } else if (strnicmp(data, "dir_mode", 3) == 0) { + } else if (strnicmp(data, "dir_mode", 4) == 0) { + if (value && *value) { + vol->dir_mode = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "dirmode", 4) == 0) { if (value && *value) { vol->dir_mode = simple_strtoul(value, &value, 0); @@ -742,6 +758,8 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol /* ignore */ } else if (strnicmp(data, "version", 3) == 0) { /* ignore */ + } else if (strnicmp(data, "guest",5) == 0) { + /* ignore */ } else if (strnicmp(data, "rw", 2) == 0) { vol->rw = TRUE; } else if ((strnicmp(data, "suid", 4) == 0) || @@ -780,6 +798,27 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol vol->intr = 0; } else if (strnicmp(data, "intr", 4) == 0) { vol->intr = 1; + } else if (strnicmp(data, "serverino",7) == 0) { + vol->server_ino = 1; + } else if (strnicmp(data, "noserverino",9) == 0) { + vol->server_ino = 0; + } else if (strnicmp(data, "acl",3) == 0) { + vol->no_psx_acl = 0; + } else if (strnicmp(data, "noacl",5) == 0) { + vol->no_psx_acl = 1; + } else if (strnicmp(data, "direct",6) == 0) { + vol->direct_io = 1; + } else if (strnicmp(data, "forcedirectio",13) == 0) { + vol->direct_io = 1; + } else if (strnicmp(data, "in6_addr",8) == 0) { + if (!value || !*value) { + vol->in6_addr = NULL; + } else if (strnlen(value, 49) == 48) { + vol->in6_addr = value; + } else { + printk(KERN_WARNING "CIFS: ip v6 address not 48 characters long\n"); + return 1; + } } else if (strnicmp(data, "noac", 4) == 0) { printk(KERN_WARNING "CIFS: Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); } else @@ -1393,6 +1432,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; if(volume_info.setuids) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; + if(volume_info.server_ino) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; + if(volume_info.direct_io) { + cERROR(1,("mounting share using direct i/o")); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; + } tcon = find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, @@ -1482,8 +1527,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* do not care if following two calls succeed - informational only */ CIFSSMBQFSDeviceInfo(xid, tcon, cifs_sb->local_nls); CIFSSMBQFSAttributeInfo(xid, tcon, cifs_sb->local_nls); - if (tcon->ses->capabilities & CAP_UNIX) - CIFSSMBQFSUnixInfo(xid, tcon, cifs_sb->local_nls); + if (tcon->ses->capabilities & CAP_UNIX) { + if(!CIFSSMBQFSUnixInfo(xid, tcon, cifs_sb->local_nls)) { + if(!volume_info.no_psx_acl) { + if(CIFS_UNIX_POSIX_ACL_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability)) + cFYI(1,("server negotiated posix acl support")); + sb->s_flags |= MS_POSIXACL; + } + } + } } /* volume_info.password is freed above when existing session found @@ -1552,14 +1605,15 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, capabilities |= CAP_DFS; } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - /* pSMB->req_no_secext.CaseInsensitivePasswordLength = - CIFS_SESSION_KEY_SIZE; */ - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + + pSMB->req_no_secext.CaseInsensitivePasswordLength = + cpu_to_le16(CIFS_SESSION_KEY_SIZE); + pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(CIFS_SESSION_KEY_SIZE); bcc_ptr = pByteArea(smb_buffer); - /* memcpy(bcc_ptr, (char *) lm_session_key, CIFS_SESSION_KEY_SIZE); - bcc_ptr += CIFS_SESSION_KEY_SIZE; */ + memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE); + bcc_ptr += CIFS_SESSION_KEY_SIZE; memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE); bcc_ptr += CIFS_SESSION_KEY_SIZE; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 91567f61d6dd..f54e1866f0f4 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -399,9 +399,6 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name (" parent inode = 0x%p name is: %s and dentry = 0x%p", parent_dir_inode, direntry->d_name.name, direntry)); - if(nd) { /* BB removeme */ - cFYI(1,("In lookup nd flags 0x%x open intent flags 0x%x",nd->flags,nd->intent.open.flags)); - } /* BB removeme BB */ /* BB Add check of incoming data - e.g. frame not longer than maximum SMB - let server check the namelen BB */ /* check whether path exists */ diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index b908768b6215..9babaf869025 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -28,6 +28,44 @@ #include "cifs_unicode.h" #include "cifs_debug.h" +__u32 convert_to_cifs_notify_flags(unsigned long fcntl_notify_flags) +{ + __u32 cifs_ntfy_flags = 0; + + /* No way on Linux VFS to ask to monitor xattr + changes (and no stream support either */ + if(fcntl_notify_flags & DN_ACCESS) { + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + } + if(fcntl_notify_flags & DN_MODIFY) { + /* What does this mean on directories? */ + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_SIZE; + } + if(fcntl_notify_flags & DN_CREATE) { + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_LAST_WRITE; + } + if(fcntl_notify_flags & DN_DELETE) { + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE; + } + if(fcntl_notify_flags & DN_RENAME) { + /* BB review this - checking various server behaviors */ + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_FILE_NAME; + } + if(fcntl_notify_flags & DN_ATTRIB) { + cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_SECURITY | + FILE_NOTIFY_CHANGE_ATTRIBUTES; + } +/* if(fcntl_notify_flags & DN_MULTISHOT) { + cifs_ntfy_flags |= ; + } */ /* BB fixme - not sure how to handle this with CIFS yet */ + + + return cifs_ntfy_flags; +} + int cifs_dir_notify(struct file * file, unsigned long arg) { int xid; @@ -37,7 +75,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg) struct cifsTconInfo *pTcon; char *full_path = NULL; __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES; - __u16 netfid; + __u16 netfid; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); @@ -50,20 +88,26 @@ int cifs_dir_notify(struct file * file, unsigned long arg) if(full_path == NULL) { rc = -ENOMEM; } else { - cFYI(1,("cifs dir notify on file %s",full_path)); + cERROR(1,("cifs dir notify on file %s with arg 0x%lx",full_path,arg)); /* BB removeme BB */ rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ | SYNCHRONIZE, 0 /* create options */, &netfid, &oplock,NULL, cifs_sb->local_nls); /* BB fixme - add this handle to a notify handle list */ if(rc) { - cFYI(1,("Could not open directory for notify")); + cERROR(1,("Could not open directory for notify")); /* BB remove BB */ } else { - rc = CIFSSMBNotify(xid, pTcon, 1 /* subdirs */, netfid, - filter, cifs_sb->local_nls); + filter = convert_to_cifs_notify_flags(arg); + if(filter != 0) { + rc = CIFSSMBNotify(xid, pTcon, 0 /* no subdirs */, netfid, + filter, cifs_sb->local_nls); + } else { + rc = -EINVAL; + } /* BB add code to close file eventually (at unmount it would close automatically but may be a way to do it easily when inode freed or when notify info is cleared/changed */ + cERROR(1,("notify rc %d",rc)); } } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index bd31d4e2a9fd..6356b66db9cc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -35,6 +35,8 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" +extern int cifs_readdir2(struct file *file, void *direntry, filldir_t filldir); /* BB removeme BB */ + int cifs_open(struct inode *inode, struct file *file) { @@ -62,7 +64,7 @@ cifs_open(struct inode *inode, struct file *file) read_lock(&GlobalSMBSeslock); list_for_each(tmp, &pCifsInode->openFileList) { pCifsFile = list_entry(tmp,struct cifsFileInfo, flist); - if((pCifsFile->pfile == NULL)&& (pCifsFile->pid = current->pid)){ + if((pCifsFile->pfile == NULL)&& (pCifsFile->pid == current->tgid)){ /* mode set in cifs_create */ pCifsFile->pfile = file; /* needed for writepage */ file->private_data = pCifsFile; @@ -166,7 +168,7 @@ cifs_open(struct inode *inode, struct file *file) memset(file->private_data, 0, sizeof(struct cifsFileInfo)); pCifsFile = (struct cifsFileInfo *) file->private_data; pCifsFile->netfid = netfid; - pCifsFile->pid = current->pid; + pCifsFile->pid = current->tgid; init_MUTEX(&pCifsFile->fh_sem); pCifsFile->pfile = file; /* needed for writepage */ pCifsFile->pInode = inode; @@ -452,18 +454,44 @@ cifs_closedir(struct inode *inode, struct file *file) { int rc = 0; int xid; - struct cifsFileInfo *pSMBFileStruct = + struct cifsFileInfo *pCFileStruct = (struct cifsFileInfo *) file->private_data; + char * ptmp; cFYI(1, ("Closedir inode = 0x%p with ", inode)); xid = GetXid(); - if (pSMBFileStruct) { + if (pCFileStruct) { + struct cifsTconInfo *pTcon; + struct cifs_sb_info * cifs_sb = CIFS_SB(file->f_dentry->d_sb); + + pTcon = cifs_sb->tcon; + cFYI(1, ("Freeing private data in close dir")); + if(pCFileStruct->srch_inf.endOfSearch == FALSE) { + pCFileStruct->invalidHandle = TRUE; + rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); + cFYI(1,("Closing uncompleted readdir with rc %d",rc)); + /* not much we can do if it fails anywway, ignore rc */ + rc = 0; + } + ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; + if(ptmp) { + cFYI(1,("freeing smb buf in srch struct in closedir")); /* BB removeme BB */ + pCFileStruct->srch_inf.ntwrk_buf_start = NULL; + cifs_buf_release(ptmp); + } + ptmp = pCFileStruct->search_resume_name; + if(ptmp) { + cFYI(1,("freeing resume name in closedir")); /* BB removeme BB */ + pCFileStruct->search_resume_name = NULL; + kfree(ptmp); + } kfree(file->private_data); file->private_data = NULL; } + /* BB can we lock the filestruct while this is going on? */ FreeXid(xid); return rc; } @@ -570,12 +598,131 @@ cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) pfLock->fl_start, numUnlock, numLock, lockType, wait_flag); if (rc == 0 && (pfLock->fl_flags & FL_POSIX)) - posix_lock_file(file, pfLock); + posix_lock_file_wait(file, pfLock); FreeXid(xid); return rc; } ssize_t +cifs_user_write(struct file * file, const char __user * write_data, + size_t write_size, loff_t * poffset) +{ + int rc = 0; + unsigned int bytes_written = 0; + unsigned int total_written; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + int xid, long_op; + struct cifsFileInfo * open_file; + + if(file->f_dentry == NULL) + return -EBADF; + + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + if(cifs_sb == NULL) { + return -EBADF; + } + pTcon = cifs_sb->tcon; + + /*cFYI(1, + (" write %d bytes to offset %lld of %s", write_size, + *poffset, file->f_dentry->d_name.name)); */ + + if (file->private_data == NULL) { + return -EBADF; + } else { + open_file = (struct cifsFileInfo *) file->private_data; + } + + xid = GetXid(); + if(file->f_dentry->d_inode == NULL) { + FreeXid(xid); + return -EBADF; + } + + if (*poffset > file->f_dentry->d_inode->i_size) + long_op = 2; /* writes past end of file can take a long time */ + else + long_op = 1; + + for (total_written = 0; write_size > total_written; + total_written += bytes_written) { + rc = -EAGAIN; + while(rc == -EAGAIN) { + if(file->private_data == NULL) { + /* file has been closed on us */ + FreeXid(xid); + /* if we have gotten here we have written some data + and blocked, and the file has been freed on us + while we blocked so return what we managed to write */ + return total_written; + } + if(open_file->closePend) { + FreeXid(xid); + if(total_written) + return total_written; + else + return -EBADF; + } + if (open_file->invalidHandle) { + if((file->f_dentry == NULL) || + (file->f_dentry->d_inode == NULL)) { + FreeXid(xid); + return total_written; + } + /* we could deadlock if we called + filemap_fdatawait from here so tell + reopen_file not to flush data to server now */ + rc = cifs_reopen_file(file->f_dentry->d_inode, + file,FALSE); + if(rc != 0) + break; + } + + rc = CIFSSMBWrite(xid, pTcon, + open_file->netfid, + write_size - total_written, *poffset, + &bytes_written, + NULL, write_data + total_written, long_op); + } + if (rc || (bytes_written == 0)) { + if (total_written) + break; + else { + FreeXid(xid); + return rc; + } + } else + *poffset += bytes_written; + long_op = FALSE; /* subsequent writes fast - 15 seconds is plenty */ + } + +#ifdef CONFIG_CIFS_STATS + if(total_written > 0) { + atomic_inc(&pTcon->num_writes); + spin_lock(&pTcon->stat_lock); + pTcon->bytes_written += total_written; + spin_unlock(&pTcon->stat_lock); + } +#endif + + /* since the write may have blocked check these pointers again */ + if(file->f_dentry) { + if(file->f_dentry->d_inode) { + file->f_dentry->d_inode->i_ctime = file->f_dentry->d_inode->i_mtime = + CURRENT_TIME; + if (total_written > 0) { + if (*poffset > file->f_dentry->d_inode->i_size) + i_size_write(file->f_dentry->d_inode, *poffset); + } + mark_inode_dirty_sync(file->f_dentry->d_inode); + } + } + FreeXid(xid); + return total_written; +} + +static ssize_t cifs_write(struct file * file, const char *write_data, size_t write_size, loff_t * poffset) { @@ -652,10 +799,10 @@ cifs_write(struct file * file, const char *write_data, } rc = CIFSSMBWrite(xid, pTcon, - open_file->netfid, + open_file->netfid, write_size - total_written, *poffset, &bytes_written, - write_data + total_written, long_op); + write_data + total_written, NULL, long_op); } if (rc || (bytes_written == 0)) { if (total_written) @@ -969,6 +1116,83 @@ int cifs_flush(struct file *file) ssize_t +cifs_user_read(struct file * file, char __user *read_data, size_t read_size, + loff_t * poffset) +{ + int rc = -EACCES; + unsigned int bytes_read = 0; + unsigned int total_read = 0; + unsigned int current_read_size; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + int xid; + struct cifsFileInfo * open_file; + char * smb_read_data; + char __user * current_offset; + struct smb_com_read_rsp * pSMBr; + + xid = GetXid(); + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + pTcon = cifs_sb->tcon; + + if (file->private_data == NULL) { + FreeXid(xid); + return -EBADF; + } + open_file = (struct cifsFileInfo *)file->private_data; + + if((file->f_flags & O_ACCMODE) == O_WRONLY) { + cFYI(1,("attempting read on write only file instance")); + } + + for (total_read = 0,current_offset=read_data; read_size > total_read; + total_read += bytes_read,current_offset+=bytes_read) { + current_read_size = min_t(const int,read_size - total_read,cifs_sb->rsize); + rc = -EAGAIN; + smb_read_data = NULL; + while(rc == -EAGAIN) { + if ((open_file->invalidHandle) && (!open_file->closePend)) { + rc = cifs_reopen_file(file->f_dentry->d_inode, + file,TRUE); + if(rc != 0) + break; + } + + rc = CIFSSMBRead(xid, pTcon, + open_file->netfid, + current_read_size, *poffset, + &bytes_read, &smb_read_data); + + pSMBr = (struct smb_com_read_rsp *)smb_read_data; + copy_to_user(current_offset,smb_read_data + 4/* RFC1001 hdr*/ + + le16_to_cpu(pSMBr->DataOffset), bytes_read); + if(smb_read_data) { + cifs_buf_release(smb_read_data); + smb_read_data = NULL; + } + } + if (rc || (bytes_read == 0)) { + if (total_read) { + break; + } else { + FreeXid(xid); + return rc; + } + } else { +#ifdef CONFIG_CIFS_STATS + atomic_inc(&pTcon->num_reads); + spin_lock(&pTcon->stat_lock); + pTcon->bytes_read += total_read; + spin_unlock(&pTcon->stat_lock); +#endif + *poffset += bytes_read; + } + } + FreeXid(xid); + return total_read; +} + +static ssize_t cifs_read(struct file * file, char *read_data, size_t read_size, loff_t * poffset) { @@ -1039,6 +1263,17 @@ int cifs_file_mmap(struct file * file, struct vm_area_struct * vma) struct dentry * dentry = file->f_dentry; int rc, xid; +#ifdef CIFS_EXPERIMENTAL /* BB fixme reenable when cifs_read_wrapper fixed */ + if(dentry->d_sb) { + struct cifs_sb_info *cifs_sb; + cifs_sb = CIFS_SB(sb); + if(cifs_sb != NULL) { + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) + return -ENODEV + } + } +#endif /* CIFS_EXPERIMENTAL */ + xid = GetXid(); rc = cifs_revalidate(dentry); if (rc) { @@ -1199,8 +1434,6 @@ cifs_readpages(struct file *file, struct address_space *mapping, spin_unlock(&pTcon->stat_lock); #endif if((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) { - cFYI(1,("Partial page %d of %d read to cache",i++,num_pages)); - i++; /* account for partial page */ /* server copy of file can have smaller size than client */ @@ -1376,23 +1609,22 @@ fill_in_inode(struct inode *tmp_inode, cFYI(0, ("CIFS FFIRST: Attributes came in as 0x%x", attr)); - if (attr & ATTR_REPARSE) { - *pobject_type = DT_LNK; - /* BB can this and S_IFREG or S_IFDIR be set as in Windows? */ - tmp_inode->i_mode |= S_IFLNK; - } else if (attr & ATTR_DIRECTORY) { + if (attr & ATTR_DIRECTORY) { *pobject_type = DT_DIR; /* override default perms since we do not lock dirs */ if(atomic_read(&cifsInfo->inUse) == 0) { tmp_inode->i_mode = cifs_sb->mnt_dir_mode; } tmp_inode->i_mode |= S_IFDIR; +/* we no longer mark these because we could not follow them */ +/* } else if (attr & ATTR_REPARSE) { + *pobject_type = DT_LNK; + tmp_inode->i_mode |= S_IFLNK;*/ } else { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; if(attr & ATTR_READONLY) tmp_inode->i_mode &= ~(S_IWUGO); - }/* could add code here - to validate if device or weird share type? */ /* can not fill in nlink here as in qpathinfo version and Unx search */ @@ -1516,13 +1748,16 @@ unix_fill_in_inode(struct inode *tmp_inode, } } -static void +/* Returns one if new inode created (which therefore needs to be hashed) */ +/* Might check in the future if inode number changed so we can rehash inode */ +int construct_dentry(struct qstr *qstring, struct file *file, struct inode **ptmp_inode, struct dentry **pnew_dentry) { struct dentry *tmp_dentry; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; + int rc = 0; cFYI(1, ("For %s ", qstring->name)); cifs_sb = CIFS_SB(file->f_dentry->d_sb); @@ -1537,29 +1772,30 @@ construct_dentry(struct qstr *qstring, struct file *file, if(*ptmp_inode == NULL) { *ptmp_inode = new_inode(file->f_dentry->d_sb); if(*ptmp_inode == NULL) - return; + return rc; + rc = 1; d_instantiate(tmp_dentry, *ptmp_inode); - insert_inode_hash(*ptmp_inode); } } else { tmp_dentry = d_alloc(file->f_dentry, qstring); if(tmp_dentry == NULL) { cERROR(1,("Failed allocating dentry")); *ptmp_inode = NULL; - return; + return rc; } *ptmp_inode = new_inode(file->f_dentry->d_sb); tmp_dentry->d_op = &cifs_dentry_ops; if(*ptmp_inode == NULL) - return; + return rc; + rc = 1; d_instantiate(tmp_dentry, *ptmp_inode); d_rehash(tmp_dentry); - insert_inode_hash(*ptmp_inode); } tmp_dentry->d_time = jiffies; *pnew_dentry = tmp_dentry; + return rc; } static void reset_resume_key(struct file * dir_file, @@ -1609,11 +1845,19 @@ cifs_filldir(struct qstr *pqstring, FILE_DIRECTORY_INFO * pfindData, pqstring->name = pfindData->FileName; /* pqstring->len is already set by caller */ - construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); + rc = construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); if((tmp_inode == NULL) || (tmp_dentry == NULL)) { return -ENOMEM; } fill_in_inode(tmp_inode, pfindData, &object_type); + if(rc) { + /* We have no reliable way to get inode numbers + from servers w/o Unix extensions yet so we can not set + i_ino from pfindData yet */ + + /* new inode created, let us hash it */ + insert_inode_hash(tmp_inode); + } /* else if inode number changed do we rehash it? */ rc = filldir(direntry, pfindData->FileName, pqstring->len, file->f_pos, tmp_inode->i_ino, object_type); if(rc) { @@ -1637,11 +1881,19 @@ cifs_filldir_unix(struct qstr *pqstring, pqstring->name = pUnixFindData->FileName; pqstring->len = strnlen(pUnixFindData->FileName, MAX_PATHCONF); - construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); + rc = construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); if((tmp_inode == NULL) || (tmp_dentry == NULL)) { return -ENOMEM; - } + } + if(rc) { + struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb); + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + tmp_inode->i_ino = + (unsigned long)pUnixFindData->UniqueId; + } + insert_inode_hash(tmp_inode); + } /* else if i_ino has changed should we rehash it? */ unix_fill_in_inode(tmp_inode, pUnixFindData, &object_type); rc = filldir(direntry, pUnixFindData->FileName, pqstring->len, file->f_pos, tmp_inode->i_ino, object_type); @@ -1675,25 +1927,34 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) FILE_DIRECTORY_INFO *lastFindData; FILE_UNIX_INFO *pfindDataUnix; + + /* BB removeme begin */ + if(!experimEnabled) + return cifs_readdir2(file,direntry,filldir); + /* BB removeme end */ + + xid = GetXid(); + if(file->f_dentry == NULL) { + rc = -EIO; + FreeXid(xid); + return rc; + } cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; bufsize = pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; - if(bufsize > CIFS_MAX_MSGSIZE) { + if(bufsize > CIFSMaxBufSize) { + rc = -EIO; FreeXid(xid); - return -EIO; + return rc; } data = kmalloc(bufsize, GFP_KERNEL); pfindData = (FILE_DIRECTORY_INFO *) data; if(data == NULL) { + rc = -ENOMEM; FreeXid(xid); - return -ENOMEM; - } - if(file->f_dentry == NULL) { - kfree(data); - FreeXid(xid); - return -EIO; + return rc; } down(&file->f_dentry->d_sb->s_vfs_rename_sem); full_path = build_wildcard_path_from_dentry(file->f_dentry); @@ -1727,8 +1988,8 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) if (file->private_data != NULL) { cifsFile = (struct cifsFileInfo *) file->private_data; - if (cifsFile->endOfSearch) { - if(cifsFile->emptyDir) { + if (cifsFile->srch_inf.endOfSearch) { + if(cifsFile->srch_inf.emptyDir) { cFYI(1, ("End of search, empty dir")); rc = 0; break; @@ -1778,7 +2039,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) break; } /* Offset of resume key same for levels 257 and 514 */ - cifsFile->resume_key = lastFindData->FileIndex; + cifsFile->srch_inf.resume_key = lastFindData->FileIndex; if(UnixSearch == FALSE) { cifsFile->resume_name_length = le32_to_cpu(lastFindData->FileNameLength); @@ -1915,13 +2176,13 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) /* if(pfindData > lastFindData) rc = -EIO; break; */ } /* end for loop */ if ((findParms.EndofSearch != 0) && cifsFile) { - cifsFile->endOfSearch = TRUE; + cifsFile->srch_inf.endOfSearch = TRUE; if(findParms.SearchCount == cpu_to_le16(2)) - cifsFile->emptyDir = TRUE; + cifsFile->srch_inf.emptyDir = TRUE; } } else { if (cifsFile) - cifsFile->endOfSearch = TRUE; + cifsFile->srch_inf.endOfSearch = TRUE; /* unless parent directory gone do not return error */ rc = 0; } @@ -1934,7 +2195,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) file->f_pos)); } else { cifsFile = (struct cifsFileInfo *) file->private_data; - if (cifsFile->endOfSearch) { + if (cifsFile->srch_inf.endOfSearch) { rc = 0; cFYI(1, ("End of search ")); break; @@ -1944,7 +2205,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) &findNextParms, searchHandle, cifsFile->search_resume_name, cifsFile->resume_name_length, - cifsFile->resume_key, + cifsFile->srch_inf.resume_key, &Unicode, &UnixSearch); cFYI(1,("Count: %d End: %d ", le16_to_cpu(findNextParms.SearchCount), @@ -1961,7 +2222,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) break; } /* Offset of resume key same for levels 257 and 514 */ - cifsFile->resume_key = lastFindData->FileIndex; + cifsFile->srch_inf.resume_key = lastFindData->FileIndex; if(UnixSearch == FALSE) { cifsFile->resume_name_length = @@ -2114,10 +2375,10 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) /* BB also should check to ensure pointer not beyond end of SMB */ } /* end for loop */ if (findNextParms.EndofSearch != 0) { - cifsFile->endOfSearch = TRUE; + cifsFile->srch_inf.endOfSearch = TRUE; } } else { - cifsFile->endOfSearch = TRUE; + cifsFile->srch_inf.endOfSearch = TRUE; rc = 0; /* unless parent directory disappeared - do not return error here (eg Access Denied or no more files) */ } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 84d09d9f1a2d..319925079afa 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -85,6 +85,13 @@ cifs_get_inode_info_unix(struct inode **pinode, *pinode = new_inode(sb); if(*pinode == NULL) return -ENOMEM; + /* Is an i_ino of zero legal? */ + /* Are there sanity checks we can use to ensure that + the server is really filling in that field? */ + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + (*pinode)->i_ino = + (unsigned long)findData.UniqueId; + } /* note ino incremented to unique num in new_inode */ insert_inode_hash(*pinode); } @@ -244,6 +251,21 @@ cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, *pinode = new_inode(sb); if(*pinode == NULL) return -ENOMEM; + /* Is an i_ino of zero legal? */ + /* Are there sanity checks we can use to ensure that + the server is really filling in that field? */ + + /* We can not use the IndexNumber from either + Windows or Samba as it is frequently set to zero */ + /* There may be higher info levels that work but + Are there Windows server or network appliances + for which IndexNumber field is not guaranteed unique? */ + + /* if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + (*pinode)->i_ino = + (unsigned long)pfindData->IndexNumber; + } */ /*NB: ino incremented to unique num in new_inode*/ + insert_inode_hash(*pinode); } inode = *pinode; @@ -273,10 +295,10 @@ cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, /* new inode, can safely set these fields */ inode->i_mode = cifs_sb->mnt_file_mode; - if (attr & ATTR_REPARSE) { - /* Can IFLNK be set as it basically is on windows with IFREG or IFDIR? */ - inode->i_mode |= S_IFLNK; - } else if (attr & ATTR_DIRECTORY) { +/* if (attr & ATTR_REPARSE) */ +/* We no longer handle these as symlinks because we could not */ +/* follow them due to the absolute path with drive letter */ + if (attr & ATTR_DIRECTORY) { /* override default perms since we do not do byte range locking on dirs */ inode->i_mode = cifs_sb->mnt_dir_mode; inode->i_mode |= S_IFDIR; @@ -958,6 +980,16 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) via Handle (SetFileInfo) instead of by path */ rc = CIFSSMBSetTimes(xid, pTcon, full_path, &time_buf, cifs_sb->local_nls); + if(rc == -EOPNOTSUPP) { + cFYI(1,("OS2 level of SetPathInfo not implemented")); + /* Need to convert time_buf into old format, + but probably better to do that inside the function + below rather than here */ + /* Better to return EOPNOTSUPP until function + below is ready */ + /* CIFSSMBSetTimesLegacy(xid, pTcon, full_path, + FILE_INFO_STANDARD * data, cifs_sb->local_nls); */ + } } /* do not need local check to inode_check_ok since the server does that */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 6bf6ee31dad5..495504dd1f6a 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1,7 +1,7 @@ /* * fs/cifs/misc.c * - * Copyright (C) International Business Machines Corp., 2002,2003 + * Copyright (C) International Business Machines Corp., 2002,2004 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -29,6 +29,7 @@ #include "smberr.h" #include "nterr.h" +extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern struct task_struct * oplockThread; @@ -160,8 +161,9 @@ cifs_buf_get(void) (struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS); /* clear the first few header bytes */ + /* for most paths, more is cleared in header_assemble */ if (ret_buf) { - memset(ret_buf, 0, sizeof (struct smb_hdr)); + memset(ret_buf, 0, sizeof(struct smb_hdr) + 3); atomic_inc(&bufAllocCount); } @@ -173,7 +175,7 @@ cifs_buf_release(void *buf_to_free) { if (buf_to_free == NULL) { - cFYI(1, ("Null buffer passed to cifs_buf_release")); + /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/ return; } mempool_free(buf_to_free,cifs_req_poolp); @@ -182,20 +184,49 @@ cifs_buf_release(void *buf_to_free) return; } +struct smb_hdr * +cifs_small_buf_get(void) +{ + struct smb_hdr *ret_buf = NULL; + +/* We could use negotiated size instead of max_msgsize - + but it may be more efficient to always alloc same size + albeit slightly larger than necessary and maxbuffersize + defaults to this and can not be bigger */ + ret_buf = + (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, SLAB_KERNEL | SLAB_NOFS); + if (ret_buf) { + /* No need to clear memory here, cleared in header assemble */ + /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ + atomic_inc(&smBufAllocCount); + } + return ret_buf; +} + +void +cifs_small_buf_release(void *buf_to_free) +{ + + if (buf_to_free == NULL) { + cFYI(1, ("Null buffer passed to cifs_small_buf_release")); + return; + } + mempool_free(buf_to_free,cifs_sm_req_poolp); + + atomic_dec(&smBufAllocCount); + return; +} + void header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , const struct cifsTconInfo *treeCon, int word_count - /* length of fixed section (word count) in two byte units */ - ) + /* length of fixed section (word count) in two byte units */) { - int i; struct list_head* temp_item; struct cifsSesInfo * ses; char *temp = (char *) buffer; - for (i = 0; i < MAX_CIFS_HDR_SIZE; i++) { - temp[i] = 0; /* BB is this needed ?? */ - } + memset(temp,0,MAX_CIFS_HDR_SIZE); buffer->smb_buf_length = (2 * word_count) + sizeof (struct smb_hdr) - @@ -320,7 +351,7 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) ("Entering checkSMB with Length: %x, smb_buf_length: %x ", length, len)); if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) || - (len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4)) { + (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) { if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) { cERROR(1, ("Length less than 2 + sizeof smb_hdr ")); if (((unsigned int)length >= sizeof (struct smb_hdr) - 1) @@ -328,9 +359,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) return 0; /* some error cases do not return wct and bcc */ } - if (len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) cERROR(1, - ("smb_buf_length greater than CIFS_MAX_MSGSIZE ... ")); + ("smb_buf_length greater than MaxBufSize")); cERROR(1, ("bad smb detected. Illegal length. The mid=%d", smb->Mid)); @@ -359,8 +390,29 @@ is_valid_oplock_break(struct smb_hdr *buf) struct cifsTconInfo *tcon; struct cifsFileInfo *netfile; - /* could add check for smb response flag 0x80 */ - cFYI(1,("Checking for oplock break")); + cFYI(1,("Checking for oplock break or dnotify response")); + if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && + (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { + struct smb_com_transaction_change_notify_rsp * pSMBr = + (struct smb_com_transaction_change_notify_rsp *)buf; + struct file_notify_information * pnotify; + __u32 data_offset = 0; + if(pSMBr->ByteCount > sizeof(struct file_notify_information)) { + data_offset = le32_to_cpu(pSMBr->DataOffset); + + pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol + + data_offset); + cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName, + pnotify->Action)); /* BB removeme BB */ + /* cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */ + return TRUE; + } + if(pSMBr->hdr.Status.CifsError) { + cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError)); + return TRUE; + } + return FALSE; + } if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) return FALSE; if(pSMB->hdr.Flags & SMBFLG_RESPONSE) { diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index a78ed0add9d7..b1f5248363e9 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -69,10 +69,12 @@ const struct smb_to_posix_error mapping_table_ERRDOS[] = { {ERRinvparm, -EINVAL}, {ERRdiskfull, -ENOSPC}, {ERRinvname, -ENOENT}, + {ERRinvlevel,-EOPNOTSUPP}, {ERRdirnotempty, -ENOTEMPTY}, {ERRnotlocked, -ENOLCK}, {ERRalreadyexists, -EEXIST}, {ERRmoredata, -EOVERFLOW}, + {ERReasnotsupported,-EOPNOTSUPP}, {ErrQuota, -EDQUOT}, {ErrNotALink, -ENOLINK}, {ERRnetlogonNotStarted,-ENOPROTOOPT}, @@ -287,7 +289,7 @@ static const struct { ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { - ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { @@ -752,7 +754,8 @@ static const struct { ERRDOS, ERRnoaccess, 0xc000028e}, { ERRDOS, ERRnoaccess, 0xc000028f}, { ERRDOS, ERRnoaccess, 0xc0000290}, { -ERRDOS, ERRbadfunc, 0xc000029c},}; + ERRDOS, ERRbadfunc, 0xc000029c}, { + ERRDOS, ERRinvlevel, 0x007c0001}, }; /***************************************************************************** Print an error message from the status code diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c new file mode 100644 index 000000000000..0fe23d2efd1b --- /dev/null +++ b/fs/cifs/readdir.c @@ -0,0 +1,648 @@ +/* + * fs/cifs/readdir.c + * + * Directory search handling + * + * Copyright (C) International Business Machines Corp., 2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * This library 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 library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/smp_lock.h> +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" + +extern int CIFSFindFirst2(const int xid, struct cifsTconInfo *tcon, + const char *searchName, const struct nls_table *nls_codepage, + __u16 *searchHandle, struct cifs_search_info * psrch_inf); + +extern int CIFSFindNext2(const int xid, struct cifsTconInfo *tcon, + __u16 searchHandle, struct cifs_search_info * psrch_inf); + +extern int construct_dentry(struct qstr *qstring, struct file *file, + struct inode **ptmp_inode, struct dentry **pnew_dentry); + +extern void fill_in_inode(struct inode *tmp_inode, + FILE_DIRECTORY_INFO * pfindData, int *pobject_type); + +extern void unix_fill_in_inode(struct inode *tmp_inode, + FILE_UNIX_INFO * pfindData, int *pobject_type); + + +/* BB fixme - add debug wrappers around this function to disable it fixme BB */ +/* static void dump_cifs_file_struct(struct file * file, char * label) +{ + struct cifsFileInfo * cf; + + if(file) { + cf = (struct cifsFileInfo *)file->private_data; + if(cf == NULL) { + cFYI(1,("empty cifs private file data")); + return; + } + if(cf->invalidHandle) { + cFYI(1,("invalid handle")); + } + if(cf->srch_inf.endOfSearch) { + cFYI(1,("end of search")); + } + if(cf->srch_inf.emptyDir) { + cFYI(1,("empty dir")); + } + + } +} */ + +static int initiate_cifs_search(const int xid, struct file * file) +{ + int rc = 0; + char * full_path; + struct cifsFileInfo * cifsFile; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + + if(file->private_data == NULL) { + file->private_data = + kmalloc(sizeof(struct cifsFileInfo),GFP_KERNEL); + } + + if(file->private_data == NULL) { + return -ENOMEM; + } else { + memset(file->private_data,0,sizeof(struct cifsFileInfo)); + } + cifsFile = (struct cifsFileInfo *)file->private_data; + cifsFile->invalidHandle = TRUE; + cifsFile->srch_inf.endOfSearch = FALSE; + + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + if(cifs_sb == NULL) + return -EINVAL; + + pTcon = cifs_sb->tcon; + if(pTcon == NULL) + return -EINVAL; + + if(file->f_dentry == NULL) + return -ENOENT; + + down(&file->f_dentry->d_sb->s_vfs_rename_sem); + full_path = build_wildcard_path_from_dentry(file->f_dentry); + up(&file->f_dentry->d_sb->s_vfs_rename_sem); + + if(full_path == NULL) { + return -ENOMEM; + } + + cFYI(1, ("Full path: %s start at: %lld ", full_path, file->f_pos)); + + /* test for Unix extensions */ + if (pTcon->ses->capabilities & CAP_UNIX) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + } else /* not srvinos - BB fixme add check for backlevel? */ { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; + } + + rc = CIFSFindFirst2(xid, pTcon,full_path,cifs_sb->local_nls, + &cifsFile->netfid, &cifsFile->srch_inf); + if(rc == 0) + cifsFile->invalidHandle = FALSE; + if(full_path) + kfree(full_path); + return rc; +} + +/* return length of unicode string in bytes */ +static int cifs_unicode_bytelen(char * str) +{ + int len; + __le16 * ustr = (__le16 *)str; + + for(len=0;len <= PATH_MAX;len++) { + if(ustr[len] == 0) + return len << 1; + } + cFYI(1,("Unicode string longer than PATH_MAX found")); + return len << 1; +} + +static char * nxt_dir_entry(char * old_entry, char * end_of_smb) +{ + char * new_entry; + FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; + + new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); + cFYI(1,("new entry %p old entry %p",new_entry,old_entry)); + /* validate that new_entry is not past end of SMB */ + if(new_entry >= end_of_smb) { + cFYI(1,("search entry %p began after end of SMB %p old entry %p", + new_entry,end_of_smb,old_entry)); + return NULL; + } else + return new_entry; + +} + +#define UNICODE_DOT cpu_to_le16(0x2e) + +/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ +static int cifs_entry_is_dot(char * current_entry, struct cifsFileInfo * cfile) +{ + int rc = 0; + char * filename = NULL; + int len = 0; + + if(cfile->srch_inf.info_level == 0x202) { + FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; + filename = &pFindData->FileName[0]; + if(cfile->srch_inf.unicode) { + len = cifs_unicode_bytelen(filename); + } else { + /* BB should we make this strnlen of PATH_MAX? */ + len = strnlen(filename, 5); + } + } else if(cfile->srch_inf.info_level == 0x101) { + FILE_DIRECTORY_INFO * pFindData = + (FILE_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else if(cfile->srch_inf.info_level == 0x102) { + FILE_FULL_DIRECTORY_INFO * pFindData = + (FILE_FULL_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else if(cfile->srch_inf.info_level == 0x105) { + SEARCH_ID_FULL_DIR_INFO * pFindData = + (SEARCH_ID_FULL_DIR_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else if(cfile->srch_inf.info_level == 0x104) { + FILE_BOTH_DIRECTORY_INFO * pFindData = + (FILE_BOTH_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else { + cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level)); + } + + if(filename) { + if(cfile->srch_inf.unicode) { + __le16 *ufilename = (__le16 *)filename; + if(len == 2) { + /* check for . */ + if(ufilename[0] == UNICODE_DOT) + rc = 1; + } else if(len == 4) { + /* check for .. */ + if((ufilename[0] == UNICODE_DOT) + &&(ufilename[1] == UNICODE_DOT)) + rc = 2; + } + } else /* ASCII */ { + if(len == 1) { + if(filename[0] == '.') + rc = 1; + } else if(len == 2) { + if((filename[0] == '.') && (filename[1] == '.')) + rc = 2; + } + } + } + + return rc; +} + +/* find the corresponding entry in the search */ +/* Note that the SMB server returns search entries for . and .. which + complicates logic here if we choose to parse for them and we do not + assume that they are located in the findfirst return buffer.*/ +/* We start counting in the buffer with entry 2 and increment for every + entry (do not increment for . or .. entry) */ +static int find_cifs_entry(const int xid, struct cifsTconInfo * pTcon, + struct file * file, char ** ppCurrentEntry,int * num_to_ret) +{ + int rc = 0; + int pos_in_buf = 0; + loff_t first_entry_in_buffer; + loff_t index_to_find = file->f_pos; + struct cifsFileInfo * cifsFile = (struct cifsFileInfo *)file->private_data; + /* check if index in the buffer */ + + if((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL)) + return -ENOENT; + + *ppCurrentEntry = NULL; + first_entry_in_buffer = + cifsFile->srch_inf.index_of_last_entry - + cifsFile->srch_inf.entries_in_buffer; +/* dump_cifs_file_struct(file, "In fce ");*/ + if(index_to_find < first_entry_in_buffer) { + /* close and restart search */ + cFYI(1,("search backing up - close and restart search")); + cifsFile->invalidHandle = TRUE; + CIFSFindClose(xid, pTcon, cifsFile->netfid); + if(cifsFile->search_resume_name) { + kfree(cifsFile->search_resume_name); + cifsFile->search_resume_name = NULL; + } + if(cifsFile->srch_inf.ntwrk_buf_start) { + cFYI(1,("freeing SMB ff cache buf on search rewind")); + cifs_buf_release(cifsFile->srch_inf.ntwrk_buf_start); + } + rc = initiate_cifs_search(xid,file); + if(rc) { + cFYI(1,("error %d reinitiating a search on rewind",rc)); + return rc; + } + } + + while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && + (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){ + cFYI(1,("calling findnext2")); + rc = CIFSFindNext2(xid,pTcon,cifsFile->netfid, &cifsFile->srch_inf); + if(rc) + return -ENOENT; + } + if(index_to_find < cifsFile->srch_inf.index_of_last_entry) { + /* we found the buffer that contains the entry */ + /* scan and find it */ + int i; + char * current_entry; + char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + + smbCalcSize((struct smb_hdr *)cifsFile->srch_inf.ntwrk_buf_start); +/* dump_cifs_file_struct(file,"found entry in fce "); */ + first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - + cifsFile->srch_inf.entries_in_buffer; + pos_in_buf = index_to_find - first_entry_in_buffer; + cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); + current_entry = cifsFile->srch_inf.srch_entries_start; + for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) { + /* go entry to next entry figuring out which we need to start with */ + /* if( . or ..) + skip */ + rc = cifs_entry_is_dot(current_entry,cifsFile); + if(rc == 1) /* is . or .. so skip */ { + cFYI(1,("Entry is .")); /* BB removeme BB */ + /* continue; */ + } else if (rc == 2 ) { + cFYI(1,("Entry is ..")); /* BB removeme BB */ + /* continue; */ + } + current_entry = nxt_dir_entry(current_entry,end_of_smb); + } + if((current_entry == NULL) && (i < pos_in_buf)) { + cERROR(1,("reached end of buf searching for pos in buf %d index to find %lld rc %d",pos_in_buf,index_to_find,rc)); /* BB removeme BB */ + } + rc = 0; + *ppCurrentEntry = current_entry; + } else { + cFYI(1,("index not in buffer - could not findnext into it")); + return 0; + } + + if(pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { + cFYI(1,("can not return entries when pos_in_buf beyond last entry")); + *num_to_ret = 0; + } else + *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; +/* dump_cifs_file_struct(file, "end fce ");*/ + + return rc; +} + +/* inode num, inode type and filename returned */ +static int cifs_get_name_from_search_buf(struct qstr * pqst,char * current_entry, + __u16 level,unsigned int unicode,struct nls_table * nlt, + ino_t * pinum) +{ + int rc = 0; + unsigned int len = 0; + char * filename; + + *pinum = 0; + + if(level == SMB_FIND_FILE_UNIX) { + FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; + + filename = &pFindData->FileName[0]; + if(unicode) { + len = cifs_unicode_bytelen(filename); + } else { + /* BB should we make this strnlen of PATH_MAX? */ + len = strnlen(filename, PATH_MAX); + } + + /* BB fixme - hash low and high 32 bits if not 64 bit arch BB fixme */ + *pinum = pFindData->UniqueId; + } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) { + FILE_DIRECTORY_INFO * pFindData = + (FILE_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { + FILE_FULL_DIRECTORY_INFO * pFindData = + (FILE_FULL_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { + SEARCH_ID_FULL_DIR_INFO * pFindData = + (SEARCH_ID_FULL_DIR_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + *pinum = pFindData->UniqueId; + } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { + FILE_BOTH_DIRECTORY_INFO * pFindData = + (FILE_BOTH_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + } else { + cFYI(1,("Unknown findfirst level %d",level)); + return -EINVAL; + } + if(unicode) { + /* BB fixme - test with long names */ + /* Note converted filename can be longer than in unicode */ + pqst->len = cifs_strfromUCS_le((char *)pqst->name,(wchar_t *)filename,len/2,nlt); + } else { + pqst->name = filename; + pqst->len = len; + } + pqst->hash = full_name_hash(pqst->name,pqst->len); +/* cFYI(1,("filldir on %s",pqst->name)); */ + return rc; +} + + +static int +cifs_filldir2(char * pfindEntry, struct file *file, + filldir_t filldir, void *direntry,char * scratch_buf) +{ + int rc = 0; + struct qstr qstring; + struct cifsFileInfo * pCifsF; + unsigned obj_type; + ino_t inum; + struct cifs_sb_info * cifs_sb; + struct inode *tmp_inode; + struct dentry *tmp_dentry; + + /* get filename and len into qstring */ + /* get dentry */ + /* decide whether to create and populate ionde */ + if((direntry == NULL) || (file == NULL)) + return -EINVAL; + + pCifsF = file->private_data; + + if((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL)) + return -ENOENT; + + if(file->f_dentry == NULL) + return -ENOENT; + + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + + qstring.name = scratch_buf; + rc = cifs_get_name_from_search_buf(&qstring,pfindEntry, + pCifsF->srch_inf.info_level, + pCifsF->srch_inf.unicode,cifs_sb->local_nls, + &inum /* returned */); + + if(rc) + return rc; + + rc = construct_dentry(&qstring,file,&tmp_inode, &tmp_dentry); + if((tmp_inode == NULL) || (tmp_dentry == NULL)) + return -ENOMEM; + + if(rc) { + /* inode created, we need to hash it with right inode number */ + if(inum != 0) { + /* BB fixme - hash the 2 32 quantities bits together if necessary BB */ + tmp_inode->i_ino = inum; + } + insert_inode_hash(tmp_inode); + } + + if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) { + unix_fill_in_inode(tmp_inode,(FILE_UNIX_INFO *)pfindEntry,&obj_type); + } else { + fill_in_inode(tmp_inode,(FILE_DIRECTORY_INFO *)pfindEntry,&obj_type); + } + + rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type); + if(rc) { + cFYI(1,("filldir rc = %d",rc)); + } + + dput(tmp_dentry); + return rc; +} + +int cifs_save_resume_key(const char * current_entry,struct cifsFileInfo * cifsFile) +{ + int rc = 0; + unsigned int len = 0; + __u16 level; + char * filename; + + if((cifsFile == NULL) || (current_entry == NULL)) + return -EINVAL; + + level = cifsFile->srch_inf.info_level; + + if(level == SMB_FIND_FILE_UNIX) { + FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; + + filename = &pFindData->FileName[0]; + if(cifsFile->srch_inf.unicode) { + len = cifs_unicode_bytelen(filename); + } else { + /* BB should we make this strnlen of PATH_MAX? */ + len = strnlen(filename, PATH_MAX); + } + cifsFile->srch_inf.resume_key = pFindData->ResumeKey; + } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) { + FILE_DIRECTORY_INFO * pFindData = + (FILE_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + cifsFile->srch_inf.resume_key = pFindData->FileIndex; + } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { + FILE_FULL_DIRECTORY_INFO * pFindData = + (FILE_FULL_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + cifsFile->srch_inf.resume_key = pFindData->FileIndex; + } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { + SEARCH_ID_FULL_DIR_INFO * pFindData = + (SEARCH_ID_FULL_DIR_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + cifsFile->srch_inf.resume_key = pFindData->FileIndex; + } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { + FILE_BOTH_DIRECTORY_INFO * pFindData = + (FILE_BOTH_DIRECTORY_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); + cifsFile->srch_inf.resume_key = pFindData->FileIndex; + } else { + cFYI(1,("Unknown findfirst level %d",level)); + return -EINVAL; + } + cifsFile->srch_inf.resume_name_len = len; + cifsFile->srch_inf.presume_name = filename; + return rc; +} + +int cifs_readdir2(struct file *file, void *direntry, filldir_t filldir) +{ + int rc = 0; + int xid,i; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + struct cifsFileInfo *cifsFile = NULL; + char * current_entry; + int num_to_fill = 0; + char * tmp_buf = NULL; + char * end_of_smb; + + xid = GetXid(); + + if(file->f_dentry == NULL) { + FreeXid(xid); + return -EIO; + } +/* dump_cifs_file_struct(file, "Begin rdir "); */ + + cifs_sb = CIFS_SB(file->f_dentry->d_sb); + pTcon = cifs_sb->tcon; + if(pTcon == NULL) + return -EINVAL; + +/* cFYI(1,("readdir2 pos: %lld",file->f_pos)); */ + + switch ((int) file->f_pos) { + case 0: + /*if (filldir(direntry, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR) < 0) { + cERROR(1, ("Filldir for current dir failed ")); + rc = -ENOMEM; + break; + } + file->f_pos++; */ + case 1: + /* if (filldir(direntry, "..", 2, file->f_pos, + file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { + cERROR(1, ("Filldir for parent dir failed ")); + rc = -ENOMEM; + break; + } + file->f_pos++; */ + case 2: + /* 1) If search is active, + is in current search buffer? + if it before then restart search + if after then keep searching till find it */ + + if(file->private_data == NULL) { + rc = initiate_cifs_search(xid,file); + cFYI(1,("initiate cifs search rc %d",rc)); + if(rc) { + FreeXid(xid); + return rc; + } + } + default: + if(file->private_data == NULL) { + rc = -EINVAL; + FreeXid(xid); + return rc; + } + cifsFile = (struct cifsFileInfo *) file->private_data; + if (cifsFile->srch_inf.endOfSearch) { + if(cifsFile->srch_inf.emptyDir) { + cFYI(1, ("End of search, empty dir")); + rc = 0; + break; + } + } /* else { + cifsFile->invalidHandle = TRUE; + CIFSFindClose(xid, pTcon, cifsFile->netfid); + } + if(cifsFile->search_resume_name) { + kfree(cifsFile->search_resume_name); + cifsFile->search_resume_name = NULL; + } */ +/* BB account for . and .. in f_pos */ + /* dump_cifs_file_struct(file, "rdir after default ");*/ + + rc = find_cifs_entry(xid,pTcon, file, + ¤t_entry,&num_to_fill); + if(rc) { + cFYI(1,("fce error %d",rc)); + goto rddir2_exit; + } else if (current_entry != NULL) { + cFYI(1,("entry %lld found",file->f_pos)); + } else { + cFYI(1,("could not find entry")); + goto rddir2_exit; + } + cFYI(1,("loop through %d times filling dir for net buf %p", + num_to_fill,cifsFile->srch_inf.ntwrk_buf_start)); + end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + + smbCalcSize((struct smb_hdr *)cifsFile->srch_inf.ntwrk_buf_start); + tmp_buf = kmalloc(NAME_MAX+1,GFP_KERNEL); + for(i=0;(i<num_to_fill) && (rc == 0);i++) { + if(current_entry == NULL) { + cERROR(1,("beyond end of smb with num to fill %d i %d",num_to_fill,i)); /* BB removeme BB */ + break; + } +/* if((!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) || + (cifsFile->srch_inf.info_level != something that supports server inodes)) { + create dentry + create inode + fill in inode new_inode (which makes number locally) + } + also create local inode for per reasons unless new mount parm says otherwise */ + rc = cifs_filldir2(current_entry, file, + filldir, direntry,tmp_buf); + file->f_pos++; + if(file->f_pos == cifsFile->srch_inf.index_of_last_entry) { + cFYI(1,("last entry in buf at pos %lld %s",file->f_pos,tmp_buf)); /* BB removeme BB */ + cifs_save_resume_key(current_entry,cifsFile); + break; + } else + current_entry = nxt_dir_entry(current_entry,end_of_smb); + } + if(tmp_buf != NULL) + kfree(tmp_buf); + break; + } /* end switch */ + +rddir2_exit: + /* dump_cifs_file_struct(file, "end rdir "); */ + FreeXid(xid); + return rc; +} + diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h index 8db20a0f7edf..e21f1384661f 100644 --- a/fs/cifs/smberr.h +++ b/fs/cifs/smberr.h @@ -69,6 +69,7 @@ #define ERRpipeclosing 232 #define ERRnotconnected 233 #define ERRmoredata 234 +#define ERReasnotsupported 282 #define ErrQuota 0x200 /* The operation would cause a quota limit to be exceeded. */ #define ErrNotALink 0x201 /* A link operation was performed on a pathname that was not a link. */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a19f4a9c9841..79f0992a2c6b 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -176,6 +176,143 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } +#ifdef CIFS_EXPERIMENTAL +/* BB finish off this function, adding support for writing set of pages as iovec */ +/* and also adding support for operations that need to parse the response smb */ +int +CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) +{ + int rc = 0; + unsigned long timeout = 15 * HZ; + struct mid_q_entry *midQ = NULL; + + if (ses == NULL) { + cERROR(1,("Null smb session")); + return -EIO; + } + if(ses->server == NULL) { + cERROR(1,("Null tcp session")); + return -EIO; + } + if(pbytes_returned == NULL) + return -EIO; + else + *pbytes_returned = 0; + + + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + if(long_op == -1) { + /* oplock breaks must not be held up */ + atomic_inc(&ses->server->inFlight); + } else { + spin_lock(&GlobalMid_Lock); + while(1) { + if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ + spin_unlock(&GlobalMid_Lock); + wait_event(ses->server->request_q, + atomic_read(&ses->server->inFlight) + < cifs_max_pending); + spin_lock(&GlobalMid_Lock); + } else { + if(ses->server->tcpStatus == CifsExiting) { + spin_unlock(&GlobalMid_Lock); + return -ENOENT; + } + + /* can not count locking commands against total since + they are allowed to block on server */ + + if(long_op < 3) { + /* update # of requests on the wire to server */ + atomic_inc(&ses->server->inFlight); + } + spin_unlock(&GlobalMid_Lock); + break; + } + } + } + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + down(&ses->server->tcpSem); + + if (ses->server->tcpStatus == CifsExiting) { + rc = -ENOENT; + goto cifs_out_label; + } else if (ses->server->tcpStatus == CifsNeedReconnect) { + cFYI(1,("tcp session dead - return to caller to retry")); + rc = -EAGAIN; + goto cifs_out_label; + } else if (ses->status != CifsGood) { + /* check if SMB session is bad because we are setting it up */ + if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && + (in_buf->Command != SMB_COM_NEGOTIATE)) { + rc = -EAGAIN; + goto cifs_out_label; + } /* else ok - we are setting up session */ + } + midQ = AllocMidQEntry(in_buf, ses); + if (midQ == NULL) { + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -ENOMEM; + } + + if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + up(&ses->server->tcpSem); + cERROR(1, + ("Illegal length, greater than maximum frame, %d ", + in_buf->smb_buf_length)); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -EIO; + } + + /* BB can we sign efficiently in this path? */ + rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); + + midQ->midState = MID_REQUEST_SUBMITTED; +/* rc = smb_send2(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec, + (struct sockaddr *) &(ses->server->addr.sockAddr));*/ + if(rc < 0) { + DeleteMidQEntry(midQ); + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } else + up(&ses->server->tcpSem); +cifs_out_label: + if(midQ) + DeleteMidQEntry(midQ); + + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + + return rc; +} + + +#endif /* CIFS_EXPERIMENTAL */ + int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, @@ -204,11 +341,11 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, } else { spin_lock(&GlobalMid_Lock); while(1) { - if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){ + if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) - < CIFS_MAX_REQ); + < cifs_max_pending); spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { @@ -260,7 +397,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return -ENOMEM; } - if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) { + if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d ", @@ -307,20 +444,19 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, /* if signal pending do not hold up user for full smb timeout but we still give response a change to complete */ timeout = 2 * HZ; - } /* No user interrupts in wait - wreaks havoc with performance */ if(timeout != MAX_SCHEDULE_TIMEOUT) { timeout += jiffies; wait_event(ses->server->response_q, - (midQ->midState & MID_RESPONSE_RECEIVED) || + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || time_after(jiffies, timeout) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } else { wait_event(ses->server->response_q, - (midQ->midState & MID_RESPONSE_RECEIVED) || + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } @@ -358,7 +494,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return rc; } - if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) { + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index ebc69c385d2e..22f909ee7a77 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -20,6 +20,7 @@ */ #include <linux/fs.h> +#include <linux/posix_acl_xattr.h> #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -27,11 +28,16 @@ #include "cifs_debug.h" #define MAX_EA_VALUE_SIZE 65535 -#define CIFS_XATTR_DOS_ATTRIB "user.DOSATTRIB" +#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib" #define CIFS_XATTR_USER_PREFIX "user." #define CIFS_XATTR_SYSTEM_PREFIX "system." -#define CIFS_XATTR_OS2_PREFIX "OS2." /* BB should check for this someday */ -/* also note could add check for security prefix XATTR_SECURITY_PREFIX */ +#define CIFS_XATTR_OS2_PREFIX "os2." +#define CIFS_XATTR_SECURITY_PREFIX ".security" +#define CIFS_XATTR_TRUSTED_PREFIX "trusted." +#define XATTR_TRUSTED_PREFIX_LEN 8 +#define XATTR_SECURITY_PREFIX_LEN 9 +/* BB need to add server (Samba e.g) support for security and trusted prefix */ + int cifs_removexattr(struct dentry * direntry, const char * ea_name) @@ -128,16 +134,47 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, if(ea_name == NULL) { cFYI(1,("Null xattr names not supported")); - } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) { - cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name)); + } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5) == 0) { + if(strncmp(ea_name,CIFS_XATTR_DOS_ATTRIB,14) == 0) { + cFYI(1,("attempt to set cifs inode metadata")); + } + ea_name += 5; /* skip past user. prefix */ + rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value, + (__u16)value_size, cifs_sb->local_nls); + } else if(strncmp(ea_name, CIFS_XATTR_OS2_PREFIX,4) == 0) { + ea_name += 4; /* skip past os2. prefix */ + rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value, + (__u16)value_size, cifs_sb->local_nls); + } else { + int temp; + temp = strncmp(ea_name,POSIX_ACL_XATTR_ACCESS, + strlen(POSIX_ACL_XATTR_ACCESS)); + if (temp == 0) { +#ifdef CONFIG_CIFS_POSIX + rc = CIFSSMBSetPosixACL(xid, pTcon,full_path,ea_value, + (const int)value_size, ACL_TYPE_ACCESS, + cifs_sb->local_nls); + cFYI(1,("set POSIX ACL rc %d",rc)); +#else + cFYI(1,("set POSIX ACL not supported")); +#endif + } else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT,strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { +#ifdef CONFIG_CIFS_POSIX + rc = CIFSSMBSetPosixACL(xid, pTcon,full_path,ea_value, + (const int)value_size, ACL_TYPE_DEFAULT, + cifs_sb->local_nls); + cFYI(1,("set POSIX default ACL rc %d",rc)); +#else + cFYI(1,("set default POSIX ACL not supported")); +#endif + } else { + cFYI(1,("illegal xattr request %s (only user namespace supported)",ea_name)); /* BB what if no namespace prefix? */ /* Should we just pass them to server, except for system and perhaps security prefixes? */ - } else { - ea_name+=5; /* skip past user. prefix */ - rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value, - (__u16)value_size, cifs_sb->local_nls); + } } + if (full_path) kfree(full_path); FreeXid(xid); @@ -163,6 +200,7 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, sb = direntry->d_inode->i_sb; if(sb == NULL) return -EIO; + xid = GetXid(); cifs_sb = CIFS_SB(sb); @@ -177,19 +215,54 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ - if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) { - cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name)); - /* BB what if no namespace prefix? */ - /* Should we just pass them to server, except for system? */ + if(ea_name == NULL) { + cFYI(1,("Null xattr names not supported")); + } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5) == 0) { + if(strncmp(ea_name,CIFS_XATTR_DOS_ATTRIB,14) == 0) { + cFYI(1,("attempt to query cifs inode metadata")); + /* revalidate/getattr then populate from inode */ + } /* BB add else when above is implemented */ + ea_name += 5; /* skip past user. prefix */ + rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, + buf_size, cifs_sb->local_nls); + } else if(strncmp(ea_name, CIFS_XATTR_OS2_PREFIX,4) == 0) { + ea_name += 4; /* skip past os2. prefix */ + rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, + buf_size, cifs_sb->local_nls); + } else if(strncmp(ea_name,POSIX_ACL_XATTR_ACCESS,strlen(POSIX_ACL_XATTR_ACCESS)) == 0) { +#ifdef CONFIG_CIFS_POSIX + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + ea_value, buf_size, ACL_TYPE_ACCESS, + cifs_sb->local_nls); +#else + cFYI(1,("query POSIX ACL not supported yet")); +#endif /* CONFIG_CIFS_POSIX */ + } else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT,strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { +#ifdef CONFIG_CIFS_POSIX + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + ea_value, buf_size, ACL_TYPE_DEFAULT, + cifs_sb->local_nls); +#else + cFYI(1,("query POSIX default ACL not supported yet")); +#endif + } else if(strncmp(ea_name, + CIFS_XATTR_TRUSTED_PREFIX,XATTR_TRUSTED_PREFIX_LEN) == 0) { + cFYI(1,("Trusted xattr namespace not supported yet")); + } else if(strncmp(ea_name, + CIFS_XATTR_SECURITY_PREFIX,XATTR_SECURITY_PREFIX_LEN) == 0) { + cFYI(1,("Security xattr namespace not supported yet")); } else { - /* We could add a check here + cFYI(1,("illegal xattr name request %s (only user namespace supported)",ea_name)); + } + + /* We could add an additional check for streams ie if proc/fs/cifs/streamstoxattr is set then search server for EAs or streams to returns as xattrs */ - ea_name+=5; /* skip past user. */ - rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, - buf_size, cifs_sb->local_nls); - } + + if(rc == -EINVAL) + rc = -EOPNOTSUPP; + if (full_path) kfree(full_path); FreeXid(xid); diff --git a/fs/compat.c b/fs/compat.c index eb289d3e8ce7..4120c9ea6ed2 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1387,25 +1387,25 @@ int compat_do_execve(char * filename, int retval; int i; - file = open_exec(filename); - - retval = PTR_ERR(file); - if (IS_ERR(file)) - return retval; - - sched_exec(); - retval = -ENOMEM; bprm = kmalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) goto out_ret; memset(bprm, 0, sizeof(*bprm)); + file = open_exec(filename); + retval = PTR_ERR(file); + if (IS_ERR(file)) + goto out_kfree; + + sched_exec(); + bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); bprm->file = file; bprm->filename = filename; bprm->interp = filename; bprm->mm = mm_alloc(); + retval = -ENOMEM; if (!bprm->mm) goto out_file; @@ -1472,6 +1472,8 @@ out_file: allow_write_access(bprm->file); fput(bprm->file); } + +out_kfree: kfree(bprm); out_ret: diff --git a/fs/direct-io.c b/fs/direct-io.c index 4cbcff38e014..e370bb2b9da8 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1161,6 +1161,9 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct dio *dio; int reader_with_isem = (rw == READ && dio_lock_type == DIO_OWN_LOCKING); + if (rw & WRITE) + current->flags |= PF_SYNCWRITE; + if (bdev) bdev_blkbits = blksize_bits(bdev_hardsect_size(bdev)); @@ -1244,6 +1247,8 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, out: if (reader_with_isem) up(&inode->i_sem); + if (rw & WRITE) + current->flags &= ~PF_SYNCWRITE; return retval; } EXPORT_SYMBOL(__blockdev_direct_IO); diff --git a/fs/exec.c b/fs/exec.c index dd1c43b6f975..a2e554cdace3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1094,26 +1094,26 @@ int do_execve(char * filename, int retval; int i; - file = open_exec(filename); - - retval = PTR_ERR(file); - if (IS_ERR(file)) - return retval; - - sched_exec(); - retval = -ENOMEM; bprm = kmalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) goto out_ret; memset(bprm, 0, sizeof(*bprm)); + file = open_exec(filename); + retval = PTR_ERR(file); + if (IS_ERR(file)) + goto out_kfree; + + sched_exec(); + bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); bprm->file = file; bprm->filename = filename; bprm->interp = filename; bprm->mm = mm_alloc(); + retval = -ENOMEM; if (!bprm->mm) goto out_file; @@ -1180,6 +1180,8 @@ out_file: allow_write_access(bprm->file); fput(bprm->file); } + +out_kfree: kfree(bprm); out_ret: diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 82f055f3b929..1bb1e0885bfe 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -122,6 +122,9 @@ static void ext2_put_super (struct super_block * sb) brelse (sbi->s_group_desc[i]); kfree(sbi->s_group_desc); kfree(sbi->s_debts); + percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeinodes_counter); + percpu_counter_destroy(&sbi->s_dirs_counter); brelse (sbi->s_sbh); sb->s_fs_info = NULL; kfree(sbi); diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 2e2af5cba1ce..b2bb90817371 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c @@ -418,7 +418,7 @@ static int call_filldir(struct file * filp, void * dirent, get_dtype(sb, fname->file_type)); if (error) { filp->f_pos = curr_pos; - info->extra_fname = fname; + info->extra_fname = fname->next; return error; } fname = fname->next; @@ -457,12 +457,9 @@ static int ext3_dx_readdir(struct file * filp, * If there are any leftover names on the hash collision * chain, return them first. */ - if (info->extra_fname) { - if(call_filldir(filp, dirent, filldir, info->extra_fname)) - goto finished; - else - goto next_entry; - } + if (info->extra_fname && + call_filldir(filp, dirent, filldir, info->extra_fname)) + goto finished; if (!info->curr_node) info->curr_node = rb_first(&info->root); @@ -495,7 +492,7 @@ static int ext3_dx_readdir(struct file * filp, info->curr_minor_hash = fname->minor_hash; if (call_filldir(filp, dirent, filldir, fname)) break; -next_entry: + info->curr_node = rb_next(info->curr_node); if (!info->curr_node) { if (info->next_hash == ~0) { diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index cf359ec81ed6..bfaf8a414da5 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -872,7 +872,7 @@ restart: if (!buffer_uptodate(bh)) { /* read error, skip block & hope for the best */ ext3_error(sb, __FUNCTION__, "reading directory #%lu " - "offset %lu\n", dir->i_ino, block); + "offset %lu", dir->i_ino, block); brelse(bh); goto next; } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 34c4744813e1..6cf25190b03b 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -400,6 +400,9 @@ void ext3_put_super (struct super_block * sb) for (i = 0; i < sbi->s_gdb_count; i++) brelse(sbi->s_group_desc[i]); kfree(sbi->s_group_desc); + percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeinodes_counter); + percpu_counter_destroy(&sbi->s_dirs_counter); brelse(sbi->s_sbh); #ifdef CONFIG_QUOTA for (i = 0; i < MAXQUOTAS; i++) { diff --git a/fs/partitions/check.c b/fs/partitions/check.c index b89b42baf6aa..b41bf7719b0d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -378,7 +378,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (disk->fops->revalidate_disk) disk->fops->revalidate_disk(disk); if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) - return -EIO; + return 0; for (p = 1; p < state->limit; p++) { sector_t size = state->parts[p].size; sector_t from = state->parts[p].from; diff --git a/fs/proc/array.c b/fs/proc/array.c index f5ad3980b0ff..ab3784acd70f 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -370,7 +370,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) stime += task->signal->stime; } } - ppid = task->pid ? task->group_leader->real_parent->tgid : 0; + ppid = pid_alive(task) ? task->group_leader->real_parent->tgid : 0; read_unlock(&tasklist_lock); if (!whole || num_threads<2) diff --git a/include/asm-arm/arch-ixp4xx/io.h b/include/asm-arm/arch-ixp4xx/io.h index ea9f8333d26d..c27b9d3079a7 100644 --- a/include/asm-arm/arch-ixp4xx/io.h +++ b/include/asm-arm/arch-ixp4xx/io.h @@ -292,7 +292,7 @@ __ixp4xx_outb(u8 value, u32 addr) } static inline void -__ixp4xx_outsb(u32 io_addr, u8 *vaddr, u32 count) +__ixp4xx_outsb(u32 io_addr, const u8 *vaddr, u32 count) { while (count--) outb(*vaddr++, io_addr); @@ -309,7 +309,7 @@ __ixp4xx_outw(u16 value, u32 addr) } static inline void -__ixp4xx_outsw(u32 io_addr, u16 *vaddr, u32 count) +__ixp4xx_outsw(u32 io_addr, const u16 *vaddr, u32 count) { while (count--) outw(cpu_to_le16(*vaddr++), io_addr); @@ -322,7 +322,7 @@ __ixp4xx_outl(u32 value, u32 addr) } static inline void -__ixp4xx_outsl(u32 io_addr, u32 *vaddr, u32 count) +__ixp4xx_outsl(u32 io_addr, const u32 *vaddr, u32 count) { while (count--) outl(*vaddr++, io_addr); diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h index 1f36b66be63c..c43cafe17e19 100644 --- a/include/asm-arm/arch-pxa/pxa-regs.h +++ b/include/asm-arm/arch-pxa/pxa-regs.h @@ -1494,7 +1494,7 @@ #define PVCR_CommandDelay (0xf80) #define PCFR_PI2C_EN (0x1 << 6) -#define PSSR_OTGPH (1 << 7) /* OTG Peripheral control Hold */ +#define PSSR_OTGPH (1 << 6) /* OTG Peripheral control Hold */ #define PSSR_RDH (1 << 5) /* Read Disable Hold */ #define PSSR_PH (1 << 4) /* Peripheral Control Hold */ #define PSSR_VFS (1 << 2) /* VDD Fault Status */ diff --git a/include/asm-arm/arch-sa1100/ide.h b/include/asm-arm/arch-sa1100/ide.h index f63b96baa6b9..2153538069c7 100644 --- a/include/asm-arm/arch-sa1100/ide.h +++ b/include/asm-arm/arch-sa1100/ide.h @@ -14,6 +14,8 @@ #include <asm/hardware.h> #include <asm/mach-types.h> +#error "This code is broken and needs update to match with current ide support" + /* * Set up a hw structure for a specified data port, control port and IRQ. diff --git a/include/asm-arm/arch-versatile/platform.h b/include/asm-arm/arch-versatile/platform.h index bc75b3ca0e67..2598d1f08548 100644 --- a/include/asm-arm/arch-versatile/platform.h +++ b/include/asm-arm/arch-versatile/platform.h @@ -61,7 +61,7 @@ #define VERSATILE_SYS_OSC2_OFFSET 0x14 #define VERSATILE_SYS_OSC3_OFFSET 0x18 #define VERSATILE_SYS_OSC4_OFFSET 0x1C -#elif defined(CONFIG_ARCH_VERSATILE_AB) +#elif defined(CONFIG_MACH_VERSATILE_AB) #define VERSATILE_SYS_OSC1_OFFSET 0x1C #endif @@ -494,7 +494,7 @@ #define VERSATILE_CSR_BASE 0x10000000 #define VERSATILE_CSR_SIZE 0x10000000 -#ifdef CONFIG_ARCH_VERSATILE_AB +#ifdef CONFIG_MACH_VERSATILE_AB /* * IB2 Versatile/AB expansion board definitions */ diff --git a/include/asm-arm/ide.h b/include/asm-arm/ide.h index cf9b1b35f8e7..2114acb3d237 100644 --- a/include/asm-arm/ide.h +++ b/include/asm-arm/ide.h @@ -17,10 +17,6 @@ #define MAX_HWIFS 4 #endif -#if defined(CONFIG_ARCH_SA1100) -# include <asm/arch/ide.h> /* obsolete + broken */ -#endif - #if !defined(CONFIG_ARCH_L7200) # define IDE_ARCH_OBSOLETE_INIT # ifdef CONFIG_ARCH_CLPS7500 diff --git a/include/asm-arm/mach/irda.h b/include/asm-arm/mach/irda.h new file mode 100644 index 000000000000..58984d9c0b0b --- /dev/null +++ b/include/asm-arm/mach/irda.h @@ -0,0 +1,20 @@ +/* + * linux/include/asm-arm/mach/irda.h + * + * Copyright (C) 2004 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_MACH_IRDA_H +#define __ASM_ARM_MACH_IRDA_H + +struct irda_platform_data { + int (*startup)(struct device *); + void (*shutdown)(struct device *); + int (*set_power)(struct device *, unsigned int state); + void (*set_speed)(struct device *, unsigned int speed); +}; + +#endif diff --git a/include/asm-i386/io.h b/include/asm-i386/io.h index 7b27cf130b1a..479e976ed86c 100644 --- a/include/asm-i386/io.h +++ b/include/asm-i386/io.h @@ -184,7 +184,7 @@ static inline void memset_io(volatile void __iomem *addr, unsigned char val, int { memset((void __force *) addr, val, count); } -static inline void memcpy_fromio(void *dst, volatile void __iomem *src, int count) +static inline void memcpy_fromio(void *dst, const volatile void __iomem *src, int count) { __memcpy(dst, (void __force *) src, count); } diff --git a/include/asm-ia64/delay.h b/include/asm-ia64/delay.h index aa627cfd88f5..57182d6f2b9a 100644 --- a/include/asm-ia64/delay.h +++ b/include/asm-ia64/delay.h @@ -91,7 +91,7 @@ udelay (unsigned long usecs) unsigned long cycles = usecs*local_cpu_data->cyc_per_usec; while (ia64_get_itc() - start < cycles) - /* skip */; + cpu_relax(); } #endif /* _ASM_IA64_DELAY_H */ diff --git a/include/asm-ppc64/paca.h b/include/asm-ppc64/paca.h index 71de51ac951f..53ee1a758baa 100644 --- a/include/asm-ppc64/paca.h +++ b/include/asm-ppc64/paca.h @@ -99,11 +99,17 @@ struct paca_struct { u64 exdsi[8]; /* used for linear mapping hash table misses */ /* - * iSeries structues which the hypervisor knows about - Not - * sure if these particularly need to be cacheline aligned. + * iSeries structure which the hypervisor knows about - + * this structure should not cross a page boundary. + * The vpa_init/register_vpa call is now known to fail if the + * lppaca structure crosses a page boundary. * The lppaca is also used on POWER5 pSeries boxes. + * The lppaca is 640 bytes long, and cannot readily change + * since the hypervisor knows its layout, so a 1kB + * alignment will suffice to ensure that it doesn't + * cross a page boundary. */ - struct ItLpPaca lppaca __attribute__((aligned(0x80))); + struct ItLpPaca lppaca __attribute__((__aligned__(0x400))); #ifdef CONFIG_PPC_ISERIES struct ItLpRegSave reg_save; #endif diff --git a/include/asm-ppc64/plpar_wrappers.h b/include/asm-ppc64/plpar_wrappers.h index 17452d372024..f4a5fb7d67c7 100644 --- a/include/asm-ppc64/plpar_wrappers.h +++ b/include/asm-ppc64/plpar_wrappers.h @@ -22,12 +22,14 @@ static inline long cede_processor(void) return(0); } -static inline long register_vpa(unsigned long flags, unsigned long proc, unsigned long vpa) +static inline long register_vpa(unsigned long flags, unsigned long proc, + unsigned long vpa) { - plpar_hcall_norets(H_REGISTER_VPA, flags, proc, vpa); - return(0); + return plpar_hcall_norets(H_REGISTER_VPA, flags, proc, vpa); } +void vpa_init(int cpu); + static inline long plpar_pte_remove(unsigned long flags, unsigned long ptex, unsigned long avpn, diff --git a/include/asm-ppc64/ptrace-common.h b/include/asm-ppc64/ptrace-common.h index 3dbd3e5847b3..af03547f9c7e 100644 --- a/include/asm-ppc64/ptrace-common.h +++ b/include/asm-ppc64/ptrace-common.h @@ -58,6 +58,7 @@ static inline void set_single_step(struct task_struct *task) struct pt_regs *regs = task->thread.regs; if (regs != NULL) regs->msr |= MSR_SE; + set_ti_thread_flag(task->thread_info, TIF_SINGLESTEP); } static inline void clear_single_step(struct task_struct *task) @@ -65,6 +66,7 @@ static inline void clear_single_step(struct task_struct *task) struct pt_regs *regs = task->thread.regs; if (regs != NULL) regs->msr &= ~MSR_SE; + clear_ti_thread_flag(task->thread_info, TIF_SINGLESTEP); } #endif /* _PPC64_PTRACE_COMMON_H */ diff --git a/include/asm-ppc64/thread_info.h b/include/asm-ppc64/thread_info.h index 6a8a4c4c334d..595d67ef4b6f 100644 --- a/include/asm-ppc64/thread_info.h +++ b/include/asm-ppc64/thread_info.h @@ -97,6 +97,7 @@ static inline struct thread_info *current_thread_info(void) #define TIF_RUN_LIGHT 6 /* iSeries run light */ #define TIF_ABI_PENDING 7 /* 32/64 bit switch needed */ #define TIF_SYSCALL_AUDIT 8 /* syscall auditing active */ +#define TIF_SINGLESTEP 9 /* singlestepping active */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) @@ -108,6 +109,7 @@ static inline struct thread_info *current_thread_info(void) #define _TIF_RUN_LIGHT (1<<TIF_RUN_LIGHT) #define _TIF_ABI_PENDING (1<<TIF_ABI_PENDING) #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) +#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP) #define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) #define _TIF_USER_WORK_MASK (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \ diff --git a/include/asm-sparc/elf.h b/include/asm-sparc/elf.h index 2b2a5195004f..d17864207d07 100644 --- a/include/asm-sparc/elf.h +++ b/include/asm-sparc/elf.h @@ -143,7 +143,7 @@ typedef struct { the loader. We need to make sure that it is out of the way of the program that it will "exec", and that there is sufficient room for the brk. */ -#define ELF_ET_DYN_BASE (0x08000000) +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE) /* This yields a mask that user programs can use to figure out what instruction set this cpu supports. This can NOT be done in userspace diff --git a/include/asm-sparc/mostek.h b/include/asm-sparc/mostek.h index f88760d9e1df..c6022a5d611d 100644 --- a/include/asm-sparc/mostek.h +++ b/include/asm-sparc/mostek.h @@ -11,6 +11,7 @@ #include <linux/config.h> #include <asm/idprom.h> +#include <asm/io.h> /* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ) * diff --git a/include/asm-sparc/processor.h b/include/asm-sparc/processor.h index 371a105056f3..83feef80c494 100644 --- a/include/asm-sparc/processor.h +++ b/include/asm-sparc/processor.h @@ -43,10 +43,12 @@ struct task_struct; +#ifdef __KERNEL__ struct fpq { unsigned long *insn_addr; unsigned long insn; }; +#endif typedef struct { int seg; diff --git a/include/asm-sparc/sigcontext.h b/include/asm-sparc/sigcontext.h index 86dc000ad681..7fa2c7d01ab4 100644 --- a/include/asm-sparc/sigcontext.h +++ b/include/asm-sparc/sigcontext.h @@ -4,7 +4,6 @@ #ifdef __KERNEL__ #include <asm/ptrace.h> -#endif #ifndef __ASSEMBLY__ @@ -59,4 +58,6 @@ typedef struct { #endif /* !(__ASSEMBLY__) */ +#endif /* (__KERNEL__) */ + #endif /* !(__SPARC_SIGCONTEXT_H) */ diff --git a/include/asm-sparc/signal.h b/include/asm-sparc/signal.h index 98aade6c4616..d8211cb6e6b4 100644 --- a/include/asm-sparc/signal.h +++ b/include/asm-sparc/signal.h @@ -3,6 +3,7 @@ #define _ASMSPARC_SIGNAL_H #include <asm/sigcontext.h> +#include <linux/compiler.h> #ifdef __KERNEL__ #ifndef __ASSEMBLY__ @@ -111,11 +112,14 @@ typedef struct { unsigned long sig[_NSIG_WORDS]; } __new_sigset_t; + +#ifdef __KERNEL__ /* A SunOS sigstack */ struct sigstack { char *the_stack; int cur_status; }; +#endif /* Sigvec flags */ #define _SV_SSTACK 1u /* This signal handler should use sig-stack */ @@ -189,6 +193,7 @@ typedef void (*__sighandler_t)(int); #define SIG_IGN ((__sighandler_t)1) /* ignore signal */ #define SIG_ERR ((__sighandler_t)-1) /* error return from signal */ +#ifdef __KERNEL__ struct __new_sigaction { __sighandler_t sa_handler; unsigned long sa_flags; @@ -196,12 +201,10 @@ struct __new_sigaction { __new_sigset_t sa_mask; }; -#ifdef __KERNEL__ struct k_sigaction { struct __new_sigaction sa; void __user *ka_restorer; }; -#endif struct __old_sigaction { __sighandler_t sa_handler; @@ -216,7 +219,6 @@ typedef struct sigaltstack { size_t ss_size; } stack_t; -#ifdef __KERNEL__ struct sparc_deliver_cookie { int restart_syscall; unsigned long orig_i0; diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index d0300dcff0af..fd0bc530dda8 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -62,7 +62,7 @@ typedef enum fe_caps { FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, - FE_NEEDS_BENDING = 0x20000000, // frontend requires frequency bending + FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output } fe_caps_t; @@ -78,7 +78,7 @@ struct dvb_frontend_info { __u32 symbol_rate_min; __u32 symbol_rate_max; __u32 symbol_rate_tolerance; /* ppm */ - __u32 notifier_delay; /* ms */ + __u32 notifier_delay; /* DEPRECATED */ fe_caps_t caps; }; diff --git a/include/linux/filter.h b/include/linux/filter.h index 17218abef8f1..3ba843c46382 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -8,7 +8,9 @@ #include <linux/compiler.h> #include <linux/types.h> +#ifdef __KERNEL__ #include <asm/atomic.h> +#endif /* * Current version of the filter code architecture. diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index c94de12a5ee1..b5d660089de4 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -1,6 +1,7 @@ #ifndef _LINUX_HDREG_H #define _LINUX_HDREG_H +#ifdef __KERNEL__ #include <linux/ata.h> /* @@ -57,7 +58,7 @@ #define IO 0x02 #define REL 0x04 #define TAG_MASK 0xf8 - +#endif /* __KERNEL__ */ /* * Command Header sizes for IOCTL commands diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 777a80f9f4a3..229bb91364ad 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -976,7 +976,7 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2O_TIMEOUT_MESSAGE_GET 5 #define I2O_TIMEOUT_RESET 30 #define I2O_TIMEOUT_STATUS_GET 5 -#define I2O_TIMEOUT_LCT_GET 20 +#define I2O_TIMEOUT_LCT_GET 360 #define I2O_TIMEOUT_SCSI_SCB_ABORT 240 /* retries */ diff --git a/include/linux/libata.h b/include/linux/libata.h index 605e0a728c0e..95a7b0ddb096 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -112,6 +112,7 @@ enum { ATA_FLAG_SRST = (1 << 5), /* use ATA SRST, not E.D.D. */ ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */ + ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */ ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h index 0ab4590a0b16..61dad5790198 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_tcp.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_tcp.h @@ -18,7 +18,7 @@ enum tcp_conntrack { }; /* Window scaling is advertised by the sender */ -#define IP_CT_TCP_STATE_FLAG_WINDOW_SCALE 0x01 +#define IP_CT_TCP_FLAG_WINDOW_SCALE 0x01 /* SACK is permitted by the sender */ #define IP_CT_TCP_FLAG_SACK_PERM 0x02 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f1946ad646e4..affb193a43e6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -344,6 +344,7 @@ #define PCI_DEVICE_ID_ATI_RS300_200 0x5833 /* ATI IXP Chipset */ #define PCI_DEVICE_ID_ATI_IXP_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP2_IDE 0x4369 /* True name not yet sure */ #define PCI_VENDOR_ID_VLSI 0x1004 #define PCI_DEVICE_ID_VLSI_82C592 0x0005 @@ -1029,6 +1030,7 @@ #define PCI_DEVICE_ID_AL_M3307 0x3307 #define PCI_DEVICE_ID_AL_M4803 0x5215 #define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 #define PCI_DEVICE_ID_AL_M5229 0x5229 #define PCI_DEVICE_ID_AL_M5237 0x5237 #define PCI_DEVICE_ID_AL_M5243 0x5243 diff --git a/include/linux/socket.h b/include/linux/socket.h index b3aef7bf1380..4c7d11301abf 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -90,6 +90,10 @@ struct cmsghdr { (struct cmsghdr *)(ctl) : \ (struct cmsghdr *)NULL) #define CMSG_FIRSTHDR(msg) __CMSG_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) +#define CMSG_OK(mhdr, cmsg) ((cmsg)->cmsg_len >= sizeof(struct cmsghdr) && \ + (cmsg)->cmsg_len <= (unsigned long) \ + ((mhdr)->msg_controllen - \ + ((char *)(cmsg) - (char *)(mhdr)->msg_control))) /* * This mess will go away with glibc diff --git a/include/linux/types.h b/include/linux/types.h index 893c4b367bae..dcb13f865df9 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -157,8 +157,10 @@ typedef __u16 __bitwise __le16; typedef __u16 __bitwise __be16; typedef __u32 __bitwise __le32; typedef __u32 __bitwise __be32; +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) typedef __u64 __bitwise __le64; typedef __u64 __bitwise __be64; +#endif struct ustat { __kernel_daddr_t f_tfree; diff --git a/include/linux/usb_sl811.h b/include/linux/usb_sl811.h new file mode 100644 index 000000000000..4f2d012d7309 --- /dev/null +++ b/include/linux/usb_sl811.h @@ -0,0 +1,26 @@ + +/* + * board initialization should put one of these into dev->platform_data + * and place the sl811hs onto platform_bus named "sl811-hcd". + */ + +struct sl811_platform_data { + unsigned can_wakeup:1; + + /* given port_power, msec/2 after power on till power good */ + u8 potpg; + + /* mA/2 power supplied on this port (max = default = 250) */ + u8 power; + + /* sl811 relies on an external source of VBUS current */ + void (*port_power)(struct device *dev, int is_on); + + /* pulse sl811 nRST (probably with a GPIO) */ + void (*reset)(struct device *dev); + + // some boards need something like these: + // int (*check_overcurrent)(struct device *dev); + // void (*clock_enable)(struct device *dev, int is_on); +}; + diff --git a/include/linux/wait.h b/include/linux/wait.h index 8b3a2b86d92a..ddb0a16f31c9 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -326,8 +326,8 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); wait_queue_t name = { \ .task = current, \ .func = autoremove_wake_function, \ - .task_list = { .next = &name.task_list, \ - .prev = &name.task_list, \ + .task_list = { .next = &(name).task_list, \ + .prev = &(name).task_list, \ }, \ } @@ -338,15 +338,15 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); .task = current, \ .func = wake_bit_function, \ .task_list = \ - LIST_HEAD_INIT(name.wait.task_list), \ + LIST_HEAD_INIT((name).wait.task_list), \ }, \ } #define init_wait(wait) \ do { \ - wait->task = current; \ - wait->func = autoremove_wake_function; \ - INIT_LIST_HEAD(&wait->task_list); \ + (wait)->task = current; \ + (wait)->func = autoremove_wake_function; \ + INIT_LIST_HEAD(&(wait)->task_list); \ } while (0) /** diff --git a/include/media/saa7146.h b/include/media/saa7146.h index 815964d52dd2..171dbb682a0e 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -51,10 +51,12 @@ extern unsigned int saa7146_debug; #define DEB_INT(x) if (0!=(DEBUG_VARIABLE&0x20)) { DEBUG_PROLOG; printk x; } /* interrupt debug messages */ #define DEB_CAP(x) if (0!=(DEBUG_VARIABLE&0x40)) { DEBUG_PROLOG; printk x; } /* capture debug messages */ -#define IER_DISABLE(x,y) \ +#define SAA7146_IER_DISABLE(x,y) \ saa7146_write(x, IER, saa7146_read(x, IER) & ~(y)); -#define IER_ENABLE(x,y) \ +#define SAA7146_IER_ENABLE(x,y) \ saa7146_write(x, IER, saa7146_read(x, IER) | (y)); +#define SAA7146_ISR_CLEAR(x,y) \ + saa7146_write(x, ISR, (y)); struct saa7146_dev; struct saa7146_extension; @@ -168,7 +170,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt); int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length ); char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt); void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data); -int saa7146_wait_for_debi_done(struct saa7146_dev *dev); +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop); /* some memory sizes */ #define SAA7146_I2C_MEM ( 1*PAGE_SIZE) diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 61a509eb1bc6..eddf16aa3e8e 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -219,8 +219,6 @@ extern struct saa7146_use_ops saa7146_vbi_uops; /* resource management functions */ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit); -int saa7146_res_check(struct saa7146_fh *fh, unsigned int bit); -int saa7146_res_locked(struct saa7146_dev *dev, unsigned int bit); void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits); #define RESOURCE_DMA1_HPS 0x1 diff --git a/include/net/act_api.h b/include/net/act_api.h index ea9c442c1a55..749637e1694d 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -8,15 +8,23 @@ #include <net/sch_generic.h> #include <net/pkt_sched.h> +#define tca_gen(name) \ +struct tcf_##name *next; \ + u32 index; \ + int refcnt; \ + int bindcnt; \ + u32 capab; \ + int action; \ + struct tcf_t tm; \ + struct gnet_stats_basic bstats; \ + struct gnet_stats_queue qstats; \ + struct gnet_stats_rate_est rate_est; \ + spinlock_t *stats_lock; \ + spinlock_t lock + struct tcf_police { - struct tcf_police *next; - int refcnt; -#ifdef CONFIG_NET_CLS_ACT - int bindcnt; -#endif - u32 index; - int action; + tca_gen(police); int result; u32 ewma_rate; u32 burst; @@ -24,33 +32,14 @@ struct tcf_police u32 toks; u32 ptoks; psched_time_t t_c; - spinlock_t lock; struct qdisc_rate_table *R_tab; struct qdisc_rate_table *P_tab; - - struct gnet_stats_basic bstats; - struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; - spinlock_t *stats_lock; }; #ifdef CONFIG_NET_CLS_ACT #define ACT_P_CREATED 1 #define ACT_P_DELETED 1 -#define tca_gen(name) \ -struct tcf_##name *next; \ - u32 index; \ - int refcnt; \ - int bindcnt; \ - u32 capab; \ - int action; \ - struct tcf_t tm; \ - struct gnet_stats_basic bstats; \ - struct gnet_stats_queue qstats; \ - struct gnet_stats_rate_est rate_est; \ - spinlock_t *stats_lock; \ - spinlock_t lock struct tcf_act_hdr { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 16dec2fe1762..fe5cc98511ba 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -38,10 +38,20 @@ extern struct proc_dir_entry *proc_bt_hci; /* HCI Core structures */ +struct inquiry_data { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 pscan_mode; + __u8 dev_class[3]; + __u16 clock_offset; + __s8 rssi; +}; + struct inquiry_entry { struct inquiry_entry *next; __u32 timestamp; - struct inquiry_info info; + struct inquiry_data data; }; struct inquiry_cache { @@ -142,6 +152,7 @@ struct hci_conn { __u16 state; __u8 type; __u8 out; + __u8 dev_class[3]; __u32 link_mode; unsigned long pend; @@ -199,7 +210,7 @@ static inline long inquiry_entry_age(struct inquiry_entry *e) } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_info *info); +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); /* ----- HCI Connections ----- */ enum { diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4c441ef94d93..7fee4c0b2353 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -358,6 +358,7 @@ enum { NET_IPV4_VS_EXPIRE_NODEST_CONN=23, NET_IPV4_VS_SYNC_THRESHOLD=24, NET_IPV4_VS_NAT_ICMP_SEND=25, + NET_IPV4_VS_EXPIRE_QUIESCENT_TEMPLATE=26, NET_IPV4_VS_LAST }; @@ -879,6 +880,7 @@ extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, */ extern int sysctl_ip_vs_cache_bypass; extern int sysctl_ip_vs_expire_nodest_conn; +extern int sysctl_ip_vs_expire_quiescent_template; extern int sysctl_ip_vs_sync_threshold[2]; extern int sysctl_ip_vs_nat_icmp_send; extern struct ip_vs_stats ip_vs_stats; diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 2ad20e79a4a3..87496e3aa330 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -228,6 +228,12 @@ extern void qdisc_put_rtab(struct qdisc_rate_table *tab); extern int qdisc_restart(struct net_device *dev); +static inline void qdisc_run(struct net_device *dev) +{ + while (!netif_queue_stopped(dev) && qdisc_restart(dev) < 0) + /* NOTHING */; +} + extern int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res); diff --git a/ipc/shm.c b/ipc/shm.c index f5ca90c8addb..dcdc6d5e22bc 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -511,11 +511,6 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) case SHM_LOCK: case SHM_UNLOCK: { - /* Allow superuser to lock segment in memory */ - if (!can_do_mlock() && cmd == SHM_LOCK) { - err = -EPERM; - goto out; - } shp = shm_lock(shmid); if(shp==NULL) { err = -EINVAL; @@ -525,6 +520,16 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if(err) goto out_unlock; + if (!capable(CAP_IPC_LOCK)) { + err = -EPERM; + if (current->euid != shp->shm_perm.uid && + current->euid != shp->shm_perm.cuid) + goto out_unlock; + if (cmd == SHM_LOCK && + !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur) + goto out_unlock; + } + err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; diff --git a/kernel/exit.c b/kernel/exit.c index 04f797a7d11f..3d786662effd 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1319,6 +1319,10 @@ static long do_wait(pid_t pid, int options, struct siginfo __user *infop, add_wait_queue(¤t->wait_chldexit,&wait); repeat: + /* + * We will set this flag if we see any child that might later + * match our criteria, even if we are not able to reap it yet. + */ flag = 0; current->state = TASK_INTERRUPTIBLE; read_lock(&tasklist_lock); @@ -1337,11 +1341,14 @@ repeat: switch (p->state) { case TASK_TRACED: - flag = 1; if (!my_ptrace_child(p)) continue; /*FALLTHROUGH*/ case TASK_STOPPED: + /* + * It's stopped now, so it might later + * continue, exit, or stop again. + */ flag = 1; if (!(options & WUNTRACED) && !my_ptrace_child(p)) @@ -1377,8 +1384,12 @@ repeat: goto end; break; } - flag = 1; check_continued: + /* + * It's running now, so it might later + * exit, stop, or stop and then continue. + */ + flag = 1; if (!unlikely(options & WCONTINUED)) continue; retval = wait_task_continued( diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6089bbe5df42..0f5dc712ad70 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -43,7 +43,7 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION; * there ain't no turning back. */ -static void power_down(u32 mode) +static void power_down(suspend_disk_method_t mode) { unsigned long flags; int error = 0; diff --git a/kernel/power/main.c b/kernel/power/main.c index dc8a99225f0f..0aefb03ede09 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -4,7 +4,7 @@ * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * - * This file is release under the GPLv2 + * This file is released under the GPLv2 * */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index cd496ed25be4..f9111d76a2b8 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -786,12 +786,13 @@ static int swsusp_alloc(void) int suspend_prepare_image(void) { - unsigned int nr_needed_pages = 0; + unsigned int nr_needed_pages; int error; pr_debug("swsusp: critical section: \n"); if (save_highmem()) { printk(KERN_CRIT "Suspend machine: Not enough free pages for highmem\n"); + restore_highmem(); return -ENOMEM; } @@ -985,6 +986,8 @@ static int __init swsusp_pagedir_relocate(void) c = *c; free_pages((unsigned long)f, pagedir_order); } + if (ret) + return ret; printk("|\n"); return check_pagedir(); } @@ -1128,7 +1131,7 @@ static int __init check_sig(void) */ error = bio_write_page(0, &swsusp_header); } else { - pr_debug(KERN_ERR "swsusp: Invalid partition type.\n"); + pr_debug(KERN_ERR "swsusp: Suspend partition has wrong signature?\n"); return -EINVAL; } if (!error) diff --git a/kernel/sched.c b/kernel/sched.c index c096b7be2808..a356f39ea938 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4446,6 +4446,7 @@ static void sched_domain_debug(void) if (sd->parent) printk(" ERROR !SD_LOAD_BALANCE domain has parent"); printk("\n"); + break; } printk("span %s\n", str); @@ -4454,8 +4455,6 @@ static void sched_domain_debug(void) printk(KERN_DEBUG "ERROR domain->span does not contain CPU%d\n", i); if (!cpu_isset(i, group->cpumask)) printk(KERN_DEBUG "ERROR domain->groups does not contain CPU%d\n", i); - if (!group->cpu_power) - printk(KERN_DEBUG "ERROR domain->cpu_power not set\n"); printk(KERN_DEBUG); for (j = 0; j < level + 2; j++) @@ -4466,6 +4465,9 @@ static void sched_domain_debug(void) printk(" ERROR: NULL"); break; } + + if (!group->cpu_power) + printk(KERN_DEBUG "ERROR group->cpu_power not set\n"); if (!cpus_weight(group->cpumask)) printk(" ERROR empty group:"); diff --git a/mm/highmem.c b/mm/highmem.c index c190cf80af4d..21d2daa37eb0 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -305,14 +305,14 @@ static void copy_to_high_bio_irq(struct bio *to, struct bio *from) } } -static void bounce_end_io(struct bio *bio, mempool_t *pool) +static void bounce_end_io(struct bio *bio, mempool_t *pool, int err) { struct bio *bio_orig = bio->bi_private; struct bio_vec *bvec, *org_vec; - int i, err = 0; + int i; - if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) - err = -EIO; + if (test_bit(BIO_EOPNOTSUPP, &bio->bi_flags)) + set_bit(BIO_EOPNOTSUPP, &bio_orig->bi_flags); /* * free up bounce indirect pages used @@ -334,7 +334,7 @@ static int bounce_end_io_write(struct bio *bio, unsigned int bytes_done,int err) if (bio->bi_size) return 1; - bounce_end_io(bio, page_pool); + bounce_end_io(bio, page_pool, err); return 0; } @@ -343,18 +343,18 @@ static int bounce_end_io_write_isa(struct bio *bio, unsigned int bytes_done, int if (bio->bi_size) return 1; - bounce_end_io(bio, isa_page_pool); + bounce_end_io(bio, isa_page_pool, err); return 0; } -static void __bounce_end_io_read(struct bio *bio, mempool_t *pool) +static void __bounce_end_io_read(struct bio *bio, mempool_t *pool, int err) { struct bio *bio_orig = bio->bi_private; if (test_bit(BIO_UPTODATE, &bio->bi_flags)) copy_to_high_bio_irq(bio_orig, bio); - bounce_end_io(bio, pool); + bounce_end_io(bio, pool, err); } static int bounce_end_io_read(struct bio *bio, unsigned int bytes_done, int err) @@ -362,7 +362,7 @@ static int bounce_end_io_read(struct bio *bio, unsigned int bytes_done, int err) if (bio->bi_size) return 1; - __bounce_end_io_read(bio, page_pool); + __bounce_end_io_read(bio, page_pool, err); return 0; } @@ -371,7 +371,7 @@ static int bounce_end_io_read_isa(struct bio *bio, unsigned int bytes_done, int if (bio->bi_size) return 1; - __bounce_end_io_read(bio, isa_page_pool); + __bounce_end_io_read(bio, isa_page_pool, err); return 0; } diff --git a/mm/mmap.c b/mm/mmap.c index 54f6a2f9966f..cf3e886aff83 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -744,12 +744,12 @@ void __vm_stat_account(struct mm_struct *mm, unsigned long flags, } #endif /* CONFIG_HUGETLB */ - if (file) + if (file) { mm->shared_vm += pages; - else if (flags & stack_flags) + if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC) + mm->exec_vm += pages; + } else if (flags & stack_flags) mm->stack_vm += pages; - if (flags & VM_EXEC) - mm->exec_vm += pages; if (flags & (VM_RESERVED|VM_IO)) mm->reserved_vm += pages; } diff --git a/mm/mprotect.c b/mm/mprotect.c index befda287dff3..80a9e8bf027b 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -111,15 +111,17 @@ change_protection(struct vm_area_struct *vma, unsigned long start, static int mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, - unsigned long start, unsigned long end, unsigned int newflags) + unsigned long start, unsigned long end, unsigned long newflags) { struct mm_struct * mm = vma->vm_mm; + unsigned long oldflags = vma->vm_flags; + long nrpages = (end - start) >> PAGE_SHIFT; unsigned long charged = 0; pgprot_t newprot; pgoff_t pgoff; int error; - if (newflags == vma->vm_flags) { + if (newflags == oldflags) { *pprev = vma; return 0; } @@ -133,8 +135,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, * a MAP_NORESERVE private mapping to writable will now reserve. */ if (newflags & VM_WRITE) { - if (!(vma->vm_flags & (VM_ACCOUNT|VM_WRITE|VM_SHARED|VM_HUGETLB))) { - charged = (end - start) >> PAGE_SHIFT; + if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_SHARED|VM_HUGETLB))) { + charged = nrpages; if (security_vm_enough_memory(charged)) return -ENOMEM; newflags |= VM_ACCOUNT; @@ -176,11 +178,11 @@ success: * vm_flags and vm_page_prot are protected by the mmap_sem * held in write mode. */ - vm_stat_unaccount(vma); vma->vm_flags = newflags; vma->vm_page_prot = newprot; change_protection(vma, start, end, newprot); - vm_stat_account(vma); + __vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); + __vm_stat_account(mm, newflags, vma->vm_file, nrpages); return 0; fail: @@ -246,7 +248,7 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot) prev = vma; for (nstart = start ; ; ) { - unsigned int newflags; + unsigned long newflags; /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ diff --git a/mm/slab.c b/mm/slab.c index 816ad01ed806..bec2eb6ccb72 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2573,6 +2573,7 @@ free_percpu(const void *objp) continue; kfree(p->ptrs[i]); } + kfree(p); } EXPORT_SYMBOL(free_percpu); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 122e9edbdf7e..bacc386111b2 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -71,9 +71,10 @@ void hci_acl_connect(struct hci_conn *conn) if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)) && inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { - cp.pscan_rep_mode = ie->info.pscan_rep_mode; - cp.pscan_mode = ie->info.pscan_mode; - cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000); + cp.pscan_rep_mode = ie->data.pscan_rep_mode; + cp.pscan_mode = ie->data.pscan_mode; + cp.clock_offset = ie->data.clock_offset | __cpu_to_le16(0x8000); + memcpy(conn->dev_class, ie->data.dev_class, 3); } cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 830a7a73bb2b..37690eacff03 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -313,19 +313,19 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b BT_DBG("cache %p, %s", cache, batostr(bdaddr)); for (e = cache->list; e; e = e->next) - if (!bacmp(&e->info.bdaddr, bdaddr)) + if (!bacmp(&e->data.bdaddr, bdaddr)) break; return e; } -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_info *info) +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data) { struct inquiry_cache *cache = &hdev->inq_cache; struct inquiry_entry *e; - BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr)); + BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); - if (!(e = hci_inquiry_cache_lookup(hdev, &info->bdaddr))) { + if (!(e = hci_inquiry_cache_lookup(hdev, &data->bdaddr))) { /* Entry not in the cache. Add new one. */ if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC))) return; @@ -334,7 +334,7 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_info *info) cache->list = e; } - memcpy(&e->info, info, sizeof(*info)); + memcpy(&e->data, data, sizeof(*data)); e->timestamp = jiffies; cache->timestamp = jiffies; } @@ -346,8 +346,16 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) struct inquiry_entry *e; int copied = 0; - for (e = cache->list; e && copied < num; e = e->next, copied++) - memcpy(info++, &e->info, sizeof(*info)); + for (e = cache->list; e && copied < num; e = e->next, copied++) { + struct inquiry_data *data = &e->data; + bacpy(&info->bdaddr, &data->bdaddr); + info->pscan_rep_mode = data->pscan_rep_mode; + info->pscan_period_mode = data->pscan_period_mode; + info->pscan_mode = data->pscan_mode; + memcpy(info->dev_class, data->dev_class, 3); + info->clock_offset = data->clock_offset; + info++; + } BT_DBG("cache %p, copied %d", cache, copied); return copied; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 31b1ceb38f26..e88d18da3b4f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -491,8 +491,18 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * BT_DBG("%s num_rsp %d", hdev->name, num_rsp); hci_dev_lock(hdev); - for (; num_rsp; num_rsp--) - hci_inquiry_cache_update(hdev, info++); + for (; num_rsp; num_rsp--) { + struct inquiry_data data; + bacpy(&data.bdaddr, &info->bdaddr); + data.pscan_rep_mode = info->pscan_rep_mode; + data.pscan_period_mode = info->pscan_period_mode; + data.pscan_mode = info->pscan_mode; + memcpy(data.dev_class, info->dev_class, 3); + data.clock_offset = info->clock_offset; + data.rssi = 0x00; + info++; + hci_inquiry_cache_update(hdev, &data); + } hci_dev_unlock(hdev); } @@ -506,15 +516,16 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); for (; num_rsp; num_rsp--) { - struct inquiry_info tmp; - bacpy(&tmp.bdaddr, &info->bdaddr); - tmp.pscan_rep_mode = info->pscan_rep_mode; - tmp.pscan_period_mode = info->pscan_period_mode; - tmp.pscan_mode = 0x00; - memcpy(tmp.dev_class, &info->dev_class, 3); - tmp.clock_offset = info->clock_offset; + struct inquiry_data data; + bacpy(&data.bdaddr, &info->bdaddr); + data.pscan_rep_mode = info->pscan_rep_mode; + data.pscan_period_mode = info->pscan_period_mode; + data.pscan_mode = 0x00; + memcpy(data.dev_class, info->dev_class, 3); + data.clock_offset = info->clock_offset; + data.rssi = info->rssi; info++; - hci_inquiry_cache_update(hdev, &tmp); + hci_inquiry_cache_update(hdev, &data); } hci_dev_unlock(hdev); } @@ -544,6 +555,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk return; } } + memcpy(conn->dev_class, ev->dev_class, 3); conn->state = BT_CONNECT; hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 5a6b7fdaee25..0fc90e3ebc78 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -48,14 +48,14 @@ static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf) hci_dev_lock_bh(hdev); for (e = cache->list; e; e = e->next) { - struct inquiry_info *info = &e->info; + struct inquiry_data *data = &e->data; bdaddr_t bdaddr; - baswap(&bdaddr, &info->bdaddr); - n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x 0x%.2x %u\n", + baswap(&bdaddr, &data->bdaddr); + n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %u\n", batostr(&bdaddr), - info->pscan_rep_mode, info->pscan_period_mode, info->pscan_mode, - info->dev_class[2], info->dev_class[1], info->dev_class[0], - info->clock_offset, 0, e->timestamp); + data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, + data->dev_class[2], data->dev_class[1], data->dev_class[0], + data->clock_offset, data->rssi, e->timestamp); } hci_dev_unlock_bh(hdev); diff --git a/net/compat.c b/net/compat.c index 69524b497f01..e80e0cebf464 100644 --- a/net/compat.c +++ b/net/compat.c @@ -124,6 +124,12 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, (struct compat_cmsghdr __user *)((msg)->msg_control) : \ (struct compat_cmsghdr __user *)NULL) +#define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \ + ((ucmlen) >= sizeof(struct cmsghdr) && \ + (ucmlen) <= (unsigned long) \ + ((mhdr)->msg_controllen - \ + ((char *)(ucmsg) - (char *)(mhdr)->msg_control))) + static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg, struct compat_cmsghdr __user *cmsg, int cmsg_len) { @@ -154,11 +160,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, return -EFAULT; /* Catch bogons. */ - if(CMSG_COMPAT_ALIGN(ucmlen) < - CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr))) - return -EINVAL; - if((unsigned long)(((char __user *)ucmsg - (char __user *)kmsg->msg_control) - + ucmlen) > kmsg->msg_controllen) + if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) return -EINVAL; tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + diff --git a/net/core/dev.c b/net/core/dev.c index ad6f08aeea80..8453a1afb036 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1202,13 +1202,6 @@ int __skb_linearize(struct sk_buff *skb, int gfp_mask) } \ } -static inline void qdisc_run(struct net_device *dev) -{ - while (!netif_queue_stopped(dev) && - qdisc_restart(dev)<0) - /* NOTHING */; -} - /** * dev_queue_xmit - transmit a buffer * @skb: buffer to transmit diff --git a/net/core/scm.c b/net/core/scm.c index 3699df388ebe..a2ebf30f6aa8 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -127,9 +127,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) for too short ancillary data object at all! Oops. OK, let's add it... */ - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) + if (!CMSG_OK(msg, cmsg)) goto error; if (cmsg->cmsg_level != SOL_SOCKET) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d127c16746bf..56872058ebce 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -821,6 +821,31 @@ struct proto_ops inet_dgram_ops = { .sendpage = inet_sendpage, }; +/* + * For SOCK_RAW sockets; should be the same as inet_dgram_ops but without + * udp_poll + */ +static struct proto_ops inet_sockraw_ops = { + .family = PF_INET, + .owner = THIS_MODULE, + .release = inet_release, + .bind = inet_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = inet_getname, + .poll = datagram_poll, + .ioctl = inet_ioctl, + .listen = sock_no_listen, + .shutdown = inet_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, + .sendpage = inet_sendpage, +}; + static struct net_proto_family inet_family_ops = { .family = PF_INET, .create = inet_create, @@ -861,7 +886,7 @@ static struct inet_protosw inetsw_array[] = .type = SOCK_RAW, .protocol = IPPROTO_IP, /* wild card */ .prot = &raw_prot, - .ops = &inet_dgram_ops, + .ops = &inet_sockraw_ops, .capability = CAP_NET_RAW, .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 312186eb6100..fc0c27f25f6a 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1778,12 +1778,12 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct goto done; rv = !0; for (i=0; i<psl->sl_count; i++) { - rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr, + rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, sizeof(__u32)); - if (rv >= 0) + if (rv == 0) break; } - if (!rv) /* source not found */ + if (rv) /* source not found */ goto done; /* update the interface filter */ @@ -1825,9 +1825,9 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct } rv = 1; /* > 0 for insert logic below if sl_count is 0 */ for (i=0; i<psl->sl_count; i++) { - rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr, + rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, sizeof(__u32)); - if (rv >= 0) + if (rv == 0) break; } if (rv == 0) /* address already there is an error */ diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 461bbd032f34..6d89f3f3e701 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -515,6 +515,8 @@ int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, in kfree(opt); return -EINVAL; } + if (*optp) + kfree(*optp); *optp = opt; return 0; } diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8bb874be141a..d352252326c1 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -146,11 +146,8 @@ int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) return -EINVAL; - } if (cmsg->cmsg_level != SOL_IP) continue; switch (cmsg->cmsg_type) { diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c index 0b9407b0ecbf..95f4568f8afa 100644 --- a/net/ipv4/ipvs/ip_vs_conn.c +++ b/net/ipv4/ipvs/ip_vs_conn.c @@ -453,7 +453,9 @@ int ip_vs_check_template(struct ip_vs_conn *ct) * Checking the dest server status. */ if ((dest == NULL) || - !(dest->flags & IP_VS_DEST_F_AVAILABLE)) { + !(dest->flags & IP_VS_DEST_F_AVAILABLE) || + (sysctl_ip_vs_expire_quiescent_template && + (atomic_read(&dest->weight) == 0))) { IP_VS_DBG(9, "check_template: dest not available for " "protocol %s s:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d " "-> d:%u.%u.%u.%u:%d\n", diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index 836ef846b780..47388b29affd 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -75,6 +75,7 @@ static int sysctl_ip_vs_amemthresh = 1024; static int sysctl_ip_vs_am_droprate = 10; int sysctl_ip_vs_cache_bypass = 0; int sysctl_ip_vs_expire_nodest_conn = 0; +int sysctl_ip_vs_expire_quiescent_template = 0; int sysctl_ip_vs_sync_threshold[2] = { 3, 50 }; int sysctl_ip_vs_nat_icmp_send = 0; @@ -1447,9 +1448,9 @@ static struct ctl_table vs_vars[] = { { .ctl_name = NET_IPV4_VS_TO_ES, .procname = "timeout_established", - .data = &vs_timeout_table_dos.timeout[IP_VS_S_ESTABLISHED], + .data = &vs_timeout_table_dos.timeout[IP_VS_S_ESTABLISHED], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1457,7 +1458,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_synsent", .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_SENT], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1465,7 +1466,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_synrecv", .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_RECV], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1473,7 +1474,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_finwait", .data = &vs_timeout_table_dos.timeout[IP_VS_S_FIN_WAIT], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1489,7 +1490,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_close", .data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1497,7 +1498,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_closewait", .data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE_WAIT], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1505,7 +1506,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_lastack", .data = &vs_timeout_table_dos.timeout[IP_VS_S_LAST_ACK], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1513,7 +1514,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_listen", .data = &vs_timeout_table_dos.timeout[IP_VS_S_LISTEN], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1521,7 +1522,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_synack", .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYNACK], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1529,7 +1530,7 @@ static struct ctl_table vs_vars[] = { .procname = "timeout_udp", .data = &vs_timeout_table_dos.timeout[IP_VS_S_UDP], .maxlen = sizeof(int), - .mode = 0644, + .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, { @@ -1558,6 +1559,14 @@ static struct ctl_table vs_vars[] = { .proc_handler = &proc_dointvec, }, { + .ctl_name = NET_IPV4_VS_EXPIRE_QUIESCENT_TEMPLATE, + .procname = "expire_quiescent_template", + .data = &sysctl_ip_vs_expire_quiescent_template, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = NET_IPV4_VS_SYNC_THRESHOLD, .procname = "sync_threshold", .data = &sysctl_ip_vs_sync_threshold, diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c index 8038cadeaa94..2d6ffa497997 100644 --- a/net/ipv4/netfilter/ip_conntrack_ftp.c +++ b/net/ipv4/netfilter/ip_conntrack_ftp.c @@ -381,6 +381,7 @@ static int help(struct sk_buff *skb, problem (DMZ machines opening holes to internal networks, or the packet filter itself). */ if (!loose) { + ip_conntrack_expect_put(exp); ret = NF_ACCEPT; goto out; } diff --git a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c index 2a048b527ec5..ec79c08a13fd 100644 --- a/net/ipv4/netfilter/ip_conntrack_irc.c +++ b/net/ipv4/netfilter/ip_conntrack_irc.c @@ -65,8 +65,8 @@ struct module *ip_conntrack_irc = THIS_MODULE; #define DEBUGP(format, args...) #endif -int parse_dcc(char *data, char *data_end, u_int32_t * ip, u_int16_t * port, - char **ad_beg_p, char **ad_end_p) +static int parse_dcc(char *data, char *data_end, u_int32_t *ip, + u_int16_t *port, char **ad_beg_p, char **ad_end_p) /* tries to get the ip_addr and port out of a dcc command return value: -1 on failure, 0 on success data pointer to first byte of DCC command data diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c index 265b583d2f99..fb4d59acd9f0 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c @@ -273,9 +273,9 @@ static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { * sCL -> sCL */ /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ -/*ack*/ { sIV, sIV, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/*ack*/ { sIV, sIG, sIV, sES, sCW, sCW, sTW, sTW, sCL, sIV }, /* - * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSS -> sIG Might be a half-open connection. * sSR -> sIV Simultaneous open. * sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. @@ -436,7 +436,7 @@ static void tcp_options(const struct sk_buff *skb, state->td_scale = 14; } state->flags |= - IP_CT_TCP_STATE_FLAG_WINDOW_SCALE; + IP_CT_TCP_FLAG_WINDOW_SCALE; } ptr += opsize - 2; length -= opsize; @@ -552,8 +552,8 @@ static int tcp_in_window(struct ip_ct_tcp *state, * Both sides must send the Window Scale option * to enable window scaling in either direction. */ - if (!(sender->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE - && receiver->flags & IP_CT_TCP_STATE_FLAG_WINDOW_SCALE)) + if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE + && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE)) sender->td_scale = receiver->td_scale = 0; } else { @@ -566,9 +566,11 @@ static int tcp_in_window(struct ip_ct_tcp *state, sender->td_maxwin = (win == 0 ? 1 : win); sender->td_maxend = end + sender->td_maxwin; } - } else if (state->state == TCP_CONNTRACK_SYN_SENT - && dir == IP_CT_DIR_ORIGINAL - && after(end, sender->td_end)) { + } else if (((state->state == TCP_CONNTRACK_SYN_SENT + && dir == IP_CT_DIR_ORIGINAL) + || (state->state == TCP_CONNTRACK_SYN_RECV + && dir == IP_CT_DIR_REPLY)) + && after(end, sender->td_end)) { /* * RFC 793: "if a TCP is reinitialized ... then it need * not wait at all; it must only be sure to use sequence @@ -685,7 +687,7 @@ static int tcp_in_window(struct ip_ct_tcp *state, "ip_ct_tcp: %s ", before(end, sender->td_maxend + 1) ? after(seq, sender->td_end - receiver->td_maxwin - 1) ? - before(ack, receiver->td_end + 1) ? + before(sack, receiver->td_end + 1) ? after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG" : "ACK is under the lower bound (possibly overly delayed ACK)" : "ACK is over the upper bound (ACKed data has never seen yet)" @@ -846,7 +848,9 @@ static int tcp_packet(struct ip_conntrack *conntrack, switch (new_state) { case TCP_CONNTRACK_IGNORE: - /* Either SYN in ORIGINAL, or SYN/ACK in REPLY direction. */ + /* Either SYN in ORIGINAL + * or SYN/ACK in REPLY + * or ACK in REPLY direction (half-open connection). */ if (index == TCP_SYNACK_SET && conntrack->proto.tcp.last_index == TCP_SYN_SET && conntrack->proto.tcp.last_dir != dir @@ -875,7 +879,7 @@ static int tcp_packet(struct ip_conntrack *conntrack, WRITE_UNLOCK(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, - "ip_ct_tcp: invalid SYN (ignored) "); + "ip_ct_tcp: invalid packet ignored "); return NF_ACCEPT; case TCP_CONNTRACK_MAX: /* Invalid packet */ @@ -900,11 +904,12 @@ static int tcp_packet(struct ip_conntrack *conntrack, break; case TCP_CONNTRACK_CLOSE: if (index == TCP_RST_SET - && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) - && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET + && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) + && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET) + || conntrack->proto.tcp.last_index == TCP_ACK_SET) && after(ntohl(th->ack_seq), conntrack->proto.tcp.last_seq)) { - /* Ignore RST closing down invalid SYN + /* Ignore RST closing down invalid SYN or ACK we had let trough. */ WRITE_UNLOCK(&tcp_lock); if (LOG_INVALID(IPPROTO_TCP)) diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index 2055fd40014f..083f0327cf27 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -741,6 +741,7 @@ static int init_or_cleanup(int init) goto cleanup_nothing; #ifdef CONFIG_PROC_FS + ret = -ENOMEM; proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops); if (!proc) goto cleanup_init; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e8769f30f0c6..fd9e0535bad5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3028,7 +3028,7 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_opt *tp, int estab) tp->snd_wscale = *(__u8 *)ptr; if(tp->snd_wscale > 14) { if(net_ratelimit()) - printk("tcp_parse_options: Illegal window " + printk(KERN_INFO "tcp_parse_options: Illegal window " "scaling value %d >14 received.", tp->snd_wscale); tp->snd_wscale = 14; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 1d10fb1409f8..fac7d9cdf238 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -524,11 +524,33 @@ extern void ipv6_sysctl_register(void); extern void ipv6_sysctl_unregister(void); #endif +/* Same as inet6_dgram_ops, sans udp_poll. */ +static struct proto_ops inet6_sockraw_ops = { + .family = PF_INET6, + .owner = THIS_MODULE, + .release = inet6_release, + .bind = inet6_bind, + .connect = inet_dgram_connect, /* ok */ + .socketpair = sock_no_socketpair, /* a do nothing */ + .accept = sock_no_accept, /* a do nothing */ + .getname = inet6_getname, + .poll = datagram_poll, /* ok */ + .ioctl = inet6_ioctl, /* must change */ + .listen = sock_no_listen, /* ok */ + .shutdown = inet_shutdown, /* ok */ + .setsockopt = sock_common_setsockopt, /* ok */ + .getsockopt = sock_common_getsockopt, /* ok */ + .sendmsg = inet_sendmsg, /* ok */ + .recvmsg = sock_common_recvmsg, /* ok */ + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + static struct inet_protosw rawv6_protosw = { .type = SOCK_RAW, .protocol = IPPROTO_IP, /* wild card */ .prot = &rawv6_prot, - .ops = &inet6_dgram_ops, + .ops = &inet6_sockraw_ops, .capability = CAP_NET_RAW, .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 770284e5e7c9..b077cd19f576 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -427,9 +427,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, int addr_type; struct net_device *dev = NULL; - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) { err = -EINVAL; goto exit_f; } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index b3243770978f..2cd84b9f96d9 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -391,12 +391,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk, goto done; rv = !0; for (i=0; i<psl->sl_count; i++) { - rv = memcmp(&psl->sl_addr, group, + rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr)); - if (rv >= 0) + if (rv == 0) break; } - if (!rv) /* source not found */ + if (rv) /* source not found */ goto done; /* update the interface filter */ @@ -437,8 +437,8 @@ int ip6_mc_source(int add, int omode, struct sock *sk, } rv = 1; /* > 0 for insert logic below if sl_count is 0 */ for (i=0; i<psl->sl_count; i++) { - rv = memcmp(&psl->sl_addr, group, sizeof(struct in6_addr)); - if (rv >= 0) + rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr)); + if (rv == 0) break; } if (rv == 0) /* address already there is an error */ diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index aa822e72afb8..d4eaaa8245a4 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -943,7 +943,7 @@ static void ndisc_recv_rs(struct sk_buff *skb) } /* Don't accept RS if we're not in router mode */ - if (!idev->cnf.forwarding || idev->cnf.accept_ra) + if (!idev->cnf.forwarding) goto out; /* diff --git a/net/sched/ipt.c b/net/sched/ipt.c index 3b6842c21307..386d948ab1b2 100644 --- a/net/sched/ipt.c +++ b/net/sched/ipt.c @@ -63,8 +63,7 @@ init_targ(struct tcf_ipt *p) target = __ipt_find_target_lock(t->u.user.name, &ret); if (!target) { - printk("init_targ: Failed to find %s\n", - t->u.kernel.target->name); + printk("init_targ: Failed to find %s\n", t->u.user.name); return -1; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 4627b8af94b3..1e2deaf77783 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -258,12 +258,13 @@ static void netem_watchdog(unsigned long arg) { struct Qdisc *sch = (struct Qdisc *)arg; struct netem_sched_data *q = qdisc_priv(sch); + struct net_device *dev = sch->dev; struct sk_buff *skb; psched_time_t now; pr_debug("netem_watchdog: fired @%lu\n", jiffies); - spin_lock_bh(&sch->dev->queue_lock); + spin_lock_bh(&dev->queue_lock); PSCHED_GET_TIME(now); while ((skb = skb_peek(&q->delayed)) != NULL) { @@ -286,7 +287,8 @@ static void netem_watchdog(unsigned long arg) else sch->q.qlen++; } - spin_unlock_bh(&sch->dev->queue_lock); + qdisc_run(dev); + spin_unlock_bh(&dev->queue_lock); } static void netem_reset(struct Qdisc *sch) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2686c2dbabd2..d9967aaefecf 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4098,12 +4098,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg, for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) { - /* Check for minimum length. The SCM code has this check. */ - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || - (unsigned long)(((char*)cmsg - (char*)msg->msg_control) - + cmsg->cmsg_len) > msg->msg_controllen) { + if (!CMSG_OK(msg, cmsg)) return -EINVAL; - } /* Should we parse this header or ignore? */ if (cmsg->cmsg_level != IPPROTO_SCTP) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 1c2ddfd44809..b503741679d3 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3099,6 +3099,7 @@ int snd_pcm_lib_mmap_iomem(snd_pcm_substream_t *substream, struct vm_area_struct area->vm_page_prot = pgprot_noncached(area->vm_page_prot); #endif area->vm_ops = &snd_pcm_vm_ops_data_mmio; + area->vm_private_data = substream; area->vm_flags |= VM_IO; size = area->vm_end - area->vm_start; offset = area->vm_pgoff << PAGE_SHIFT; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index ee3c6b5d04ef..f186ac70959a 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1932,6 +1932,7 @@ static int ali_suspend(snd_card_t *card, unsigned int state) outl(0xffffffff, ALI_REG(chip, ALI_STOP)); spin_unlock_irq(&chip->reg_lock); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -1986,6 +1987,7 @@ static int snd_ali_free(ali_t * codec) } if (codec->port) pci_release_regions(codec->pci); + pci_disable_device(codec->pci); #ifdef CONFIG_PM if (codec->image) kfree(codec->image); @@ -2093,11 +2095,14 @@ static int __devinit snd_ali_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x7fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x7fffffff) < 0) { snd_printk("architecture does not support 31bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } - if ((codec = kcalloc(1, sizeof(*codec), GFP_KERNEL)) == NULL) + if ((codec = kcalloc(1, sizeof(*codec), GFP_KERNEL)) == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&codec->reg_lock); spin_lock_init(&codec->voice_alloc); diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 992c97320f69..7be0f0d6d068 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -582,6 +582,7 @@ static void snd_card_als4000_free( snd_card_t *card ) } #endif pci_release_regions(acard->pci); + pci_disable_device(acard->pci); } static int __devinit snd_card_als4000_probe(struct pci_dev *pci, @@ -612,11 +613,14 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci, if (pci_set_dma_mask(pci, 0x00ffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) { snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } - if ((err = pci_request_regions(pci, "ALS4000")) < 0) + if ((err = pci_request_regions(pci, "ALS4000")) < 0) { + pci_disable_device(pci); return err; + } gcr = pci_resource_start(pci, 0); pci_read_config_word(pci, PCI_COMMAND, &word); @@ -627,6 +631,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci, sizeof( snd_card_als4000_t ) ); if (card == NULL) { pci_release_regions(pci); + pci_disable_device(pci); return -ENOMEM; } diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 6129fa79c4bb..36010940e1d1 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1420,6 +1420,7 @@ static int snd_atiixp_resume(snd_card_t *card, unsigned int state) pci_enable_device(chip->pci); pci_set_power_state(chip->pci, 0); + pci_set_master(chip->pci); snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); @@ -1473,6 +1474,7 @@ static int snd_atiixp_free(atiixp_t *chip) if (chip->remap_addr) iounmap(chip->remap_addr); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -1500,8 +1502,10 @@ static int __devinit snd_atiixp_create(snd_card_t *card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); @@ -1510,6 +1514,7 @@ static int __devinit snd_atiixp_create(snd_card_t *card, chip->pci = pci; chip->irq = -1; if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) { + pci_disable_device(pci); kfree(chip); return err; } diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index da18e00bd887..e1ed2e3a87fb 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1142,6 +1142,7 @@ static int snd_atiixp_resume(snd_card_t *card, unsigned int state) pci_enable_device(chip->pci); pci_set_power_state(chip->pci, 0); + pci_set_master(chip->pci); snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); @@ -1195,6 +1196,7 @@ static int snd_atiixp_free(atiixp_t *chip) if (chip->remap_addr) iounmap(chip->remap_addr); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -1222,8 +1224,10 @@ static int __devinit snd_atiixp_create(snd_card_t *card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); @@ -1233,6 +1237,7 @@ static int __devinit snd_atiixp_create(snd_card_t *card, chip->irq = -1; if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->addr = pci_resource_start(pci, 0); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index bc7c0346cc9f..02c835a31604 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1268,6 +1268,7 @@ static int snd_azf3328_free(azf3328_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -1317,8 +1318,10 @@ static int __devinit snd_azf3328_create(snd_card_t * card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; @@ -1328,11 +1331,13 @@ static int __devinit snd_azf3328_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x00ffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) { snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 171699e87b70..83b7546ee6fd 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -648,6 +648,7 @@ static int snd_bt87x_free(bt87x_t *chip) if (chip->irq >= 0) free_irq(chip->irq, chip); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -693,8 +694,10 @@ static int __devinit snd_bt87x_create(snd_card_t *card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (!chip) + if (!chip) { + pci_disable_device(pci); return -ENOMEM; + } chip->card = card; chip->pci = pci; chip->irq = -1; @@ -702,6 +705,7 @@ static int __devinit snd_bt87x_create(snd_card_t *card, if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->mmio = ioremap_nocache(pci_resource_start(pci, 0), diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 02864e938555..df4d04f7c201 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2550,6 +2550,7 @@ static int snd_cmipci_free(cmipci_t *cm) } #endif pci_release_regions(cm->pci); + pci_disable_device(cm->pci); kfree(cm); return 0; } @@ -2583,8 +2584,10 @@ static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci, return err; cm = kcalloc(1, sizeof(*cm), GFP_KERNEL); - if (cm == NULL) + if (cm == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&cm->reg_lock); init_MUTEX(&cm->open_mutex); @@ -2598,6 +2601,7 @@ static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci, if ((err = pci_request_regions(pci, card->driver)) < 0) { kfree(cm); + pci_disable_device(pci); return err; } cm->iobase = pci_resource_start(pci, 0); diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index c2d4a6a92659..d1ac0dd2814f 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1362,6 +1362,7 @@ static int snd_cs4281_free(cs4281_t *chip) if (chip->ba1) iounmap(chip->ba1); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -1395,8 +1396,10 @@ static int __devinit snd_cs4281_create(snd_card_t * card, if ((err = pci_enable_device(pci)) < 0) return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; @@ -1410,6 +1413,7 @@ static int __devinit snd_cs4281_create(snd_card_t * card, if ((err = pci_request_regions(pci, "CS4281")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->ba0_addr = pci_resource_start(pci, 0); @@ -2074,6 +2078,7 @@ static int cs4281_suspend(snd_card_t *card, unsigned int state) ulCLK &= ~CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -2085,6 +2090,7 @@ static int cs4281_resume(snd_card_t *card, unsigned int state) u32 ulCLK; pci_enable_device(chip->pci); + pci_set_master(chip->pci); ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK |= CLKCR1_CKRA; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 296e0650ffae..565c45b5a0e2 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2885,6 +2885,7 @@ static int snd_cs46xx_free(cs46xx_t *chip) } #endif + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -3678,6 +3679,7 @@ static int snd_cs46xx_suspend(snd_card_t *card, unsigned int state) /* disable CLKRUN */ chip->active_ctrl(chip, -chip->amplifier); chip->amplifier = amp_saved; /* restore the status */ + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -3688,6 +3690,7 @@ static int snd_cs46xx_resume(snd_card_t *card, unsigned int state) int amp_saved; pci_enable_device(chip->pci); + pci_set_master(chip->pci); amp_saved = chip->amplifier; chip->amplifier = 0; chip->active_ctrl(chip, 1); /* force to on */ @@ -3744,8 +3747,10 @@ int __devinit snd_cs46xx_create(snd_card_t * card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); #ifdef CONFIG_SND_CS46XX_NEW_DSP init_MUTEX(&chip->spos_mutex); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 4f7a0ea60754..45f3ceda5c3c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -561,6 +561,7 @@ static int snd_emu10k1_free(emu10k1_t *emu) free_irq(emu->irq, (void *)emu); if (emu->port) pci_release_regions(emu->pci); + pci_disable_device(emu->pci); kfree(emu); return 0; } @@ -596,14 +597,17 @@ int __devinit snd_emu10k1_create(snd_card_t * card, return err; emu = kcalloc(1, sizeof(*emu), GFP_KERNEL); - if (emu == NULL) + if (emu == NULL) { + pci_disable_device(pci); return -ENOMEM; + } /* set the DMA transfer mask */ emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK; if (pci_set_dma_mask(pci, emu->dma_mask) < 0 || pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) { snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask); kfree(emu); + pci_disable_device(pci); return -ENXIO; } emu->card = card; @@ -629,6 +633,7 @@ int __devinit snd_emu10k1_create(snd_card_t * card, if ((err = pci_request_regions(pci, "EMU10K1")) < 0) { kfree(emu); + pci_disable_device(pci); return err; } emu->port = pci_resource_start(pci, 0); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 70d07014b675..d2d9d4c5c979 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1839,6 +1839,7 @@ static int snd_ensoniq_free(ensoniq_t *ensoniq) if (ensoniq->irq >= 0) free_irq(ensoniq->irq, (void *)ensoniq); pci_release_regions(ensoniq->pci); + pci_disable_device(ensoniq->pci); kfree(ensoniq); return 0; } @@ -1893,8 +1894,10 @@ static int __devinit snd_ensoniq_create(snd_card_t * card, if ((err = pci_enable_device(pci)) < 0) return err; ensoniq = kcalloc(1, sizeof(*ensoniq), GFP_KERNEL); - if (ensoniq == NULL) + if (ensoniq == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&ensoniq->reg_lock); init_MUTEX(&ensoniq->src_mutex); ensoniq->card = card; @@ -1902,6 +1905,7 @@ static int __devinit snd_ensoniq_create(snd_card_t * card, ensoniq->irq = -1; if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) { kfree(ensoniq); + pci_disable_device(pci); return err; } ensoniq->port = pci_resource_start(pci, 0); diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index d056ce3675fe..4ef94409b9c4 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1394,6 +1394,7 @@ static int es1938_suspend(snd_card_t *card, unsigned int state) outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -1432,6 +1433,7 @@ static int snd_es1938_free(es1938_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -1461,18 +1463,22 @@ static int __devinit snd_es1938_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x00ffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) { snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->mixer_lock); chip->card = card; chip->pci = pci; if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->io_port = pci_resource_start(pci, 0); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 3e568cb2ea18..6a6979263fe2 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2411,6 +2411,7 @@ static int es1968_suspend(snd_card_t *card, unsigned int state) snd_ac97_suspend(chip->ac97); snd_es1968_bob_stop(chip); snd_es1968_set_acpi(chip, ACPI_D3); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -2424,6 +2425,7 @@ static int es1968_resume(snd_card_t *card, unsigned int state) /* restore all our config */ pci_enable_device(chip->pci); + pci_set_master(chip->pci); snd_es1968_chip_init(chip); /* need to restore the base pointers.. */ @@ -2467,6 +2469,7 @@ static int snd_es1968_free(es1968_t *chip) chip->master_switch = NULL; chip->master_volume = NULL; pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -2518,12 +2521,15 @@ static int __devinit snd_es1968_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x0fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) { snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (! chip) + if (! chip) { + pci_disable_device(pci); return -ENOMEM; + } /* Set Vars */ chip->type = chip_type; @@ -2543,6 +2549,7 @@ static int __devinit snd_es1968_create(snd_card_t * card, if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->io_port = pci_resource_start(pci, 0); diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 2f19d7601da2..606b5cce5359 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1234,6 +1234,7 @@ static int snd_fm801_free(fm801_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -1263,14 +1264,17 @@ static int __devinit snd_fm801_create(snd_card_t * card, if ((err = pci_enable_device(pci)) < 0) return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; if ((err = pci_request_regions(pci, "FM801")) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->port = pci_resource_start(pci, 0); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 9063929b0d84..fddd9e0e5c43 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2495,6 +2495,7 @@ static int snd_ice1712_free(ice1712_t *ice) if (ice->port) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); + pci_disable_device(ice->pci); kfree(ice); return 0; } @@ -2527,12 +2528,15 @@ static int __devinit snd_ice1712_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x0fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) { snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } ice = kcalloc(1, sizeof(*ice), GFP_KERNEL); - if (ice == NULL) + if (ice == NULL) { + pci_disable_device(pci); return -ENOMEM; + } ice->omni = omni ? 1 : 0; if (cs8427_timeout < 1) cs8427_timeout = 1; @@ -2562,6 +2566,7 @@ static int __devinit snd_ice1712_create(snd_card_t * card, if ((err = pci_request_regions(pci, "ICE1712")) < 0) { kfree(ice); + pci_disable_device(pci); return err; } ice->port = pci_resource_start(pci, 0); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 5688b6d7a170..91c961dd4994 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2076,6 +2076,7 @@ static int snd_vt1724_free(ice1712_t *ice) } pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); + pci_disable_device(ice->pci); kfree(ice); return 0; } @@ -2105,8 +2106,10 @@ static int __devinit snd_vt1724_create(snd_card_t * card, return err; ice = kcalloc(1, sizeof(*ice), GFP_KERNEL); - if (ice == NULL) + if (ice == NULL) { + pci_disable_device(pci); return -ENOMEM; + } ice->vt1724 = 1; spin_lock_init(&ice->reg_lock); init_MUTEX(&ice->gpio_mutex); @@ -2124,6 +2127,7 @@ static int __devinit snd_vt1724_create(snd_card_t * card, if ((err = pci_request_regions(pci, "ICE1724")) < 0) { kfree(ice); + pci_disable_device(pci); return err; } ice->port = pci_resource_start(pci, 0); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 2ff1a28d3026..32b733520fba 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1020,7 +1020,9 @@ static void snd_intel8x0_setup_pcm_out(intel8x0_t *chip, */ if (cnt & ICH_PCM_246_MASK) { iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK); + spin_unlock_irq(&chip->reg_lock); msleep(50); /* grrr... */ + spin_lock_irq(&chip->reg_lock); } } else if (chip->device_type == DEVICE_INTEL_ICH4) { if (runtime->sample_bits > 16) @@ -2261,6 +2263,7 @@ static int snd_intel8x0_free(intel8x0_t *chip) if (chip->remap_bmaddr) iounmap(chip->remap_bmaddr); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -2279,6 +2282,7 @@ static int intel8x0_suspend(snd_card_t *card, unsigned int state) for (i = 0; i < 3; i++) if (chip->ac97[i]) snd_ac97_suspend(chip->ac97[i]); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -2496,8 +2500,10 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); chip->device_type = device_type; @@ -2517,6 +2523,7 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, if ((err = pci_request_regions(pci, card->shortname)) < 0) { kfree(chip); + pci_disable_device(pci); return err; } diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 96ee78dda0bc..e97b41452df9 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1074,6 +1074,7 @@ static int snd_intel8x0_free(intel8x0_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -1091,6 +1092,7 @@ static int intel8x0m_suspend(snd_card_t *card, unsigned int state) snd_pcm_suspend_all(chip->pcm[i]); if (chip->ac97) snd_ac97_suspend(chip->ac97); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -1172,8 +1174,10 @@ static int __devinit snd_intel8x0m_create(snd_card_t * card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->ac97_lock); chip->device_type = device_type; @@ -1183,6 +1187,7 @@ static int __devinit snd_intel8x0m_create(snd_card_t * card, if ((err = pci_request_regions(pci, card->shortname)) < 0) { kfree(chip); + pci_disable_device(pci); return err; } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index b3eb3b62aee5..ca256c4f18b9 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2179,6 +2179,7 @@ snd_korg1212_free(korg1212_t *korg1212) korg1212->dma_shared.area = NULL; } + pci_disable_device(korg1212->pci); kfree(korg1212); return 0; } @@ -2210,8 +2211,10 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, return err; korg1212 = kcalloc(1, sizeof(*korg1212), GFP_KERNEL); - if (korg1212 == NULL) + if (korg1212 == NULL) { + pci_disable_device(pci); return -ENOMEM; + } korg1212->card = card; korg1212->pci = pci; @@ -2244,6 +2247,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if ((err = pci_request_regions(pci, "korg1212")) < 0) { kfree(korg1212); + pci_disable_device(pci); return err; } @@ -2270,6 +2274,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if ((korg1212->iobase = ioremap(korg1212->iomem, iomem_size)) == NULL) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", korg1212->iomem, korg1212->iomem + iomem_size - 1); + snd_korg1212_free(korg1212); return -EBUSY; } @@ -2279,6 +2284,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if (err) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_korg1212_free(korg1212); return -EBUSY; } @@ -2326,6 +2332,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) { snd_printk(KERN_ERR "can not allocate shared buffer memory (%Zd bytes)\n", sizeof(KorgSharedBuffer)); + snd_korg1212_free(korg1212); return -ENOMEM; } korg1212->sharedBufferPtr = (KorgSharedBuffer *)korg1212->dma_shared.area; @@ -2342,6 +2349,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), korg1212->DataBufsSize, &korg1212->dma_play) < 0) { snd_printk(KERN_ERR "can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + snd_korg1212_free(korg1212); return -ENOMEM; } korg1212->playDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_play.area; @@ -2355,6 +2363,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), korg1212->DataBufsSize, &korg1212->dma_rec) < 0) { snd_printk(KERN_ERR "can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + snd_korg1212_free(korg1212); return -ENOMEM; } korg1212->recordDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_rec.area; @@ -2386,6 +2395,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { snd_printk(KERN_ERR "can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + snd_korg1212_free(korg1212); return -ENOMEM; } @@ -2405,8 +2415,10 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, mdelay(CARD_BOOT_DELAY_IN_MS); - if (snd_korg1212_downloadDSPCode(korg1212)) + if (snd_korg1212_downloadDSPCode(korg1212)) { + snd_korg1212_free(korg1212); return -EBUSY; + } printk(KERN_INFO "dspMemPhy = %08x U[%08x]\n" "PlayDataPhy = %08x L[%08x]\n" @@ -2421,8 +2433,10 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy), korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy)); - if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0) + if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0) { + snd_korg1212_free(korg1212); return err; + } korg1212->pcm->private_data = korg1212; korg1212->pcm->private_free = snd_korg1212_free_pcm; @@ -2439,8 +2453,10 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci, for (i = 0; i < ARRAY_SIZE(snd_korg1212_controls); i++) { err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212)); - if (err < 0) + if (err < 0) { + snd_korg1212_free(korg1212); return err; + } } snd_korg1212_proc_init(korg1212); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 4b3d2b4c0e2b..a68504b20e88 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2386,6 +2386,7 @@ static int snd_m3_free(m3_t *chip) if (chip->iobase) pci_release_regions(chip->pci); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -2422,6 +2423,8 @@ static int m3_suspend(snd_card_t *card, unsigned int state) /* power down apci registers */ snd_m3_outw(chip, 0xffff, 0x54); snd_m3_outw(chip, 0xffff, 0x56); + + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -2434,6 +2437,7 @@ static int m3_resume(snd_card_t *card, unsigned int state) if (chip->suspend_mem == NULL) return 0; + pci_enable_device(chip->pci); pci_set_master(chip->pci); /* first lets just bring everything back. .*/ @@ -2502,12 +2506,15 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, if (pci_set_dma_mask(pci, 0x0fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) { snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&chip->reg_lock); switch (pci->device) { @@ -2549,6 +2556,7 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL); if (chip->substreams == NULL) { kfree(chip); + pci_disable_device(pci); return -ENOMEM; } memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 65e96f82e2ec..32a728ffb1bc 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1092,6 +1092,7 @@ static int snd_mixart_free(mixart_mgr_t *mgr) mgr->bufferinfo.area = NULL; } + pci_disable_device(mgr->pci); kfree(mgr); return 0; } @@ -1292,14 +1293,17 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci, /* check if we can restrict PCI DMA transfers to 32 bits */ if (pci_set_dma_mask(pci, 0xffffffff) < 0) { snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } /* */ mgr = kcalloc(1, sizeof(*mgr), GFP_KERNEL); - if (! mgr) + if (! mgr) { + pci_disable_device(pci); return -ENOMEM; + } mgr->pci = pci; mgr->irq = -1; @@ -1307,6 +1311,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci, /* resource assignment */ if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { kfree(mgr); + pci_disable_device(pci); return err; } for (i = 0; i < 2; i++) { diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index a30e487f333b..25949c43497b 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1274,6 +1274,7 @@ static int nm256_suspend(snd_card_t *card, unsigned int state) snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); chip->coeffs_current = 0; + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -1319,6 +1320,7 @@ static int snd_nm256_free(nm256_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -1346,9 +1348,14 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci, *chip_ret = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } chip->card = card; chip->pci = pci; @@ -1535,9 +1542,6 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci, struct nm256_quirk *q; u16 subsystem_vendor, subsystem_device; - if ((err = pci_enable_device(pci)) < 0) - return err; - if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 00ca48d74598..2c131766a5a2 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1350,6 +1350,7 @@ static void snd_rme32_free(void *private_data) pci_release_regions(rme32->pci); rme32->port = 0; } + pci_disable_device(rme32->pci); } static void snd_rme32_free_spdif_pcm(snd_pcm_t * pcm) diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 5360cdef82da..8ab9ba8cb77e 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1553,6 +1553,7 @@ snd_rme96_free(void *private_data) pci_release_regions(rme96->pci); rme96->port = 0; } + pci_disable_device(rme96->pci); } static void diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index dba61f685ffd..1b76f18baad1 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5060,6 +5060,7 @@ static int snd_hdsp_free(hdsp_t *hdsp) if (hdsp->port) pci_release_regions(hdsp->pci); + pci_disable_device(hdsp->pci); return 0; } diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 10784f736b81..420779931869 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1812,6 +1812,7 @@ static int snd_rme9652_free(rme9652_t *rme9652) if (rme9652->port) pci_release_regions(rme9652->pci); + pci_disable_device(rme9652->pci); return 0; } diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 54e6c20d3f3b..8dcfc0800c49 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1185,6 +1185,7 @@ static int snd_sonicvibes_free(sonicvibes_t *sonic) kfree_nocheck(sonic->res_dmac); } pci_release_regions(sonic->pci); + pci_disable_device(sonic->pci); kfree(sonic); return 0; } @@ -1216,12 +1217,15 @@ static int __devinit snd_sonicvibes_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x00ffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) { snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } sonic = kcalloc(1, sizeof(*sonic), GFP_KERNEL); - if (sonic == NULL) + if (sonic == NULL) { + pci_disable_device(pci); return -ENOMEM; + } spin_lock_init(&sonic->reg_lock); sonic->card = card; sonic->pci = pci; @@ -1229,6 +1233,7 @@ static int __devinit snd_sonicvibes_create(snd_card_t * card, if ((err = pci_request_regions(pci, "S3 SonicVibes")) < 0) { kfree(sonic); + pci_disable_device(pci); return err; } diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 46d050d2bd6d..815e87883e86 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3537,12 +3537,15 @@ int __devinit snd_trident_create(snd_card_t * card, if (pci_set_dma_mask(pci, 0x3fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x3fffffff) < 0) { snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + pci_disable_device(pci); return -ENXIO; } trident = kcalloc(1, sizeof(*trident), GFP_KERNEL); - if (trident == NULL) + if (trident == NULL) { + pci_disable_device(pci); return -ENOMEM; + } trident->device = (pci->vendor << 16) | pci->device; trident->card = card; trident->pci = pci; @@ -3564,6 +3567,7 @@ int __devinit snd_trident_create(snd_card_t * card, if ((err = pci_request_regions(pci, "Trident Audio")) < 0) { kfree(trident); + pci_disable_device(pci); return err; } trident->port = pci_resource_start(pci, 0); @@ -3682,6 +3686,7 @@ int snd_trident_free(trident_t *trident) if (trident->irq >= 0) free_irq(trident->irq, (void *)trident); pci_release_regions(trident->pci); + pci_disable_device(trident->pci); kfree(trident); return 0; } @@ -3949,6 +3954,7 @@ static int snd_trident_suspend(snd_card_t *card, unsigned int state) case TRIDENT_DEVICE_ID_SI7018: break; } + pci_disable_device(trident->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 3767826431c2..ea853d19f4e0 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1980,6 +1980,7 @@ static int snd_via82xx_free(via82xx_t *chip) pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy); pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg); } + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -2006,8 +2007,10 @@ static int __devinit snd_via82xx_create(snd_card_t * card, if ((err = pci_enable_device(pci)) < 0) return err; - if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) + if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) { + pci_disable_device(pci); return -ENOMEM; + } chip->chip_type = chip_type; chip->revision = revision; @@ -2025,6 +2028,7 @@ static int __devinit snd_via82xx_create(snd_card_t * card, if ((err = pci_request_regions(pci, card->driver)) < 0) { kfree(chip); + pci_disable_device(pci); return err; } chip->port = pci_resource_start(pci, 0); diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index e4e64a88a52f..f465590c94df 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -116,6 +116,7 @@ static int snd_vx222_free(vx_core_t *chip) free_irq(chip->irq, (void*)chip); if (vx->port[0]) pci_release_regions(vx->pci); + pci_disable_device(vx->pci); kfree(chip); return 0; } @@ -147,8 +148,10 @@ static int __devinit snd_vx222_create(snd_card_t *card, struct pci_dev *pci, vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops; chip = snd_vx_create(card, hw, vx_ops, sizeof(struct snd_vx222) - sizeof(vx_core_t)); - if (! chip) + if (! chip) { + pci_disable_device(pci); return -ENOMEM; + } vx = (struct snd_vx222 *)chip; vx->pci = pci; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 1f43d9e48a56..87214a02b50a 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2098,6 +2098,7 @@ static int snd_ymfpci_free(ymfpci_t *chip) pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); + pci_disable_device(chip->pci); kfree(chip); return 0; } @@ -2153,6 +2154,7 @@ static int snd_ymfpci_suspend(snd_card_t *card, unsigned int state) chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_disable_dsp(chip); + pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } @@ -2204,8 +2206,10 @@ int __devinit snd_ymfpci_create(snd_card_t * card, return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) + if (chip == NULL) { + pci_disable_device(pci); return -ENOMEM; + } chip->old_legacy_ctrl = old_legacy_ctrl; spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->voice_lock); |
